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

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

このコミットは、Go言語のコンパイラとランタイムにおいて、符号なし整数(uint32およびuint64)から浮動小数点数(float32およびfloat64)へのビットレベルでの変換を可能にする新しい組み込み関数float32frombitsおよびfloat64frombitsを追加するものです。これにより、IEEE 754浮動小数点数のバイナリ表現を直接操作し、そのビットパターンから対応する浮動小数点数を得る機能が提供されます。

コミット

commit 2f4d35ffb9dfc84277a1c868d71d6f89bfd19f7f
Author: Rob Pike <r@golang.org>
Date:   Wed Nov 12 11:51:34 2008 -0800

    converting uint bits back into floats
    
    R=rsc
    DELTA=32  (32 added, 0 deleted, 0 changed)
    OCL=19084
    CL=19091

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

https://github.com/golang/go/commit/2f4d35ffb9dfc84277a1c868d71d6f89bfd19f7f

元コミット内容

converting uint bits back into floats

変更の背景

Go言語では、浮動小数点数とそれに対応するビット表現(uint32uint64)の間で変換を行うための関数が以前から存在していました。具体的には、float32bitsfloat64bitsは、浮動小数点数のビット表現を符号なし整数として取得するために使用されます。

このコミットの背景には、その逆の操作、すなわち符号なし整数として表現されたビットパターンを、対応する浮動小数点数として解釈する機能の必要性がありました。これは、低レベルの数値操作、特にIEEE 754浮動小数点標準に準拠したビット操作を必要とする場面で不可欠です。例えば、ネットワークプロトコルで浮動小数点数がビット列として送受信される場合や、特定のビットパターンを持つ浮動小数点数を生成してテストする場合などに、この機能が役立ちます。

この機能は、Go言語の標準ライブラリの一部として提供されることで、開発者が安全かつ移植性の高い方法でこれらの変換を行えるようにすることを目的としています。

前提知識の解説

IEEE 754 浮動小数点標準

IEEE 754は、浮動小数点数のコンピュータ上での表現方法を定めた国際標準です。この標準は、単精度(32ビット、float32)と倍精度(64ビット、float64)の浮動小数点数について、符号部、指数部、仮数部のビット割り当てを定義しています。

  • 単精度 (float32): 32ビットで構成され、1ビットの符号、8ビットの指数、23ビットの仮数からなります。
  • 倍精度 (float64): 64ビットで構成され、1ビットの符号、11ビットの指数、52ビットの仮数からなります。

これらのビットパターンを直接操作することで、特殊な浮動小数点値(例: 無限大 Inf、非数 NaN)を生成したり、浮動小数点演算の挙動を詳細に分析したりすることが可能になります。

型エイリアシングとunion

C言語やC++において、異なる型のポインタが同じメモリ領域を指すことを「型エイリアシング」と呼びます。厳密なエイリアシング規則(Strict Aliasing Rule)は、コンパイラが最適化を行う際に、異なる型のポインタを介したメモリアクセスが互いに影響しないと仮定することを許可します。これにより、コンパイラはより積極的な最適化を行うことができますが、開発者がこの規則に違反すると未定義動作を引き起こす可能性があります。

浮動小数点数のビット表現を整数として、またはその逆として扱う場合、直接的な型キャスト(例: *(float*)&i)は厳密なエイリアシング規則に違反する可能性があります。このような状況を安全に、かつ移植性高く扱うための一般的なイディオムがunion(共用体)の使用です。

unionは、複数のメンバーが同じメモリ領域を共有する特殊なデータ構造です。unionのメンバーに値を書き込み、別のメンバーとしてその値を読み出すことは、厳密なエイリアシング規則の例外として認められています。これにより、コンパイラは安全に型変換を行うことができ、未定義動作を回避できます。

技術的詳細

このコミットで追加されたfloat32frombitsfloat64frombits関数は、Go言語の組み込み関数として提供されます。これらの関数は、それぞれuint32uint64の入力ビットパターンを受け取り、対応するfloat32float64の浮動小数点数を返します。

実装の核心は、src/runtime/runtime.c内のC言語コードにあります。ここで、float32frombitsfloat64frombitsの内部実装が定義されています。特に注目すべきは、前述の「前提知識の解説」で触れたunionの使用です。

