[インデックス 10982] ファイルの概要
このコミットは、src/pkg/strconv/atof_test.go
と src/pkg/strconv/extfloat.go
の2つのファイルを変更しています。前者はテストファイルであり、後者はGo言語の標準ライブラリであるstrconv
パッケージ内で拡張浮動小数点数(extended-float)の変換ロジックを扱うファイルです。
コミット
commit 2afebbdf35baaf289328c86a7579c8961617ecf8
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Thu Dec 22 17:28:35 2011 -0500
strconv: fix bug in extended-float based conversion.
A test intended for denormals erroneously returned true also for
infinities, leading to bad overflows and wrong error estimates.
R=rsc
CC=golang-dev, remy
https://golang.org/cl/5489091
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2afebbdf35baaf289328c86a7579c8961617ecf8
元コミット内容
strconv: fix bug in extended-float based conversion.
A test intended for denormals erroneously returned true also for
infinities, leading to bad overflows and wrong error estimates.
R=rsc
CC=golang-dev, remy
https://golang.org/cl/5489091
変更の背景
このコミットは、Go言語のstrconv
パッケージにおける浮動小数点数変換のバグを修正するものです。具体的には、非正規化数(denormal numbers)を検出するための内部的なテストロジックが、誤って無限大(infinities)に対しても真を返してしまうという問題がありました。この誤った判定が、数値のオーバーフローや不正確な誤差評価を引き起こし、結果としてParseFloat
関数が期待通りの結果を返さない原因となっていました。
浮動小数点数の変換は、非常に精密な計算が求められる領域であり、特に非正規化数や無限大といった特殊な値の扱いは、IEEE 754標準に厳密に準拠する必要があります。このバグは、これらの特殊なケースにおけるGoのstrconv
パッケージの堅牢性に影響を与えていました。
前提知識の解説
Go言語の strconv
パッケージ
strconv
パッケージは、Go言語において基本的なデータ型(文字列、整数、浮動小数点数、真偽値など)間の変換を提供する標準ライブラリです。特にParseFloat
関数は、文字列を浮動小数点数(float32
またはfloat64
)に変換するために使用されます。このパッケージは、数値の厳密なパースとフォーマットを保証するために、IEEE 754浮動小数点数標準に準拠した実装を提供しています。
浮動小数点数 (IEEE 754) の表現
浮動小数点数は、コンピュータが実数を近似的に表現するための形式です。IEEE 754は、浮動小数点数の表現と演算に関する国際標準であり、float32
(単精度)やfloat64
(倍精度)といった形式が定義されています。これらの形式は、符号部、指数部、仮数部から構成され、非常に広い範囲の数値を表現できますが、精度には限界があります。
正規化数 (Normal numbers) と非正規化数 (Denormal numbers)
- 正規化数 (Normal numbers): 浮動小数点数の表現において、仮数部の最上位ビットが常に1であると暗黙的に仮定される形式です。これにより、表現可能な数値の範囲と精度が最大化されます。
- 非正規化数 (Denormal numbers) / 非正規数 (Subnormal numbers): ゼロに非常に近い小さな数値を表現するために使用される特殊な形式です。正規化数とは異なり、仮数部の最上位ビットが0である場合があります。これにより、アンダーフロー(表現可能な最小値よりも小さい絶対値の数値が発生すること)が発生した際に、精度を犠牲にしながらもゼロではない値を表現し続けることができます。非正規化数の計算は、正規化数に比べてパフォーマンスが低下する可能性があります。
無限大 (Infinity)
IEEE 754標準では、正の無限大(+Inf
)と負の無限大(-Inf
)が定義されています。これらは、数値が表現可能な最大値を超えた場合(オーバーフロー)や、ゼロ除算などの特定の演算結果として発生します。strconv.ParseFloat
は、"Inf"や"Infinity"といった文字列をこれらの特殊な値として認識し、変換します。
ParseFloat
関数の役割
strconv.ParseFloat(s string, bitSize int)
関数は、与えられた文字列s
をbitSize
(32または64)に応じた浮動小数点数に変換します。この関数は、文字列が有効な浮動小数点数形式でない場合や、変換結果が指定されたbitSize
の範囲を超えた場合にエラーを返します。内部的には、文字列を解析し、IEEE 754標準に従って浮動小数点数のバイナリ表現を構築します。
技術的詳細
このバグは、strconv
パッケージ内部のextfloat.go
ファイルにあるextFloat
型と、そのAssignDecimal
メソッドのロジックに起因していました。extFloat
は、float64
よりも高い精度で浮動小数点数を扱うための内部表現です。
問題の核心は、AssignDecimal
メソッド内で非正規化数を検出するための条件式にありました。元のコードでは、非正規化数を判定するためにf.exp <= denormalExp || f.exp >= 1023-64
という条件を使用していました。ここで、f.exp
はextFloat
の指数部を表します。
f.exp <= denormalExp
: これは非正規化数を正しく検出するための条件です。denormalExp
は、非正規化数の指数部の閾値を定義しています。f.exp >= 1023-64
: この部分が問題でした。1023-64
という値は、float64
の最大正規化数の指数部に関連する値です。しかし、この条件は無限大のケースも誤って含んでしまっていました。無限大は、指数部が最大値(1023
)であり、仮数部がゼロである特殊な表現を持つため、この条件に合致してしまうことがありました。
結果として、無限大の値をパースしようとした際に、非正規化数として誤って扱われ、その後の処理で不正確な計算やオーバーフローが発生していました。これは、特に非常に大きな数値を文字列からfloat64
に変換する際に顕著な問題となりました。
コアとなるコードの変更箇所
このコミットでは、主に以下の2つのファイルが変更されています。
-
src/pkg/strconv/atof_test.go
:- 新しいテストケースが追加されました。
このテストケースは、以前のバグによって誤ってパースされていた非常に大きな数値(+ // A very large number (initially wrongly parsed by the fast algorithm). + {"4.630813248087435e+307", "4.630813248087435e+307", nil},
float64
の最大値に近い値)を対象としています。 TestAtofRandom
関数内のt.Errorf
の引数の順序が修正されました。これはバグ修正とは直接関係なく、テストのエラーメッセージの可読性を向上させるためのものです。- t.Errorf("number %s badly parsed as %b (expected %b)", test.s, test.x, x) + t.Errorf("number %s badly parsed as %b (expected %b)", test.s, x, test.x)
- 新しいテストケースが追加されました。
-
src/pkg/strconv/extfloat.go
:extFloat
型のAssignDecimal
メソッド内の条件式が修正されました。- if f.exp <= denormalExp || f.exp >= 1023-64 { + if f.exp <= denormalExp {
f.exp >= 1023-64
という条件が削除され、非正規化数の検出がf.exp <= denormalExp
のみに限定されました。
コアとなるコードの解説
src/pkg/strconv/extfloat.go
の変更
このコミットの最も重要な変更は、src/pkg/strconv/extfloat.go
ファイル内のAssignDecimal
メソッドにおける条件式の修正です。
元のコード:
if f.exp <= denormalExp || f.exp >= 1023-64 {
修正後のコード:
if f.exp <= denormalExp {
この変更により、非正規化数を検出するための条件がf.exp <= denormalExp
のみに絞られました。これにより、無限大の値を非正規化数として誤って扱うことがなくなりました。無限大は、その指数部が1023
(float64
の最大指数)であり、仮数部がゼロであるという特殊な表現を持つため、以前の条件f.exp >= 1023-64
に合致してしまうことがありました。この修正によって、無限大は無限大として正しく処理され、非正規化数とは区別されるようになりました。
src/pkg/strconv/atof_test.go
の変更
追加されたテストケース:
{"4.630813248087435e+307", "4.630813248087435e+307", nil},
このテストケースは、float64
で表現可能な非常に大きな数値(約4.63 x 10^307)を文字列からパースする際の正確性を検証するために追加されました。この数値は、以前のバグの影響を受けやすく、誤った結果を返していました。このテストケースが追加され、修正後のコードで正しくパースされることを確認することで、バグが修正されたことが保証されます。
TestAtofRandom
関数内のt.Errorf
の引数順序の修正は、デバッグ時のメッセージの分かりやすさを向上させるためのもので、機能的な変更ではありません。
これらの変更により、strconv.ParseFloat
は、非正規化数と無限大をより正確に区別し、非常に大きな数値の変換においても堅牢性が向上しました。
関連リンク
- Go CL 5489091: https://golang.org/cl/5489091
参考にした情報源リンク
- IEEE 754 浮動小数点数標準に関する情報 (例: Wikipedia, 各種技術ドキュメント)
- Go言語の
strconv
パッケージの公式ドキュメント - Go言語の浮動小数点数に関する議論や実装の詳細(Goのソースコードや関連するIssueなど)