[インデックス 1097] ファイルの概要
このコミットは、Go言語のランタイムにおける浮動小数点数(float64
)の出力処理を改善し、Inf
(無限大)およびNaN
(非数)といった特殊な値を正しく表示できるようにするものです。また、整数出力関数にも関連する変更が加えられています。
コミット
commit e8766354a4666044d23027de177e57f2fc992802
Author: Russ Cox <rsc@golang.org>
Date: Mon Nov 10 14:54:10 2008 -0800
handle Inf, NaN in float print
R=r
DELTA=48 (23 added, 14 deleted, 11 changed)
OCL=18707
CL=18922
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e8766354a4666044d23027de177e57f2fc992802
元コミット内容
浮動小数点数の出力において、無限大(Inf)と非数(NaN)を処理するように変更。
変更の背景
Go言語の初期段階において、浮動小数点数の特殊な値である無限大(Inf
)や非数(NaN
)が標準出力関数(sys·printfloat
)で正しく扱われていませんでした。これらの値が出力されると、予期しない文字列やクラッシュを引き起こす可能性がありました。このコミットは、これらの特殊な浮動小数点値をIEEE 754標準に準拠した形式(例: NaN
, +Inf
)で出力できるようにすることで、ランタイムの堅牢性と正確性を向上させることを目的としています。
前提知識の解説
- 浮動小数点数 (Floating-Point Numbers): コンピュータで実数を表現するための形式です。一般的にIEEE 754標準に従って表現され、符号部、指数部、仮数部から構成されます。
- IEEE 754: 浮動小数点数の表現と演算に関する国際標準です。この標準では、通常の数値に加えて、以下のような特殊な値を定義しています。
- 無限大 (Infinity, Inf): 0で割るなどの演算結果として生じる、非常に大きな(または小さな)値を表します。正の無限大 (
+Inf
) と負の無限大 (-Inf
) があります。 - 非数 (Not a Number, NaN): 不定形な演算(例: 0/0, 無限大 - 無限大)の結果として生じる、数値ではないことを示す特殊な値です。
- 無限大 (Infinity, Inf): 0で割るなどの演算結果として生じる、非常に大きな(または小さな)値を表します。正の無限大 (
- Go言語のランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのコードです。ガベージコレクション、スケジューリング、I/O操作、プリミティブ型の操作などが含まれます。
src/runtime
ディレクトリにそのソースコードがあります。 sys·printfloat
: Goランタイム内部で使用される、浮動小数点数を標準出力に出力するための低レベル関数です。sys·printint
: Goランタイム内部で使用される、整数を標準出力に出力するための低レベル関数です。
技術的詳細
このコミットの主要な変更点は、src/runtime/print.c
内の sys·printfloat
関数に、浮動小数点数が NaN
または Inf
であるかを判定し、それに応じて適切な文字列を出力するロジックが追加されたことです。
-
NaN
およびInf
の検出:sys·printfloat
の冒頭に、isNaN(v)
とisInf(v, sign)
という新しい関数呼び出しが追加されました。isNaN(v)
は、入力されたfloat64
値v
がNaN
であるかを判定します。isInf(v, sign)
は、入力されたfloat64
値v
がInf
であるかを判定し、sign
引数によって正の無限大か負の無限大かを区別できます。- これらの関数は
src/runtime/runtime.c
で定義され、static
からbool
型の戻り値を持つ関数に変更され、src/runtime/runtime.h
で宣言されることで、ランタイム内で広く利用可能になりました。
-
特殊値の出力:
isNaN(v)
が真の場合、sys·write(1, "NaN", 3)
を呼び出して "NaN" を出力します。isInf(v, 0)
またはisInf(v, -1)
が真の場合(正または負の無限大)、sys·write(1, "+Inf", 4)
を呼び出して "+Inf" を出力します。元のコミットでは負の無限大も+Inf
と出力していますが、これは初期の実装であり、後のコミットで-Inf
の出力が追加される可能性があります。
-
指数部の出力桁数の変更:
sys·printfloat
において、指数部(e
)の出力桁数が2桁から3桁に拡張されました。これにより、より大きな指数を持つ浮動小数点数も正しく表示できるようになります。具体的には、buf[n+4]
、buf[n+5]
、buf[n+6]
を使用して3桁の指数を出力し、sys·write
の長さもn+6
からn+7
に変更されています。
-
整数出力関数のリファクタリング:
sys·printint(int64 v)
関数がリファクタリングされ、符号なし整数を出力する新しい関数sys·printuint(uint64 v)
が導入されました。sys·printint
は、まず入力値v
が負であるかをチェックし、負であれば "-" を出力してから、v
の絶対値をsys·printuint
に渡して出力するように変更されました。- これにより、整数出力ロジックが符号の処理と数値の桁ごとの処理に分離され、コードの可読性と保守性が向上しています。
コアとなるコードの変更箇所
src/runtime/print.c
:sys·printfloat
関数にisNaN
とisInf
のチェックと、それに応じた "NaN" や "+Inf" の出力ロジックが追加されました。sys·printfloat
の指数部出力ロジックが2桁から3桁に拡張されました。sys·printint
関数がリファクタリングされ、符号なし整数を出力するsys·printuint
関数が新設されました。
src/runtime/runtime.c
:isInf
とisNaN
関数の定義がstatic int32
からbool
に変更され、外部から呼び出し可能になりました。
src/runtime/runtime.h
:isInf
とisNaN
関数のプロトタイプ宣言が追加され、これらの関数がランタイムの他の部分から利用できるようになりました。
コアとなるコードの解説
src/runtime/print.c
の変更
// sys·printfloat 関数内
if(isNaN(v)) {
sys·write(1, "NaN", 3);
return;
}
if(isInf(v, 0)) { // 正の無限大
sys·write(1, "+Inf", 4);
return;
}
if(isInf(v, -1)) { // 負の無限大
sys·write(1, "+Inf", 4); // 初期実装では負の無限大も+Infと出力
return;
}
// 指数部の出力桁数変更
- buf[n+4] = (e/10) + '0';
- buf[n+5] = (e%10) + '0';
- sys·write(1, buf, n+6);
+ buf[n+4] = (e/100) + '0';
+ buf[n+5] = (e/10)%10 + '0';
+ buf[n+6] = (e%10) + '0';
+ sys·write(1, buf, n+7);
// sys·printint と sys·printuint の変更
void
sys·printuint(uint64 v)
{
byte buf[100];
int32 i;
for(i=nelem(buf)-1; i>0; i--) {
buf[i] = v%10 + '0';
if(v < 10)
break;
v = v/10;
}
sys·write(1, buf+i, nelem(buf)-i);
}
void
sys·printint(int64 v)
{
if(v < 0) {
sys·write(1, "-", 1);
v = -v;
}
sys·printuint(v);
}
sys·printfloat
は、まず isNaN
と isInf
を使って入力値が特殊な浮動小数点数であるかをチェックします。もしそうであれば、それぞれ "NaN" または "+Inf" を直接出力して関数を終了します。これにより、特殊値が通常の数値として誤って処理されることを防ぎます。また、指数部の出力ロジックが変更され、最大3桁の指数に対応できるようになりました。
sys·printint
は、符号の処理を sys·printuint
から分離しました。sys·printint
は、負の数であればまず負符号を出力し、その後数値の絶対値を sys·printuint
に渡します。sys·printuint
は、与えられた符号なし整数を桁ごとに文字に変換し、バッファに格納して出力します。この分離により、コードがよりモジュール化され、それぞれの関数の責任が明確になりました。
src/runtime/runtime.c
および src/runtime/runtime.h
の変更
isInf
と isNaN
関数は、浮動小数点数の内部表現(IEEE 754)を直接操作して、その値が無限大または非数であるかを判定します。これらの関数が static
から bool
型の戻り値を持つように変更され、runtime.h
で宣言されたことで、ランタイムの他の部分からもこれらの判定ロジックを再利用できるようになりました。
関連リンク
- IEEE 754 - Wikipedia: https://ja.wikipedia.org/wiki/IEEE_754
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- コミットハッシュ:
e8766354a4666044d23027de177e57f2fc992802
の内容 - Go言語のドキュメント (浮動小数点数に関する一般的な情報)
- C言語における浮動小数点数の扱いに関する一般的な知識