[インデックス 14480] ファイルの概要
コミット
このコミットは、Go言語のテストスイートに、浮動小数点定数の丸めに関する新しいテストケースを追加するものです。具体的には、float32
からfloat64
への型変換における0.01
の丸め挙動が、gccgo
コンパイラで正しく扱われていなかった問題に対応するためのテストです。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/76fa4f430ad1768450dd052f0fefefba0cc022b7
元コミット内容
commit 76fa4f430ad1768450dd052f0fefefba0cc022b7
Author: Ian Lance Taylor <iant@golang.org>
Date: Mon Nov 26 08:31:15 2012 -0800
test: add test for floating point rounding of constants
Failed with gccgo.
R=golang-dev, remyoudompheng, rsc
CC=golang-dev
https://golang.org/cl/6855053
---
test/fixedbugs/bug470.go | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/test/fixedbugs/bug470.go b/test/fixedbugs/bug470.go
new file mode 100644
index 0000000000..0a359184c6
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
変更の背景
このコミットの背景には、Go言語のコンパイラの一つであるgccgo
における浮動小数点数の丸め処理の不正確さがありました。浮動小数点数は、コンピュータ内部で厳密な十進数として表現できない場合が多く、近似値として格納されます。この近似値の計算や型変換の際に、丸め誤差が発生します。
特に問題となったのは、0.01
という十進数をfloat32
(単精度浮動小数点数)に変換し、さらにそれをfloat64
(倍精度浮動小数点数)に変換する際の挙動です。Go言語の仕様では、定数の型変換において適切な丸めが行われることが期待されます。しかし、gccgo
ではこの丸め処理が期待通りに行われず、結果として異なる値が生成されてしまうバグが存在していました。
このバグは、数値計算の正確性が求められるアプリケーションにおいて、予期せぬ結果や誤動作を引き起こす可能性がありました。そのため、この問題を特定し、将来的に同様のバグが再発しないように、テストケースを追加する必要がありました。このテストは、gccgo
だけでなく、Go言語の他のコンパイラ(例: gc
)でも同様の挙動が保証されることを確認するためにも重要です。
前提知識の解説
浮動小数点数 (Floating-Point Numbers)
浮動小数点数は、実数をコンピュータで表現するための形式です。IEEE 754規格が広く用いられており、Go言語のfloat32
とfloat64
もこの規格に準拠しています。
float32
(単精度浮動小数点数): 32ビットで表現され、約7桁の十進精度を持ちます。表現できる数値の範囲は広いですが、精度は限定的です。float64
(倍精度浮動小数点数): 64ビットで表現され、約15〜17桁の十進精度を持ちます。float32
よりも高い精度と広い範囲を持ちます。
浮動小数点数の表現と丸め誤差
多くの十進数は、二進数では無限小数になります(例: 0.1は二進数では0.00011001100...と無限に続きます)。コンピュータは有限のビット数で数値を表現するため、無限小数を途中で打ち切る必要があり、この際に丸め誤差 (Rounding Error) が発生します。
例えば、0.01
という十進数をfloat32
で表現しようとすると、最も近いfloat32
の値に丸められます。この丸められたfloat32
の値をさらにfloat64
に変換すると、そのfloat32
の正確な値がfloat64
で表現されます。これは、元の0.01
を直接float64
で表現した場合とは異なる値になる可能性があります。
gccgo
gccgo
は、GCC (GNU Compiler Collection) のフロントエンドとして実装されたGo言語のコンパイラです。Go言語の公式コンパイラであるgc
とは異なる実装であり、Go言語の仕様に準拠しつつも、GCCの最適化やバックエンドを利用します。異なるコンパイラ実装が存在することで、言語仕様の解釈や数値計算の挙動に微妙な差異が生じることがあり、今回の浮動小数点数の丸め問題もその一例でした。
技術的詳細
このコミットが対処している技術的な問題は、Go言語における浮動小数点定数の型変換時の丸め挙動の一貫性です。特に、0.01
という十進数のリテラルがfloat32
に変換され、その後float64
に変換される際の正確な値が、Go言語の仕様に沿っているかどうかが焦点です。
Go言語の定数は、型付けされていない場合、その値が表現できる最も広い精度で扱われます。しかし、明示的に型変換が行われる場合、その型の精度に合わせて値が丸められます。
今回のケースでは、以下の2つの値が比較されています。
float64(float32(0.01))
:- まず、十進数の
0.01
がfloat32
に変換されます。この際、0.01
に最も近いfloat32
の値に丸められます。この値は0.00999999977648258209228515625
です。 - 次に、この
float32
の値がfloat64
に変換されます。float32
で表現された値は、float64
の精度であれば正確に表現できるため、値はそのまま0.00999999977648258209228515625
となります。
- まず、十進数の
0.01
を直接float64
で表現した場合:- 十進数の
0.01
を直接float64
で表現すると、0.01000000000000000020816681711721685132943093776702880859375
という値になります。
- 十進数の
この2つの値は、見た目は似ていますが、浮動小数点数の内部表現においては異なる値です。Go言語の仕様では、定数の型変換は予測可能な方法で丸めを行うべきであり、float64(float32(0.01))
の結果は、0.01
をfloat32
に丸めた結果をfloat64
で表現した値と一致する必要があります。
gccgo
がこのテストで失敗したということは、gccgo
のコンパイラがfloat64(float32(0.01))
の計算において、期待される0.00999999977648258209228515625
ではなく、0.01000000000000000020816681711721685132943093776702880859375
に近い、あるいはそれと等しい値を生成してしまっていたことを示唆しています。これは、gccgo
が中間的なfloat32
への丸めを正しく行っていなかったか、あるいは最適化の過程で丸め挙動が変化してしまっていた可能性を意味します。
このテストの追加により、Go言語の異なるコンパイラ実装間での浮動小数点定数の丸め挙動の一貫性が保証され、数値計算の信頼性が向上します。
コアとなるコードの変更箇所
このコミットでは、test/fixedbugs/bug470.go
という新しいテストファイルが追加されています。
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
コアとなるコードの解説
test/fixedbugs/bug470.go
ファイルは、浮動小数点数の丸め挙動を検証するためのシンプルなGoプログラムです。
-
パッケージとインポート:
package main
: 実行可能なプログラムであることを示します。import "fmt"
: フォーマットされたI/O(入出力)を行うためのパッケージをインポートしています。fmt.Sprintf
関数がエラーメッセージの整形に使用されます。
-
定数の定義:
const ( F32 = 0.00999999977648258209228515625 F64 = 0.01000000000000000020816681711721685132943093776702880859375 )
F32
: これは、十進数の0.01
をfloat32
に丸めた後、そのfloat32
の値をfloat64
で正確に表現したものです。この値は、0.01
に最も近いfloat32
のバイナリ表現を十進数で表したものです。F64
: これは、十進数の0.01
を直接float64
で表現したものです。0.01
は二進数で正確に表現できないため、float64
の精度で最も近い値に丸められています。
-
変数の定義:
var F = float64(float32(0.01))
F
: この変数は、テストの対象となる計算結果を保持します。0.01
というリテラルをまずfloat32
に明示的に型変換し、その結果をさらにfloat64
に型変換しています。Go言語の仕様では、このF
の値がF32
と厳密に一致することが期待されます。
-
main
関数:func main() { // 0.01 rounded to float32 then to float64 is F32. // 0.01 represented directly in float64 is F64. if F != F32 { panic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32)) } }
- この
main
関数がテストの本体です。 if F != F32
:F
の値がF32
と等しいかどうかを比較しています。もし等しくなければ、期待される丸め挙動が実現されていないことを意味します。panic(fmt.Sprintf(...))
:F
とF32
が一致しない場合、プログラムはパニック(異常終了)します。エラーメッセージには、実際に得られたF
の値と、期待されるF32
の値が、非常に高い精度(小数点以下1000桁)で表示されます。これにより、丸め誤差の具体的な数値を確認できます。
- この
このテストは、Go言語のコンパイラが浮動小数点定数の型変換において、IEEE 754規格とGo言語の仕様に準拠した正確な丸め処理を行っていることを検証するために不可欠です。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語のIssue Tracker: https://github.com/golang/go/issues
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/
参考にした情報源リンク
- IEEE 754 浮動小数点数標準: https://en.wikipedia.org/wiki/IEEE_754
- Go言語の数値型に関するドキュメント (Go言語のバージョンやドキュメントの更新によりURLが変更される可能性があります):
- A Tour of Go - Basic Types: https://go.dev/tour/basics/11
- Go Language Specification - Numeric types: https://go.dev/ref/spec#Numeric_types
- GCCGoに関する情報: https://gcc.gnu.org/onlinedocs/gccgo/
- 浮動小数点数の精度問題に関する一般的な解説記事 (例: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"): https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (これは一般的な情報源であり、特定のGo言語の文脈に限定されません)
[インデックス 14480] ファイルの概要
コミット
このコミットは、Go言語のテストスイートに、浮動小数点定数の丸めに関する新しいテストケースを追加するものです。具体的には、float32
からfloat64
への型変換における0.01
の丸め挙動が、gccgo
コンパイラで正しく扱われていなかった問題に対応するためのテストです。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/76fa4f430ad1768450dd052f0fefefba0cc022b7
元コミット内容
commit 76fa4f430ad1768450dd052f0fefefba0cc022b7
Author: Ian Lance Taylor <iant@golang.org>
Date: Mon Nov 26 08:31:15 2012 -0800
test: add test for floating point rounding of constants
Failed with gccgo.
R=golang-dev, remyoudompheng, rsc
CC=golang-dev
https://golang.org/cl/6855053
---
test/fixedbugs/bug470.go | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/test/fixedbugs/bug470.go b/test/fixedbugs/bug470.go
new file mode 100644
index 0000000000..0a359184c6
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
変更の背景
このコミットの背景には、Go言語のコンパイラの一つであるgccgo
における浮動小数点数の丸め処理の不正確さがありました。浮動小数点数は、コンピュータ内部で厳密な十進数として表現できない場合が多く、近似値として格納されます。この近似値の計算や型変換の際に、丸め誤差が発生します。
特に問題となったのは、0.01
という十進数をfloat32
(単精度浮動小数点数)に変換し、さらにそれをfloat64
(倍精度浮動小数点数)に変換する際の挙動です。Go言語の仕様では、定数の型変換において適切な丸めが行われることが期待されます。しかし、gccgo
ではこの丸め処理が期待通りに行われず、結果として異なる値が生成されてしまうバグが存在していました。
このバグは、数値計算の正確性が求められるアプリケーションにおいて、予期せぬ結果や誤動作を引き起こす可能性がありました。そのため、この問題を特定し、将来的に同様のバグが再発しないように、テストケースを追加する必要がありました。このテストは、gccgo
だけでなく、Go言語の他のコンパイラ(例: gc
)でも同様の挙動が保証されることを確認するためにも重要です。
前提知識の解説
浮動小数点数 (Floating-Point Numbers)
浮動小数点数は、実数をコンピュータで表現するための形式です。IEEE 754規格が広く用いられており、Go言語のfloat32
とfloat64
もこの規格に準拠しています。
float32
(単精度浮動小数点数): 32ビットで表現され、約7桁の十進精度を持ちます。表現できる数値の範囲は広いですが、精度は限定的です。float64
(倍精度浮動小数点数): 64ビットで表現され、約15〜17桁の十進精度を持ちます。float32
よりも高い精度と広い範囲を持ちます。
浮動小数点数の表現と丸め誤差
多くの十進数は、二進数では無限小数になります(例: 0.1は二進数では0.00011001100...と無限に続きます)。コンピュータは有限のビット数で数値を表現するため、無限小数を途中で打ち切る必要があり、この際に丸め誤差 (Rounding Error) が発生します。
例えば、0.01
という十進数をfloat32
で表現しようとすると、最も近いfloat32
の値に丸められます。この丸められたfloat32
の値をさらにfloat64
に変換すると、そのfloat32
の正確な値がfloat64
で表現されます。これは、元の0.01
を直接float64
で表現した場合とは異なる値になる可能性があります。
gccgo
gccgo
は、GCC (GNU Compiler Collection) のフロントエンドとして実装されたGo言語のコンパイラです。Go言語の公式コンパイラであるgc
とは異なる実装であり、Go言語の仕様に準拠しつつも、GCCの最適化やバックエンドを利用します。異なるコンパイラ実装が存在することで、言語仕様の解釈や数値計算の挙動に微妙な差異が生じることがあり、今回の浮動小数点数の丸め問題もその一例でした。
技術的詳細
このコミットが対処している技術的な問題は、Go言語における浮動小数点定数の型変換時の丸め挙動の一貫性です。特に、0.01
という十進数のリテラルがfloat32
に変換され、その後float64
に変換される際の正確な値が、Go言語の仕様に沿っているかどうかが焦点です。
Go言語の定数は、型付けされていない場合、その値が表現できる最も広い精度で扱われます。しかし、明示的に型変換が行われる場合、その型の精度に合わせて値が丸められます。
今回のケースでは、以下の2つの値が比較されています。
float64(float32(0.01))
:- まず、十進数の
0.01
がfloat32
に変換されます。この際、0.01
に最も近いfloat32
の値に丸められます。この値は0.00999999977648258209228515625
です。 - 次に、この
float32
の値がfloat64
に変換されます。float32
で表現された値は、float64
の精度であれば正確に表現できるため、値はそのまま0.00999999977648258209228515625
となります。
- まず、十進数の
0.01
を直接float64
で表現した場合:- 十進数の
0.01
を直接float64
で表現すると、0.01000000000000000020816681711721685132943093776702880859375
という値になります。
- 十進数の
この2つの値は、見た目は似ていますが、浮動小数点数の内部表現においては異なる値です。Go言語の仕様では、定数の型変換は予測可能な方法で丸めを行うべきであり、float64(float32(0.01))
の結果は、0.01
をfloat32
に丸めた結果をfloat64
で表現した値と一致する必要があります。
gccgo
がこのテストで失敗したということは、gccgo
のコンパイラがfloat64(float32(0.01))
の計算において、期待される0.00999999977648258209228515625
ではなく、0.01000000000000000020816681711721685132943093776702880859375
に近い、あるいはそれと等しい値を生成してしまっていたことを示唆しています。これは、gccgo
が中間的なfloat32
への丸めを正しく行っていなかったか、あるいは最適化の過程で丸め挙動が変化してしまっていた可能性を意味します。
このテストの追加により、Go言語の異なるコンパイラ実装間での浮動小数点定数の丸め挙動の一貫性が保証され、数値計算の信頼性が向上します。
コアとなるコードの変更箇所
このコミットでは、test/fixedbugs/bug470.go
という新しいテストファイルが追加されています。
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
コアとなるコードの解説
test/fixedbugs/bug470.go
ファイルは、浮動小数点数の丸め挙動を検証するためのシンプルなGoプログラムです。
-
パッケージとインポート:
package main
: 実行可能なプログラムであることを示します。import "fmt"
: フォーマットされたI/O(入出力)を行うためのパッケージをインポートしています。fmt.Sprintf
関数がエラーメッセージの整形に使用されます。
-
定数の定義:
const ( F32 = 0.00999999977648258209228515625 F64 = 0.01000000000000000020816681711721685132943093776702880859375 )
F32
: これは、十進数の0.01
をfloat32
に丸めた後、そのfloat32
の値をfloat64
で正確に表現したものです。この値は、0.01
に最も近いfloat32
のバイナリ表現を十進数で表したものです。F64
: これは、十進数の0.01
を直接float64
で表現したものです。0.01
は二進数で正確に表現できないため、float64
の精度で最も近い値に丸められています。
-
変数の定義:
var F = float64(float32(0.01))
F
: この変数は、テストの対象となる計算結果を保持します。0.01
というリテラルをまずfloat32
に明示的に型変換し、その結果をさらにfloat64
に型変換しています。Go言語の仕様では、このF
の値がF32
と厳密に一致することが期待されます。
-
main
関数:func main() { // 0.01 rounded to float32 then to float64 is F32. // 0.01 represented directly in float64 is F64. if F != F32 { panic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32)) } }
- この
main
関数がテストの本体です。 if F != F32
:F
の値がF32
と等しいかどうかを比較しています。もし等しくなければ、期待される丸め挙動が実現されていないことを意味します。panic(fmt.Sprintf(...))
:F
とF32
が一致しない場合、プログラムはパニック(異常終了)します。エラーメッセージには、実際に得られたF
の値と、期待されるF32
の値が、非常に高い精度(小数点以下1000桁)で表示されます。これにより、丸め誤差の具体的な数値を確認できます。
- この
このテストは、Go言語のコンパイラが浮動小数点定数の型変換において、IEEE 754規格とGo言語の仕様に準拠した正確な丸め処理を行っていることを検証するために不可欠です。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語のIssue Tracker: https://github.com/golang/go/issues
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/
参考にした情報源リンク
- IEEE 754 浮動小数点数標準: https://en.wikipedia.org/wiki/IEEE_754
- Go言語の数値型に関するドキュメント (Go言語のバージョンやドキュメントの更新によりURLが変更される可能性があります):
- A Tour of Go - Basic Types: https://go.dev/tour/basics/11
- Go Language Specification - Numeric types: https://go.dev/ref/spec#Numeric_types
- GCCGoに関する情報: https://gcc.gnu.org/onlinedocs/gccgo/
- 浮動小数点数の精度問題に関する一般的な解説記事 (例: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"): https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (これは一般的な情報源であり、特定のGo言語の文脈に限定されません)
[インデックス 14480] ファイルの概要
コミット
このコミットは、Go言語のテストスイートに、浮動小数点定数の丸めに関する新しいテストケースを追加するものです。具体的には、float32
からfloat64
への型変換における0.01
の丸め挙動が、gccgo
コンパイラで正しく扱われていなかった問題に対応するためのテストです。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/76fa4f430ad1768450dd052f0fefefba0cc022b7
元コミット内容
commit 76fa4f430ad1768450dd052f0fefefba0cc022b7
Author: Ian Lance Taylor <iant@golang.org>
Date: Mon Nov 26 08:31:15 2012 -0800
test: add test for floating point rounding of constants
Failed with gccgo.
R=golang-dev, remyoudompheng, rsc
CC=golang-dev
https://golang.org/cl/6855053
---
test/fixedbugs/bug470.go | 26 ++++++++++++++++++++++++++\n 1 file changed, 26 insertions(+)\n\ndiff --git a/test/fixedbugs/bug470.go b/test/fixedbugs/bug470.go
new file mode 100644
index 0000000000..0a359184c6
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
変更の背景
このコミットの背景には、Go言語のコンパイラの一つであるgccgo
における浮動小数点数の丸め処理の不正確さがありました。浮動小数点数は、コンピュータ内部で厳密な十進数として表現できない場合が多く、近似値として格納されます。この近似値の計算や型変換の際に、丸め誤差が発生します。
特に問題となったのは、0.01
という十進数をfloat32
(単精度浮動小数点数)に変換し、さらにそれをfloat64
(倍精度浮動小数点数)に変換する際の挙動です。Go言語の仕様では、定数の型変換において適切な丸めが行われることが期待されます。しかし、gccgo
ではこの丸め処理が期待通りに行われず、結果として異なる値が生成されてしまうバグが存在していました。
このバグは、数値計算の正確性が求められるアプリケーションにおいて、予期せぬ結果や誤動作を引き起こす可能性がありました。そのため、この問題を特定し、将来的に同様のバグが再発しないように、テストケースを追加する必要がありました。このテストは、gccgo
だけでなく、Go言語の他のコンパイラ(例: gc
)でも同様の挙動が保証されることを確認するためにも重要です。
前提知識の解説
浮動小数点数 (Floating-Point Numbers)
浮動小数点数は、実数をコンピュータで表現するための形式です。IEEE 754規格が広く用いられており、Go言語のfloat32
とfloat64
もこの規格に準拠しています。
float32
(単精度浮動小数点数): 32ビットで表現され、約7桁の十進精度を持ちます。表現できる数値の範囲は広いですが、精度は限定的です。float64
(倍精度浮動小数点数): 64ビットで表現され、約15〜17桁の十進精度を持ちます。float32
よりも高い精度と広い範囲を持ちます。
浮動小数点数の表現と丸め誤差
多くの十進数は、二進数では無限小数になります(例: 0.1は二進数では0.00011001100...と無限に続きます)。コンピュータは有限のビット数で数値を表現するため、無限小数を途中で打ち切る必要があり、この際に丸め誤差 (Rounding Error) が発生します。
例えば、0.01
という十進数をfloat32
で表現しようとすると、最も近いfloat32
の値に丸められます。この丸められたfloat32
の値をさらにfloat64
に変換すると、そのfloat32
の正確な値がfloat64
で表現されます。これは、元の0.01
を直接float64
で表現した場合とは異なる値になる可能性があります。
gccgo
gccgo
は、GCC (GNU Compiler Collection) のフロントエンドとして実装されたGo言語のコンパイラです。Go言語の公式コンパイラであるgc
とは異なる実装であり、Go言語の仕様に準拠しつつも、GCCの最適化やバックエンドを利用します。異なるコンパイラ実装が存在することで、言語仕様の解釈や数値計算の挙動に微妙な差異が生じることがあり、今回の浮動小数点数の丸め問題もその一例でした。
技術的詳細
このコミットが対処している技術的な問題は、Go言語における浮動小数点定数の型変換時の丸め挙動の一貫性です。特に、0.01
という十進数のリテラルがfloat32
に変換され、その後float64
に変換される際の正確な値が、Go言語の仕様に沿っているかどうかが焦点です。
Go言語の定数は、型付けされていない場合、その値が表現できる最も広い精度で扱われます。しかし、明示的に型変換が行われる場合、その型の精度に合わせて値が丸められます。
今回のケースでは、以下の2つの値が比較されています。
float64(float32(0.01))
:- まず、十進数の
0.01
がfloat32
に変換されます。この際、0.01
に最も近いfloat32
の値に丸められます。この値は0.00999999977648258209228515625
です。 - 次に、この
float32
の値がfloat64
に変換されます。float32
で表現された値は、float64
の精度であれば正確に表現できるため、値はそのまま0.00999999977648258209228515625
となります。
- まず、十進数の
0.01
を直接float64
で表現した場合:- 十進数の
0.01
を直接float64
で表現すると、0.01000000000000000020816681711721685132943093776702880859375
という値になります。
- 十進数の
この2つの値は、見た目は似ていますが、浮動小数点数の内部表現においては異なる値です。Go言語の仕様では、定数の型変換は予測可能な方法で丸めを行うべきであり、float64(float32(0.01))
の結果は、0.01
をfloat32
に丸めた結果をfloat64
で表現した値と一致する必要があります。
gccgo
がこのテストで失敗したということは、gccgo
のコンパイラがfloat64(float32(0.01))
の計算において、期待される0.00999999977648258209228515625
ではなく、0.01000000000000000020816681711721685132943093776702880859375
に近い、あるいはそれと等しい値を生成してしまっていたことを示唆しています。これは、gccgo
が中間的なfloat32
への丸めを正しく行っていなかったか、あるいは最適化の過程で丸め挙動が変化してしまっていた可能性を意味します。
このテストの追加により、Go言語の異なるコンパイラ実装間での浮動小数点定数の丸め挙動の一貫性が保証され、数値計算の信頼性が向上します。
コアとなるコードの変更箇所
このコミットでは、test/fixedbugs/bug470.go
という新しいテストファイルが追加されています。
--- /dev/null
+++ b/test/fixedbugs/bug470.go
@@ -0,0 +1,26 @@
+// run
+\n+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+\n+// Converting constants between types must introduce rounding.
+\n+package main
+\n+import "fmt"\n+\n+const (\n+ F32 = 0.00999999977648258209228515625
+ F64 = 0.01000000000000000020816681711721685132943093776702880859375
+)\n+\n+var F = float64(float32(0.01))\n+\n+func main() {\n+\t// 0.01 rounded to float32 then to float64 is F32.
+\t// 0.01 represented directly in float64 is F64.
+\tif F != F32 {\n+\t\tpanic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32))\n+\t}\n+}\n
コアとなるコードの解説
test/fixedbugs/bug470.go
ファイルは、浮動小数点数の丸め挙動を検証するためのシンプルなGoプログラムです。
-
パッケージとインポート:
package main
: 実行可能なプログラムであることを示します。import "fmt"
: フォーマットされたI/O(入出力)を行うためのパッケージをインポートしています。fmt.Sprintf
関数がエラーメッセージの整形に使用されます。
-
定数の定義:
const ( F32 = 0.00999999977648258209228515625 F64 = 0.01000000000000000020816681711721685132943093776702880859375 )
F32
: これは、十進数の0.01
をfloat32
に丸めた後、そのfloat32
の値をfloat64
で正確に表現したものです。この値は、0.01
に最も近いfloat32
のバイナリ表現を十進数で表したものです。F64
: これは、十進数の0.01
を直接float64
で表現したものです。0.01
は二進数で正確に表現できないため、float64
の精度で最も近い値に丸められています。
-
変数の定義:
var F = float64(float32(0.01))
F
: この変数は、テストの対象となる計算結果を保持します。0.01
というリテラルをまずfloat32
に明示的に型変換し、その結果をさらにfloat64
に型変換しています。Go言語の仕様では、このF
の値がF32
と厳密に一致することが期待されます。
-
main
関数:func main() { // 0.01 rounded to float32 then to float64 is F32. // 0.01 represented directly in float64 is F64. if F != F32 { panic(fmt.Sprintf("F=%.1000g, want %.1000g", F, F32)) } }
- この
main
関数がテストの本体です。 if F != F32
:F
の値がF32
と等しいかどうかを比較しています。もし等しくなければ、期待される丸め挙動が実現されていないことを意味します。panic(fmt.Sprintf(...))
:F
とF32
が一致しない場合、プログラムはパニック(異常終了)します。エラーメッセージには、実際に得られたF
の値と、期待されるF32
の値が、非常に高い精度(小数点以下1000桁)で表示されます。これにより、丸め誤差の具体的な数値を確認できます。
- この
このテストは、Go言語のコンパイラが浮動小数点定数の型変換において、IEEE 754規格とGo言語の仕様に準拠した正確な丸め処理を行っていることを検証するために不可欠です。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/
- Go言語のIssue Tracker: https://github.com/golang/go/issues
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/
参考にした情報源リンク
- IEEE 754 浮動小数点数標準: https://en.wikipedia.org/wiki/IEEE_754
- Go言語の数値型に関するドキュメント (Go言語のバージョンやドキュメントの更新によりURLが変更される可能性があります):
- A Tour of Go - Basic Types: https://go.dev/tour/basics/11
- Go Language Specification - Numeric types: https://go.dev/ref/spec#Numeric_types
- GCCGoに関する情報: https://gcc.gnu.org/onlinedocs/gccgo/
- 浮動小数点数の精度問題に関する一般的な解説記事 (例: "What Every Computer Scientist Should Know About Floating-Point Arithmetic"): https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (これは一般的な情報源であり、特定のGo言語の文脈に限定されません)