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

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

このコミットは、src/pkg/strconv/atof_test.gosrc/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)関数は、与えられた文字列sbitSize(32または64)に応じた浮動小数点数に変換します。この関数は、文字列が有効な浮動小数点数形式でない場合や、変換結果が指定されたbitSizeの範囲を超えた場合にエラーを返します。内部的には、文字列を解析し、IEEE 754標準に従って浮動小数点数のバイナリ表現を構築します。

技術的詳細

このバグは、strconvパッケージ内部のextfloat.goファイルにあるextFloat型と、そのAssignDecimalメソッドのロジックに起因していました。extFloatは、float64よりも高い精度で浮動小数点数を扱うための内部表現です。

問題の核心は、AssignDecimalメソッド内で非正規化数を検出するための条件式にありました。元のコードでは、非正規化数を判定するためにf.exp <= denormalExp || f.exp >= 1023-64という条件を使用していました。ここで、f.expextFloatの指数部を表します。

  • f.exp <= denormalExp: これは非正規化数を正しく検出するための条件です。denormalExpは、非正規化数の指数部の閾値を定義しています。
  • f.exp >= 1023-64: この部分が問題でした。1023-64という値は、float64の最大正規化数の指数部に関連する値です。しかし、この条件は無限大のケースも誤って含んでしまっていました。無限大は、指数部が最大値(1023)であり、仮数部がゼロである特殊な表現を持つため、この条件に合致してしまうことがありました。

結果として、無限大の値をパースしようとした際に、非正規化数として誤って扱われ、その後の処理で不正確な計算やオーバーフローが発生していました。これは、特に非常に大きな数値を文字列からfloat64に変換する際に顕著な問題となりました。

コアとなるコードの変更箇所

このコミットでは、主に以下の2つのファイルが変更されています。

  1. 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)
      
  2. 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のみに絞られました。これにより、無限大の値を非正規化数として誤って扱うことがなくなりました。無限大は、その指数部が1023float64の最大指数)であり、仮数部がゼロであるという特殊な表現を持つため、以前の条件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は、非正規化数と無限大をより正確に区別し、非常に大きな数値の変換においても堅牢性が向上しました。

関連リンク

参考にした情報源リンク

  • IEEE 754 浮動小数点数標準に関する情報 (例: Wikipedia, 各種技術ドキュメント)
  • Go言語のstrconvパッケージの公式ドキュメント
  • Go言語の浮動小数点数に関する議論や実装の詳細(Goのソースコードや関連するIssueなど)