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

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

このコミットでは、Go言語の math パッケージ内のアセンブリ言語ファイルにおいて、NaN (Not a Number) の定義が更新されました。具体的には、src/pkg/math/dim_amd64.ssrc/pkg/math/hypot_386.ssrc/pkg/math/hypot_amd64.ssrc/pkg/math/log_amd64.ssrc/pkg/math/sincos_amd64.s の5つのファイルが変更されています。

コミット

Go言語の math パッケージにおけるアセンブリ言語ファイル内で定義されているNaN (Not a Number) のビットパターンが修正されました。これにより、Goの浮動小数点演算におけるNaNの挙動が、より標準的なIEEE 754のQuiet NaNの定義に準拠するようになります。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/a5c4e0fa2a9b409d6b268a3aeba99af23b968a9a

元コミット内容

math: update definition of NaN in assembly language files

R=rsc, minux.ma, golang-dev, nigeltao
CC=golang-dev
https://golang.org/cl/6461047

変更の背景

この変更の背景には、浮動小数点数の標準であるIEEE 754におけるNaN (Not a Number) の表現に関する厳密な定義と、Go言語の内部実装におけるその整合性の問題があります。

IEEE 754標準では、NaNには大きく分けて「Quiet NaN (qNaN)」と「Signaling NaN (sNaN)」の2種類が存在します。

  • Quiet NaN (qNaN): ほとんどの浮動小数点演算で例外を発生させずに伝播するNaNです。通常、不正な演算結果(例: 0/0、無限大/無限大)によって生成されます。
  • Signaling NaN (sNaN): 浮動小数点演算で使用されると、通常は「無効演算 (Invalid Operation)」例外を発生させるNaNです。これは、未初期化の変数や、特定の条件でエラーを検出するために使用されることがあります。

IEEE 754の64ビット倍精度浮動小数点数 (double-precision) において、NaNは指数部が全て1 (0x7FF) で、仮数部が0以外の値を持つことで表現されます。qNaNとsNaNの区別は、仮数部の最上位ビット (MSB) によって行われます。

  • qNaN: 仮数部のMSBが1。
  • sNaN: 仮数部のMSBが0。

元のGoのアセンブリコードでは、NaNの定義として 0x7FF0000000000001 が使用されていました。この値は、指数部が 0x7FF であり、仮数部のMSBが0であるため、IEEE 754の定義上はSignaling NaNに該当します。しかし、Goの math パッケージの関数が通常返すNaNは、例外を発生させずに伝播するQuiet NaNであることが期待されます。

このコミットは、Goの math パッケージが生成するNaNが、意図した通りQuiet NaNとして振る舞うように、そのビットパターンを 0x7FF8000000000001 に修正することを目的としています。この新しい値は、仮数部のMSBが1であるため、IEEE 754のQuiet NaNの定義に合致します。これにより、Goの浮動小数点演算の挙動がより予測可能になり、標準に準拠するようになります。

前提知識の解説

浮動小数点数 (Floating-Point Numbers)

浮動小数点数は、非常に広い範囲の数値を表現するためにコンピュータで使用される数値表現形式です。これは、科学表記法(例: 1.23 × 10^4)に似ており、符号部、指数部、仮数部(または小数部)の3つの要素で構成されます。

  • 符号部 (Sign Bit): 数値が正か負かを示す1ビット。
  • 指数部 (Exponent): 数値の大きさを決定する部分。基数(通常は2)の何乗かを表現します。
  • 仮数部 (Mantissa/Significand): 数値の精度を決定する部分。有効数字を表現します。

IEEE 754 標準

IEEE 754は、浮動小数点演算の国際標準です。この標準は、浮動小数点数の表現形式、演算方法、および特殊な値(無限大、NaNなど)の取り扱いを定義しています。これにより、異なるコンピュータシステム間での浮動小数点演算の互換性が保証されます。

64ビット倍精度浮動小数点数 (double-precision) の場合、ビットの割り当ては以下のようになります。

  • 符号部: 1ビット (ビット63)
  • 指数部: 11ビット (ビット62-52)
  • 仮数部: 52ビット (ビット51-0)

NaN (Not a Number)

NaNは、IEEE 754標準で定義されている特殊な浮動小数点値の一つで、「非数」と訳されます。これは、数学的に定義できない、または表現できない結果(例: 0/0、無限大 - 無限大、負の数の平方根)を表すために使用されます。

NaNは、指数部が全て1 (0x7FF) で、仮数部が0以外の値を持つことで識別されます。

Quiet NaN (qNaN) と Signaling NaN (sNaN)

