kazu22002の技術覚書

PHPer, Golang, AWS エンジニアの日々

golangのfloat64の丸誤差について

プログラムの小数点の計算や割り算において、丸誤差を認識する機会がたまにあります。

今回はgolangのプログラムを書いていて発生したので、検証してみました。

プログラムと検証


func main() {
    fmt.Printf("%f\n", getFloat() * 1000 )
    fmt.Printf("%.10f\n", getFloat() * 1000 )

    fmt.Printf("%f\n", getFloat2() * 1000 )
    fmt.Printf("%.10f\n", getFloat2() * 1000 )

    fmt.Printf("%f\n", getFloat3() * 1000 )
    fmt.Printf("%.10f\n", getFloat3() * 1000 )
}

func getFloat() float64 {
    return 340.785
}

func getFloat2() float64 {
    return -1192.295 + 1766.96 -233.88
}

func getFloat3() float64 {
    t1 := -1192.295
    t2 := 1766.96
    t3 := -233.88

    return t1 + t2 + t3
}
340785.000000
340785.0000000000
340785.000000
340785.0000000000
340785.000000
340784.9999999999

まず変数に変換しなければ、期待した値になっています。

変数にいれて計算を行うことで、丸誤差が発生するようになっています。

またPrintfは、四捨五入をするためこのような結果になります。

printfの四捨五入

func main() {
    fmt.Printf("%f\n", 10.0000000000 )
    fmt.Printf("%.10f\n", 10.0000000000 )

    fmt.Printf("%f\n", 10.44444444444 )
    fmt.Printf("%.10f\n", 10.44444444444 )

    fmt.Printf("%f\n", 10.55555555555 )
    fmt.Printf("%.10f\n", 10.55555555555 )

    fmt.Printf("%f\n", 10.99999999999 )
    fmt.Printf("%.10f\n", 10.99999999999 )
}
10.000000
10.0000000000
10.444444
10.4444444444
10.555556
10.5555555556
11.000000
11.0000000000

パラメータを指定しない場合は、小数点6桁で四捨五入する仕様になっています。

改めて実行してみると、動作がよくわかると思います。

対策も考えて調べてみましたが、math/bigで試しても同じような結果になったので、割り切るしかないのかな。

詳しく対策がわかったらまた記事を書きたいです。

頑張ります。