[インデックス 11133] ファイルの概要
このコミットでは、src/pkg/strconv/ftoa.go
と src/pkg/strconv/ftoa_test.go
の2つのファイルが変更されています。
コミット
commit 6f77cd291419dcd70faf611ecc82dc50cc294552
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 12 11:32:28 2012 -0800
strconv: fix round up corner case
Comment described the correct condition
but the code did not implement it.
Fixes #2625.
R=remyoudompheng
CC=golang-dev
https://golang.org/cl/5530082
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6f77cd291419dcd70faf611ecc82dc50cc294552
元コミット内容
strconv: fix round up corner case
Comment described the correct condition
but the code did not implement it.
Fixes #2625.
R=remyoudompheng
CC=golang-dev
https://golang.org/cl/5530082
変更の背景
このコミットは、Go言語の標準ライブラリである strconv
パッケージ内の浮動小数点数から文字列への変換(ftoa
、float to ASCII)における丸め処理のコーナーケースのバグを修正するものです。具体的には、ftoa.go
ファイル内の roundShortest
関数において、コメントで記述されていた正しい丸め上げの条件が、実際のコードでは正しく実装されていなかったことが問題でした。
コミットメッセージにある「Fixes #2625」は、当時のGo言語のIssueトラッカー(現在はGitHubに移行済み)におけるバグ報告の参照です。このバグにより、特定の浮動小数点数が期待通りに丸められず、誤った文字列表現が生成される可能性がありました。この修正は、数値の正確な文字列表現を保証するために不可欠でした。
前提知識の解説
strconv
パッケージ
strconv
パッケージは、Go言語において基本的なデータ型(真偽値、整数、浮動小数点数)と文字列との間の変換機能を提供します。例えば、Atoi
は文字列を整数に、Itoa
は整数を文字列に変換します。浮動小数点数に関しては、ParseFloat
が文字列を浮動小数点数に、FormatFloat
が浮動小数点数を文字列に変換します。このコミットが関連するのは、FormatFloat
の内部で利用される ftoa
(float to ASCII) 処理です。
浮動小数点数表現 (IEEE 754)
コンピュータにおける浮動小数点数は、通常IEEE 754標準に従って表現されます。これは、数値を符号部、指数部、仮数部に分けて表現する方法です。この表現には、有限のビット数で無限の数を表現するため、常に精度に関する課題が伴います。特に、10進数で正確に表現できる数値でも、2進数では循環小数になる場合があり、その結果、丸め誤差が生じます。
丸め処理と「最短表現」
浮動小数点数を文字列に変換する際、多くの場合、元の浮動小数点数の値を正確に表現しつつ、かつ最も短い文字列形式(最短表現)を生成することが求められます。これは、例えば 0.1
という浮動小数点数が内部的には 0.1000000000000000055511151231257827021181583404541015625
のように表現される場合でも、ユーザーには 0.1
と表示したいという要求に応えるためです。
最短表現を実現するためには、元の浮動小数点数と、その隣接する浮動小数点数の中間点との関係を考慮して丸め処理を行う必要があります。ある浮動小数点数を文字列に変換する際、その文字列を再度浮動小数点数に変換したときに、元の値と完全に一致するかどうかを検証する「ラウンドトリップ」の概念も重要です。
roundShortest
関数
strconv
パッケージの ftoa.go
にある roundShortest
関数は、この最短表現を実現するための丸め処理を担当します。この関数は、与えられた浮動小数点数を10進数の仮数部と指数部に分解し、それを文字列に変換する際に、どの桁で丸めるべきかを決定します。この決定は、元の浮動小数点数と、その上下の隣接する浮動小数点数との相対的な距離に基づいて行われます。
技術的詳細
このコミットの核心は、roundShortest
関数内の okup
変数の計算ロジックの変更です。okup
は、現在の桁で丸め上げを行うことが許容されるかどうかを判断するためのブール値です。
元のコードでは、okup
は以下のように定義されていました。
okup := m != u && (inclusive || i+1 < upper.nd)
ここで、
m
は現在の10進数の仮数部(mantissa)u
は元の浮動小数点数よりわずかに大きい値の10進数の仮数部(upper bound)inclusive
は、丸め上げが許容される条件の一つi+1 < upper.nd
は、丸め上げ後の桁数がupper
の桁数よりも少ない場合に丸め上げが許容される条件
この条件は、「m
が u
と異なり、かつ inclusive
であるか、または丸め上げ後の桁数が upper
の桁数よりも少ない場合」に丸め上げを許可していました。
しかし、この条件には不備がありました。特に、m
と u
が非常に近い値である場合、つまり m
を丸め上げた結果が u
に非常に近くなるが、厳密には u
に到達しない場合において、丸め上げが正しく行われない可能性がありました。
新しいコードでは、okup
の定義が以下のように変更されました。
okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
この変更のポイントは、m+1 < u
という新しい条件が追加されたことです。
m+1 < u
: これは、「現在の仮数部m
を1つ増やした値が、上限u
よりも小さい場合」を意味します。この条件が追加されたことで、m
を丸め上げた結果がu
に到達しない場合でも、丸め上げが許容されるケースがより正確にカバーされるようになりました。
具体的には、元の条件 i+1 < upper.nd
は、丸め上げ後の桁数に関するものでしたが、m+1 < u
は、丸め上げによって数値が u
を超えないことを保証しつつ、より正確な丸め上げを可能にします。これにより、m
が u
に非常に近いが、丸め上げによって u
を超えないような「コーナーケース」において、正しい丸め上げが行われるようになりました。
この修正により、strconv
パッケージの浮動小数点数から文字列への変換が、より堅牢で正確になり、特定の数値が誤って表現されるバグが解消されました。
コアとなるコードの変更箇所
src/pkg/strconv/ftoa.go
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -241,7 +241,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// Okay to round up if upper has a different digit and
// either upper is inclusive or upper is bigger than the result of rounding up.
- okup := m != u && (inclusive || i+1 < upper.nd)
+ okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
// If it's okay to do either, then round to the nearest one.
// If it's okay to do only one, do it.
src/pkg/strconv/ftoa_test.go
--- a/src/pkg/strconv/ftoa_test.go
+++ b/src/pkg/strconv/ftoa_test.go
@@ -123,6 +123,10 @@ var ftoatests = []ftoaTest{
{2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"},
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
{2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"},
+
+ // Issue 2625.
+ {383260575764816448, 'f', 0, "383260575764816448"},
+ {383260575764816448, 'g', -1, "3.8326057576481645e+17"},
}
func TestFtoa(t *testing.T) {
コアとなるコードの解説
src/pkg/strconv/ftoa.go
の変更
変更の中心は、roundShortest
関数内の okup
変数の定義行です。
- 変更前:
okup := m != u && (inclusive || i+1 < upper.nd)
- 変更後:
okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
この変更により、丸め上げが許容される条件に m+1 < u
が追加されました。これは、現在の仮数部 m
を1つ増やした値が、上限 u
よりも小さい場合に丸め上げを許可するという意味です。これにより、m
が u
に非常に近いが、丸め上げによって u
を超えないような特定のコーナーケースにおいて、正しい丸め上げが行われるようになりました。
src/pkg/strconv/ftoa_test.go
の変更
テストファイルには、Issue 2625
に関連する新しいテストケースが追加されています。
// Issue 2625.
{383260575764816448, 'f', 0, "383260575764816448"},
{383260575764816448, 'g', -1, "3.8326057576481645e+17"},
これらのテストケースは、383260575764816448
という特定の浮動小数点数が、異なるフォーマット('f'
と 'g'
)で正しく文字列に変換されることを検証しています。特に、'g'
フォーマットでの変換結果 3.8326057576481645e+17
は、丸め上げが正しく行われた結果を示しており、このコミットが修正したバグの具体的な影響範囲をカバーしています。これらのテストの追加は、バグが修正されたことを確認し、将来的な回帰を防ぐための重要なステップです。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/6f77cd291419dcd70faf611ecc82dc50cc294552
- 関連Issue (旧トラッカー): Issue 2625 (Go言語の旧Issueトラッカーに存在したバグ報告)
参考にした情報源リンク
- コミットメッセージとコードの差分
- Go言語の
strconv
パッケージのドキュメント (一般的な情報) - IEEE 754 浮動小数点数標準に関する一般的な知識
- 浮動小数点数の最短表現に関する一般的な知識
- Go言語の旧Issueトラッカーに関する一般的な情報 (Issue 2625の具体的な内容は確認できませんでしたが、その存在と役割を理解するために参照しました)
[インデックス 11133] ファイルの概要
このコミットでは、src/pkg/strconv/ftoa.go
と src/pkg/strconv/ftoa_test.go
の2つのファイルが変更されています。
コミット
commit 6f77cd291419dcd70faf611ecc82dc50cc294552
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 12 11:32:28 2012 -0800
strconv: fix round up corner case
Comment described the correct condition
but the code did not implement it.
Fixes #2625.
R=remyoudompheng
CC=golang-dev
https://golang.org/cl/5530082
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6f77cd291419dcd70faf611ecc82dc50cc294552
元コミット内容
strconv: fix round up corner case
Comment described the correct condition
but the code did not implement it.
Fixes #2625.
R=remyoudompheng
CC=golang-dev
https://golang.org/cl/5530082
変更の背景
このコミットは、Go言語の標準ライブラリである strconv
パッケージ内の浮動小数点数から文字列への変換(ftoa
、float to ASCII)における丸め処理のコーナーケースのバグを修正するものです。具体的には、ftoa.go
ファイル内の roundShortest
関数において、コメントで記述されていた正しい丸め上げの条件が、実際のコードでは正しく実装されていなかったことが問題でした。
コミットメッセージにある「Fixes #2625」は、当時のGo言語のIssueトラッカー(現在はGitHubに移行済み)におけるバグ報告の参照です。このバグにより、特定の浮動小数点数が期待通りに丸められず、誤った文字列表現が生成される可能性がありました。この修正は、数値の正確な文字列表現を保証するために不可欠でした。
前提知識の解説
strconv
パッケージ
strconv
パッケージは、Go言語において基本的なデータ型(真偽値、整数、浮動小数点数)と文字列との間の変換機能を提供します。例えば、Atoi
は文字列を整数に、Itoa
は整数を文字列に変換します。浮動小数点数に関しては、ParseFloat
が文字列を浮動小数点数に、FormatFloat
が浮動小数点数を文字列に変換します。このコミットが関連するのは、FormatFloat
の内部で利用される ftoa
(float to ASCII) 処理です。
浮動小数点数表現 (IEEE 754)
コンピュータにおける浮動小数点数は、通常IEEE 754標準に従って表現されます。これは、数値を符号部、指数部、仮数部に分けて表現する方法です。この表現には、有限のビット数で無限の数を表現するため、常に精度に関する課題が伴います。特に、10進数で正確に表現できる数値でも、2進数では循環小数になる場合があり、その結果、丸め誤差が生じます。
丸め処理と「最短表現」
浮動小数点数を文字列に変換する際、多くの場合、元の浮動小数点数の値を正確に表現しつつ、かつ最も短い文字列形式(最短表現)を生成することが求められます。これは、例えば 0.1
という浮動小数点数が内部的には 0.1000000000000000055511151231257827021181583404541015625
のように表現される場合でも、ユーザーには 0.1
と表示したいという要求に応えるためです。
最短表現を実現するためには、元の浮動小数点数と、その隣接する浮動小数点数の中間点との関係を考慮して丸め処理を行う必要があります。ある浮動小数点数を文字列に変換する際、その文字列を再度浮動小数点数に変換したときに、元の値と完全に一致するかどうかを検証する「ラウンドトリップ」の概念も重要です。
roundShortest
関数
strconv
パッケージの ftoa.go
にある roundShortest
関数は、この最短表現を実現するための丸め処理を担当します。この関数は、与えられた浮動小数点数を10進数の仮数部と指数部に分解し、それを文字列に変換する際に、どの桁で丸めるべきかを決定します。この決定は、元の浮動小数点数と、その上下の隣接する浮動小数点数との相対的な距離に基づいて行われます。
技術的詳細
このコミットの核心は、roundShortest
関数内の okup
変数の計算ロジックの変更です。okup
は、現在の桁で丸め上げを行うことが許容されるかどうかを判断するためのブール値です。
元のコードでは、okup
は以下のように定義されていました。
okup := m != u && (inclusive || i+1 < upper.nd)
ここで、
m
は現在の10進数の仮数部(mantissa)u
は元の浮動小数点数よりわずかに大きい値の10進数の仮数部(upper bound)inclusive
は、丸め上げが許容される条件の一つi+1 < upper.nd
は、丸め上げ後の桁数がupper
の桁数よりも少ない場合に丸め上げが許容される条件
この条件は、「m
が u
と異なり、かつ inclusive
であるか、または丸め上げ後の桁数が upper
の桁数よりも少ない場合」に丸め上げを許可していました。
しかし、この条件には不備がありました。特に、m
と u
が非常に近い値である場合、つまり m
を丸め上げた結果が u
に非常に近くなるが、厳密には u
に到達しない場合において、丸め上げが正しく行われない可能性がありました。
新しいコードでは、okup
の定義が以下のように変更されました。
okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
この変更のポイントは、m+1 < u
という新しい条件が追加されたことです。
m+1 < u
: これは、「現在の仮数部m
を1つ増やした値が、上限u
よりも小さい場合」を意味します。この条件が追加されたことで、m
を丸め上げた結果がu
に到達しない場合でも、丸め上げが許容されるケースがより正確にカバーされるようになりました。
具体的には、元の条件 i+1 < upper.nd
は、丸め上げ後の桁数に関するものでしたが、m+1 < u
は、丸め上げによって数値が u
を超えないことを保証しつつ、より正確な丸め上げを可能にします。これにより、m
が u
に非常に近いが、丸め上げによって u
を超えないような「コーナーケース」において、正しい丸め上げが行われるようになりました。
この修正により、strconv
パッケージの浮動小数点数から文字列への変換が、より堅牢で正確になり、特定の数値が誤って表現されるバグが解消されました。
コアとなるコードの変更箇所
src/pkg/strconv/ftoa.go
--- a/src/pkg/strconv/ftoa.go
+++ b/src/pkg/strconv/ftoa.go
@@ -241,7 +241,7 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
// Okay to round up if upper has a different digit and
// either upper is inclusive or upper is bigger than the result of rounding up.
- okup := m != u && (inclusive || i+1 < upper.nd)
+ okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
// If it's okay to do either, then round to the nearest one.
// If it's okay to do only one, do it.
src/pkg/strconv/ftoa_test.go
--- a/src/pkg/strconv/ftoa_test.go
+++ b/src/pkg/strconv/ftoa_test.go
@@ -123,6 +123,10 @@ var ftoatests = []ftoaTest{
{2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"},
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
{2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"},
+
+ // Issue 2625.
+ {383260575764816448, 'f', 0, "383260575764816448"},
+ {383260575764816448, 'g', -1, "3.8326057576481645e+17"},
}
func TestFtoa(t *testing.T) {
コアとなるコードの解説
src/pkg/strconv/ftoa.go
の変更
変更の中心は、roundShortest
関数内の okup
変数の定義行です。
- 変更前:
okup := m != u && (inclusive || i+1 < upper.nd)
- 変更後:
okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
この変更により、丸め上げが許容される条件に m+1 < u
が追加されました。これは、現在の仮数部 m
を1つ増やした値が、上限 u
よりも小さい場合に丸め上げを許可するという意味です。これにより、m
が u
に非常に近いが、丸め上げによって u
を超えないような特定のコーナーケースにおいて、正しい丸め上げが行われるようになりました。
src/pkg/strconv/ftoa_test.go
の変更
テストファイルには、Issue 2625
に関連する新しいテストケースが追加されています。
// Issue 2625.
{383260575764816448, 'f', 0, "383260575764816448"},
{383260575764816448, 'g', -1, "3.8326057576481645e+17"},
これらのテストケースは、383260575764816448
という特定の浮動小数点数が、異なるフォーマット('f'
と 'g'
)で正しく文字列に変換されることを検証しています。特に、'g'
フォーマットでの変換結果 3.8326057576481645e+17
は、丸め上げが正しく行われた結果を示しており、このコミットが修正したバグの具体的な影響範囲をカバーしています。これらのテストの追加は、バグが修正されたことを確認し、将来的な回帰を防ぐための重要なステップです。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/6f77cd291419dcd70faf611ecc82dc50cc294552
- 関連Issue (旧トラッカー): Issue 2625 (Go言語の旧Issueトラッカーに存在したバグ報告)
参考にした情報源リンク
- コミットメッセージとコードの差分
- Go言語の
strconv
パッケージのドキュメント (一般的な情報) - IEEE 754 浮動小数点数標準に関する一般的な知識
- 浮動小数点数の最短表現に関する一般的な知識
- Go言語の旧Issueトラッカーに関する一般的な情報 (Issue 2625の具体的な内容は確認できませんでしたが、その存在と役割を理解するために参照しました)