NaNはさらに、Quiet NaN (qNaN) と Signaling NaN (sNaN) に分類されます。この区別は、仮数部の最上位ビット (MSB、ビット51) の値によって行われます。

  • Quiet NaN (qNaN): 仮数部のMSBが1。
    • 特徴: ほとんどの浮動小数点演算で例外を発生させずに伝播します。一度qNaNが生成されると、その後の演算結果もqNaNになる傾向があります。
    • 用途: 不正な演算結果の伝播、未初期化の浮動小数点変数のデフォルト値など。
    • 例 (64ビット): 0x7FF8000000000001 (仮数部のMSBが1)
  • Signaling NaN (sNaN): 仮数部のMSBが0。
    • 特徴: 浮動小数点演算で使用されると、通常は「無効演算 (Invalid Operation)」例外を発生させます。
    • 用途: デバッグ、未初期化の変数の検出、特定の条件でのエラーチェックなど。
    • 例 (64ビット): 0x7FF0000000000001 (仮数部のMSBが0)

このコミットは、Goが生成するNaNが、例外を発生させないQuiet NaNとして正しく認識されるように、そのビットパターンを修正するものです。

技術的詳細

このコミットの核心は、Go言語の math パッケージ内のアセンブリ言語ファイルにおける NaN 定義のビットパターン変更です。

変更前: NaN = 0x7FF0000000000001 変更後: NaN = 0x7FF8000000000001

この変更は、64ビット倍精度浮動小数点数 (double-precision) のIEEE 754表現におけるNaNの構造に基づいています。

  • 符号部 (Sign Bit): 最上位ビット (ビット63)。この場合、両方の値で0であり、正の数を示します。
  • 指数部 (Exponent): ビット62からビット52までの11ビット。0x7FF は11ビット全てが1であることを示し、これは無限大またはNaNを表す特殊な指数です。
  • 仮数部 (Mantissa/Significand): ビット51からビット0までの52ビット。この部分がNaNの種類(qNaNまたはsNaN)と、そのNaNに付随するペイロード(診断情報など)を決定します。

変更前 (0x7FF0000000000001):

  • 指数部: 0x7FF (全て1)
  • 仮数部: 0x0000000000001
    • 仮数部の最上位ビット (ビット51): 0
    • 残りのビット: 0以外の値 (ここでは1)
    • 仮数部のMSBが0であるため、これはIEEE 754の定義上 Signaling NaN (sNaN) に該当します。

変更後 (0x7FF8000000000001):

  • 指数部: 0x7FF (全て1)
  • 仮数部: 0x8000000000001
    • 仮数部の最上位ビット (ビット51): 1
    • 残りのビット: 0以外の値 (ここでは1)
    • 仮数部のMSBが1であるため、これはIEEE 754の定義上 Quiet NaN (qNaN) に該当します。

この修正により、Goの math パッケージが生成するNaNは、標準的なQuiet NaNのビットパターンに合致するようになります。これにより、Goの浮動小数点演算が、他のシステムやライブラリとの間でNaNの挙動に関してより一貫性を持ち、予期せぬ浮動小数点例外の発生を防ぐことができます。

特に、hypot_386.s ファイルでは、32ビットアーキテクチャ向けにNaNのビットパターンをレジスタにロードする部分が修正されています。MOVL $0x7ff00000, rh+20(FP)MOVL $0x7ff80000, rh+20(FP) に変更されており、これは64ビット値の上位32ビット(0x7FF00000 から 0x7FF80000)の変更に対応しています。下位32ビットは 0x00000001 のままであり、これは64ビット値の 0x7FF8000000000001 を構成します。

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

diff --git a/src/pkg/math/dim_amd64.s b/src/pkg/math/dim_amd64.s
index c867db5537..a1505ce44c 100644
--- a/src/pkg/math/dim_amd64.s
+++ b/src/pkg/math/dim_amd64.s
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 #define PosInf 0x7FF0000000000000
-#define NaN    0x7FF0000000000001
+#define NaN    0x7FF8000000000001
 #define NegInf 0xFFF0000000000000
 
 // func Dim(x, y float64) float64
diff --git a/src/pkg/math/hypot_386.s b/src/pkg/math/hypot_386.s
index 70ff19a176..3b5ea88041 100644
--- a/src/pkg/math/hypot_386.s
+++ b/src/pkg/math/hypot_386.s
@@ -48,7 +48,7 @@ not_finite:
 	ANDL    $0x7fffffff, AX
 	CMPL    AX, $0x7ff00000
 	JEQ     is_inf