static float32
float32frombits(uint32 i)
{
	// The obvious cast-and-pointer code is technically
	// not valid, and gcc miscompiles it.  Use a union instead.
	union {
		float32 f;
		uint32 i;
	} u;
	u.i = i;
	return u.f;
}

このコードスニペットは、uint32型の入力ifloat32として解釈するためにunionを使用しています。u.i = i;uint32としてビットパターンをunionに格納し、return u.f;float32としてそのビットパターンを読み出しています。これにより、厳密なエイリアシング規則に違反することなく、ビットレベルでの型変換が安全に行われます。同様のロジックがfloat64frombitsにも適用されます。

これらの関数は、Go言語のコンパイラ(src/cmd/gc/sys.goおよびsrc/cmd/gc/sysimport.c)によって認識され、Goのコードから呼び出せるようにエクスポートされます。

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

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

  1. src/cmd/gc/sys.go:

    • export func float32frombits(uint32) float32;
    • export func float64frombits(uint64) float64; の2行が追加され、Goコンパイラがこれらの新しい組み込み関数を認識するように定義されています。
  2. src/cmd/gc/sysimport.c:

    • "export func sys.float32frombits (? uint32) (? float32)\\n"
    • "export func sys.float64frombits (? uint64) (? float64)\\n" の2行が追加され、Goコンパイラのシステムインポート定義にこれらの関数が追加されています。これにより、Goのソースコードからこれらの関数を呼び出すことが可能になります。
  3. src/runtime/runtime.c:

    • float32frombits(uint32 i)関数の実装が追加されました。この関数は、unionを使用してuint32のビットパターンをfloat32として解釈します。
    • float64frombits(uint64 i)関数の実装が追加されました。この関数は、unionを使用してuint64のビットパターンをfloat64として解釈します。
    • Goのシステムコールとしてこれらの関数を公開するためのラッパー関数sys·float32frombitssys·float64frombitsが追加されました。

コアとなるコードの解説

src/cmd/gc/sys.go および src/cmd/gc/sysimport.c

これらのファイルへの変更は、Goコンパイラが新しい組み込み関数float32frombitsfloat64frombitsを認識し、Goのプログラム内で使用できるようにするためのものです。sys.goはGo言語の組み込み関数の宣言を、sysimport.cはそれらのC言語側のインポート定義を扱います。これにより、Goのコードからmath.Float32frombitsmath.Float64frombitsのような形でこれらの関数を呼び出すことが可能になります(最終的にはmathパッケージに配置されることになります)。

src/runtime/runtime.c

このファイルはGoランタイムのC言語部分であり、低レベルの操作や組み込み関数の実装が含まれています。

static float32
float32frombits(uint32 i)
{
	// The obvious cast-and-pointer code is technically
	// not valid, and gcc miscompiles it.  Use a union instead.
	union {
		float32 f;
		uint32 i;
	} u;
	u.i = i;
	return u.f;
}

このfloat32frombits関数の実装は、uint32型の整数iを引数として受け取ります。内部では、float32uint32の2つのメンバーを持つunion uを定義しています。まず、入力されたuint32のビットパターンをu.iに代入します。これにより、そのビットパターンがunionの共有メモリ領域に書き込まれます。次に、u.fを返すことで、同じメモリ領域に格納されたビットパターンをfloat32として解釈し、その値を返します。

コメントにあるように、直接的なキャスト(例: *(float32*)&i)はC言語の厳密なエイリアシング規則に違反し、GCCのようなコンパイラが誤った最適化を行う可能性があるため、unionを使用しています。unionを使うことで、このビットレベルの型変換が安全かつ移植性高く行われることが保証されます。

float64frombitsも同様の原理で実装されており、uint64float64の間の変換を行います。

これらの関数は、GoのランタイムからGoのコードに公開されるsys·float32frombitsおよびsys·float64frombitsというラッパー関数を介して利用されます。

関連リンク

参考にした情報源リンク

  • コミット情報: ./commit_data/1110.txt
  • Go言語のソースコード(特にsrc/cmd/gc/src/runtime/ディレクトリ)
  • C言語のunionと厳密なエイリアシング規則に関する一般的な知識
  • IEEE 754浮動小数点標準に関する一般的な知識