Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11133] ファイルの概要

このコミットでは、src/pkg/strconv/ftoa.gosrc/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 の桁数よりも少ない場合に丸め上げが許容される条件

この条件は、「mu と異なり、かつ inclusive であるか、または丸め上げ後の桁数が upper の桁数よりも少ない場合」に丸め上げを許可していました。

しかし、この条件には不備がありました。特に、mu が非常に近い値である場合、つまり 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 を超えないことを保証しつつ、より正確な丸め上げを可能にします。これにより、mu に非常に近いが、丸め上げによって 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 よりも小さい場合に丸め上げを許可するという意味です。これにより、mu に非常に近いが、丸め上げによって 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 は、丸め上げが正しく行われた結果を示しており、このコミットが修正したバグの具体的な影響範囲をカバーしています。これらのテストの追加は、バグが修正されたことを確認し、将来的な回帰を防ぐための重要なステップです。

関連リンク

参考にした情報源リンク

  • コミットメッセージとコードの差分
  • Go言語の strconv パッケージのドキュメント (一般的な情報)
  • IEEE 754 浮動小数点数標準に関する一般的な知識
  • 浮動小数点数の最短表現に関する一般的な知識
  • Go言語の旧Issueトラッカーに関する一般的な情報 (Issue 2625の具体的な内容は確認できませんでしたが、その存在と役割を理解するために参照しました)

[インデックス 11133] ファイルの概要

このコミットでは、src/pkg/strconv/ftoa.gosrc/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 の桁数よりも少ない場合に丸め上げが許容される条件

この条件は、「mu と異なり、かつ inclusive であるか、または丸め上げ後の桁数が upper の桁数よりも少ない場合」に丸め上げを許可していました。

しかし、この条件には不備がありました。特に、mu が非常に近い値である場合、つまり 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 を超えないことを保証しつつ、より正確な丸め上げを可能にします。これにより、mu に非常に近いが、丸め上げによって 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 よりも小さい場合に丸め上げを許可するという意味です。これにより、mu に非常に近いが、丸め上げによって 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 は、丸め上げが正しく行われた結果を示しており、このコミットが修正したバグの具体的な影響範囲をカバーしています。これらのテストの追加は、バグが修正されたことを確認し、将来的な回帰を防ぐための重要なステップです。

関連リンク

参考にした情報源リンク

  • コミットメッセージとコードの差分
  • Go言語の strconv パッケージのドキュメント (一般的な情報)
  • IEEE 754 浮動小数点数標準に関する一般的な知識
  • 浮動小数点数の最短表現に関する一般的な知識
  • Go言語の旧Issueトラッカーに関する一般的な情報 (Issue 2625の具体的な内容は確認できませんでしたが、その存在と役割を理解するために参照しました)