-\tMOVL    $0x7ff00000, rh+20(FP)  // return NaN = 0x7FF0000000000001
+\tMOVL    $0x7ff80000, rh+20(FP)  // return NaN = 0x7FF8000000000001
 	MOVL    $0x00000001, rl+16(FP)\n 	RET
 is_inf:
 diff --git a/src/pkg/math/hypot_amd64.s b/src/pkg/math/hypot_amd64.s
index 1f691e70ea..aeb95456bd 100644
--- a/src/pkg/math/hypot_amd64.s
+++ b/src/pkg/math/hypot_amd64.s
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.\n \n-#define PosInf 0x7ff0000000000000
-#define NaN 0x7FF0000000000001
+#define PosInf 0x7FF0000000000000
+#define NaN 0x7FF8000000000001
 \n // func Hypot(x, y float64) float64
 TEXT ·Hypot(SB),7,$0
 diff --git a/src/pkg/math/log_amd64.s b/src/pkg/math/log_amd64.s
index bf989bd2e8..75bc55764d 100644
--- a/src/pkg/math/log_amd64.s
+++ b/src/pkg/math/log_amd64.s
@@ -12,7 +12,7 @@
 #define L5     1.818357216161805012e-01   // 0x3FC7466496CB03DE
 #define L6     1.531383769920937332e-01   // 0x3FC39A09D078C69F
 #define L7     1.479819860511658591e-01   // 0x3FC2F112DF3E5244
-#define NaN    0x7FF0000000000001
+#define NaN    0x7FF8000000000001
 #define NegInf 0xFFF0000000000000
 #define PosInf 0x7FF0000000000000
 
diff --git a/src/pkg/math/sincos_amd64.s b/src/pkg/math/sincos_amd64.s
index 18c824e512..c9dea09164 100644
--- a/src/pkg/math/sincos_amd64.s
+++ b/src/pkg/math/sincos_amd64.s
@@ -19,7 +19,7 @@
 
 #define PosOne 0x3FF0000000000000
 #define PosInf 0x7FF0000000000000
-#define NaN    0x7FF0000000000001
+#define NaN    0x7FF8000000000001
 #define PI4A 0.7853981554508209228515625 // pi/4 split into three parts
 #define PI4B 0.794662735614792836713604629039764404296875e-8
 #define PI4C 0.306161699786838294306516483068750264552437361480769e-16

コアとなるコードの解説

このコミットで変更されたコードは、Go言語の math パッケージ内で使用されるアセンブリ言語ファイルです。これらのファイルは、特定の浮動小数点演算(例: DimHypotLogSinCos)のパフォーマンスを最適化するために、Goのランタイムによって直接呼び出される低レベルな実装を提供します。

変更のポイントは、各ファイル内で #define NaN によって定義されているNaNのビットパターンです。

  1. dim_amd64.s, hypot_amd64.s, log_amd64.s, sincos_amd64.s (AMD64アーキテクチャ向け): これらのファイルでは、64ビットのNaN定数が直接変更されています。

    • 変更前: #define NaN 0x7FF0000000000001
    • 変更後: #define NaN 0x7FF8000000000001 この変更は、前述の「技術的詳細」で説明した通り、Signaling NaNからQuiet NaNへのビットパターンの変更を直接反映しています。
  2. hypot_386.s (386アーキテクチャ向け): このファイルは32ビットアーキテクチャ向けであり、64ビットの浮動小数点数を2つの32ビットレジスタ(rhrl)に分割して扱います。

    • 変更前: MOVL $0x7ff00000, rh+20(FP) // return NaN = 0x7FF0000000000001
    • 変更後: MOVL $0x7ff80000, rh+20(FP) // return NaN = 0x7FF8000000000001 rh+20(FP) は、スタックフレームポインタ (FP) からのオフセットで、64ビット浮動小数点数の上位32ビットを格納するメモリ位置を示します。rl+16(FP) は下位32ビットを格納します。 元の 0x7FF0000000000001 の上位32ビットは 0x7FF00000 であり、下位32ビットは 0x00000001 です。 新しい 0x7FF8000000000001 の上位32ビットは 0x7FF80000 であり、下位32ビットは 0x00000001 です。 したがって、この変更は64ビットのNaN定義の変更を32ビットアーキテクチャのアセンブリコードに正しく反映させるものです。MOVL 命令は32ビット値をレジスタに移動させるため、64ビット値の上位部分のみが変更されています。

これらの変更により、Goの math パッケージが浮動小数点演算の結果としてNaNを返す際に、常にIEEE 754標準に準拠したQuiet NaNが生成されるようになります。これは、Goプログラムの浮動小数点演算の正確性と移植性を向上させる上で重要な修正です。

関連リンク

参考にした情報源リンク