go のサンプル コードは次のとおりです。
package main
import "fmt"
func mult32(a, b float32) float32 { return a*b }
func mult64(a, b float64) float64 { return a*b }
func main() {
fmt.Println(3*4.3) // A1, 12.9
fmt.Println(mult32(3, 4.3)) // B1, 12.900001
fmt.Println(mult64(3, 4.3)) // C1, 12.899999999999999
fmt.Println(12.9 - 3*4.3) // A2, 1.8033161362862765e-130
fmt.Println(12.9 - mult32(3, 4.3)) // B2, -9.536743e-07
fmt.Println(12.9 - mult64(3, 4.3)) // C2, 1.7763568394002505e-15
fmt.Println(12.9 - 3*4.3) // A4, 1.8033161362862765e-130
fmt.Println(float32(12.9) - float32(3)*float32(4.3)) // B4, -9.536743e-07
fmt.Println(float64(12.9) - float64(3)*float64(4.3)) // C4, 1.7763568394002505e-15
}
ライン A1、B1、および C1 の結果の違いは理解できます。しかし、A2からC2の魔法が始まります。B2 と C2 のどちらからの結果も、A2 行の結果と一致しません。同じことが行 x2 (x = A、B、または C) にも当てはまりますが、x2 と x4 の出力は同じです。
念のため、結果をバイナリ形式で出力してみましょう。
fmt.Printf("%b\n", 3*4.3) // A11, 7262054399134925p-49
fmt.Printf("%b\n", mult32(3, 4.3)) // B11, 13526631p-20
fmt.Printf("%b\n", mult64(3, 4.3)) // C11, 7262054399134924p-49
fmt.Printf("%b\n", 12.9 - 3*4.3) // A12, 4503599627370496p-483
fmt.Printf("%b\n", 12.9 - mult32(3, 4.3)) // B12, -8388608p-43
fmt.Printf("%b\n", 12.9 - mult64(3, 4.3)) // C12, 4503599627370496p-101
fmt.Printf("%b\n", 12.9 - 3*4.3) // A14, 4503599627370496p-483
fmt.Printf("%b\n", float32(12.9) - float32(3)*float32(4.3)) // B14, -8388608p-43
fmt.Printf("%b\n", float64(12.9) - float64(3)*float64(4.3)) // C14, 4503599627370496p-101
上記のコードからのいくつかの事実 (bin 形式の 1 つ):
- 行 A11 と C11 (最後の桁 - 指数の直前) には違いがあります。
- 行 A12 と C12 はほとんど同じです (指数を除いて!!!)、同じことが行 A14 と C14 の間で観察できます。
そして、ここで質問が来ます:
- 裸の (裸の :)) 数値の計算はどのように実行されますか? (すべての Axx 行での計算)
- それらはコンパイラなどによって実行されますか?
- はいの場合、なぜそれらは異なるのですか? 最適化?
- それらは、IEE-754 とは異なるシステムで計算されますか?
- はいの場合、なぜそうですか?
- より正確な精度を達成することは、そのようなアプローチを正当化しますか?
コードは、「go run」と「go build」(go1.0.3) の両方で 64 ビット Linux でテストされており、次のサイトでもテストされています: http://tour.golang.org/