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

[インデックス 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, 無限大 - 無限大)の結果として生じる、数値ではないことを示す特殊な値です。
  • Go言語のランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのコードです。ガベージコレクション、スケジューリング、I/O操作、プリミティブ型の操作などが含まれます。src/runtimeディレクトリにそのソースコードがあります。
  • sys·printfloat: Goランタイム内部で使用される、浮動小数点数を標準出力に出力するための低レベル関数です。
  • sys·printint: Goランタイム内部で使用される、整数を標準出力に出力するための低レベル関数です。

技術的詳細

このコミットの主要な変更点は、src/runtime/print.c 内の sys·printfloat 関数に、浮動小数点数が NaN または Inf であるかを判定し、それに応じて適切な文字列を出力するロジックが追加されたことです。

  1. NaN および Inf の検出:

    • sys·printfloat の冒頭に、isNaN(v)isInf(v, sign) という新しい関数呼び出しが追加されました。
    • isNaN(v) は、入力された float64vNaN であるかを判定します。
    • isInf(v, sign) は、入力された float64vInf であるかを判定し、sign 引数によって正の無限大か負の無限大かを区別できます。
    • これらの関数は src/runtime/runtime.c で定義され、static から bool 型の戻り値を持つ関数に変更され、src/runtime/runtime.h で宣言されることで、ランタイム内で広く利用可能になりました。
  2. 特殊値の出力:

    • isNaN(v) が真の場合、sys·write(1, "NaN", 3) を呼び出して "NaN" を出力します。
    • isInf(v, 0) または isInf(v, -1) が真の場合(正または負の無限大)、sys·write(1, "+Inf", 4) を呼び出して "+Inf" を出力します。元のコミットでは負の無限大も+Infと出力していますが、これは初期の実装であり、後のコミットで-Infの出力が追加される可能性があります。
  3. 指数部の出力桁数の変更:

    • sys·printfloat において、指数部(e)の出力桁数が2桁から3桁に拡張されました。これにより、より大きな指数を持つ浮動小数点数も正しく表示できるようになります。具体的には、buf[n+4]buf[n+5]buf[n+6] を使用して3桁の指数を出力し、sys·write の長さも n+6 から n+7 に変更されています。
  4. 整数出力関数のリファクタリング:

    • sys·printint(int64 v) 関数がリファクタリングされ、符号なし整数を出力する新しい関数 sys·printuint(uint64 v) が導入されました。
    • sys·printint は、まず入力値 v が負であるかをチェックし、負であれば "-" を出力してから、v の絶対値を sys·printuint に渡して出力するように変更されました。
    • これにより、整数出力ロジックが符号の処理と数値の桁ごとの処理に分離され、コードの可読性と保守性が向上しています。

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

  • src/runtime/print.c:
    • sys·printfloat 関数に isNaNisInf のチェックと、それに応じた "NaN" や "+Inf" の出力ロジックが追加されました。
    • sys·printfloat の指数部出力ロジックが2桁から3桁に拡張されました。
    • sys·printint 関数がリファクタリングされ、符号なし整数を出力する sys·printuint 関数が新設されました。
  • src/runtime/runtime.c:
    • isInfisNaN 関数の定義が static int32 から bool に変更され、外部から呼び出し可能になりました。
  • src/runtime/runtime.h:
    • isInfisNaN 関数のプロトタイプ宣言が追加され、これらの関数がランタイムの他の部分から利用できるようになりました。

コアとなるコードの解説

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 は、まず isNaNisInf を使って入力値が特殊な浮動小数点数であるかをチェックします。もしそうであれば、それぞれ "NaN" または "+Inf" を直接出力して関数を終了します。これにより、特殊値が通常の数値として誤って処理されることを防ぎます。また、指数部の出力ロジックが変更され、最大3桁の指数に対応できるようになりました。

sys·printint は、符号の処理を sys·printuint から分離しました。sys·printint は、負の数であればまず負符号を出力し、その後数値の絶対値を sys·printuint に渡します。sys·printuint は、与えられた符号なし整数を桁ごとに文字に変換し、バッファに格納して出力します。この分離により、コードがよりモジュール化され、それぞれの関数の責任が明確になりました。

src/runtime/runtime.c および src/runtime/runtime.h の変更

isInfisNaN 関数は、浮動小数点数の内部表現(IEEE 754)を直接操作して、その値が無限大または非数であるかを判定します。これらの関数が static から bool 型の戻り値を持つように変更され、runtime.h で宣言されたことで、ランタイムの他の部分からもこれらの判定ロジックを再利用できるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (GitHub): https://github.com/golang/go
  • コミットハッシュ: e8766354a4666044d23027de177e57f2fc992802 の内容
  • Go言語のドキュメント (浮動小数点数に関する一般的な情報)
  • C言語における浮動小数点数の扱いに関する一般的な知識