반응형
문제
Go나 타 언어에서 실수(float)를 비교하면 예상했던 것과 다른 결과를 나타내는 경우를 볼 수 있습니다.
아래 코드로 예를 들 수 있습니다.
package main
import "fmt"
func main() {
// Go에서 지역 변수는 간단한 네이밍 권함
var a float64 = 0.1
var b float64 = 0.2
var c float64 = 0.3
if a+b == c {
fmt.Println("이 조건문은 실행되지 않습니다.")
}
// 출력해보면 조건식이 맞을 것 같지만
fmt.Printf("%f + %f = %f (%v)\n", a, b, a+b, a+b == c)
// 실제로는 0.1, 0.2, 03는 정확한 0.1, 0.2, 0.3이 아니다. (Go에서 자체 반올림해서 보여주기 때문에 더 햇갈릴 수 있다.)
fmt.Printf("%0.18f + %0.18f = %0.18f (%v)\n", a, b, c, a+b == c)
}
이 코드를 실행시키면 아래와 같습니다.
0.100000 + 0.200000 = 0.300000 (false)
0.100000000000000006 + 0.200000000000000011 = 0.299999999999999989 (false)
이렇게 되는 이유는 소수점 이하의 수들은 2의 마이너스 승으로 표하게 됩니다.
그 이유는 컴퓨터는 2진수를 사용하기 때문이죠.
그렇다 보니 2의 마이너스 승으로는 정확한 0.1을 만들 수 없게 된 것입니다.
그래서 대부분의 언어에서는 근사치를 사용해 실수를 표현하곤 합니다.
해결법
그렇다고 0.1 + 0.2 == 0.3과 같은 간다한 비교 전혀 불가능하진 않습니다.
언어마다 해결법이 다른데 Go에서는 아래 두 가지 방법이 있습니다.
package main
import (
"fmt"
"math"
"math/big"
)
func main() {
// 1. 작은 오차 무시하기
// go에서 실수를 표현할 때 2가지 수가 발생합니다.
// 이때 두 수의 차이는 마지막 비트 하나 밖에 차이가 발생하지 않으므로 이걸 무시하면 됩니다.
// Go에서는 편리하게 math 패키지의 Nextafter 함수가 이를 해결해줍니다.
var a float64 = 0.1
var b float64 = 0.2
var c float64 = 0.3
// math.Nextafter는 첫 번째 인수의 값이 두 번째 인수보다 작으면 1비트 증가 크면 1비트 감소시키고 값을 반환해줍니다.
// 이걸로 비교 가능합니다.
fmt.Printf("%0.18f + %0.18f = %0.18f (%v)\n", a, b, c, math.Nextafter(a+b, c) == c)
// 2. math/big을 사용해서 비교합니다.
d, _ := new(big.Float).SetString("0.1")
e, _ := new(big.Float).SetString("0.2")
f, _ := new(big.Float).SetString("0.3")
g := new(big.Float).Add(d, e)
// Cmp는 f가 작으면 -1을 크면 1를 같으면 0을 반환합니다.
// math/big은 format을 사용시 에러 발생하므로 주의가 필요합니다.
fmt.Println(d, " + ", e, " = ", f, " (", f.Cmp(g) == 0, ")")
}
결과는 아래와 같습니다.
0.100000000000000006 + 0.200000000000000011 = 0.299999999999999989 (true)
0.1 + 0.2 = 0.3 ( true )
컴퓨터의 실수 표현에 경우 훌륭하게 정리되어있는 블로그가 많으니 같이 참고하시면 좋을 것 같습니다.
반응형
'Go Lang > Study' 카테고리의 다른 글
[GoLang] 단축 URL 웹사이트 만들기 (0) | 2023.11.03 |
---|---|
[GoLang] 구조체 선언 시 메모리 최적화 하기 (1) | 2023.10.29 |
[GoLang] Go언어란? (1) | 2023.10.24 |
[GoLang] .env 파일에 DB 접속 정보를 저장하자 (0) | 2023.10.04 |
[GoLang] xls 파일을 xlsx 파일로 바꿔주자 (0) | 2023.09.17 |