[インデックス 12939] ファイルの概要
コミット
commit f14a5347e69b4bb95e4f1d62d0967b14734193f3
Author: Ian Lance Taylor <iant@golang.org>
Date: Mon Apr 23 15:47:34 2012 -0700
test: test handling of negative float constants
This used to panic when compiled by gccgo.
Updates #2876.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6100055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f14a5347e69b4bb95e4f1d62d0967b14734193f3
元コミット内容
このコミットは、Go言語のテストスイートに新しいテストファイル test/fixedbugs/bug434.go を追加するものです。このテストの目的は、負の浮動小数点定数(特に負のゼロ)の取り扱いが正しく行われることを検証することです。
元々の問題は、gccgo コンパイラでコンパイルした際に、負の浮動小数点定数を扱うとパニック(プログラムの異常終了)が発生していた点にあります。このコミットは、その問題を修正し、関連するIssue #2876を更新するものです。
変更の背景
Go言語のコンパイラの一つである gccgo は、GoのコードをGCCのバックエンドを利用してコンパイルします。浮動小数点数の扱いは、特にゼロの符号(正のゼロ +0.0 と負のゼロ -0.0)に関して、IEEE 754浮動小数点標準で厳密に定義されています。
このコミットが修正しようとしている問題は、gccgo が負の浮動小数点定数を処理する際に、内部的なエラーや不整合を引き起こし、結果としてプログラムがパニックを起こしていたというものです。これは、コンパイラが浮動小数点数の内部表現、特に符号ビットの扱いに誤りがあった可能性を示唆しています。
Go言語の設計思想として、異なるコンパイラ(例: gc と gccgo)間での挙動の一貫性は非常に重要です。特定のコンパイラでのみ発生するパニックは、Goプログラムの移植性と信頼性を損なうため、このようなバグは早期に修正される必要があります。このテストの追加は、将来的に同様の回帰バグが発生しないようにするための予防策でもあります。
前提知識の解説
浮動小数点数とIEEE 754標準
コンピュータにおける浮動小数点数は、実数を近似的に表現するための形式です。ほとんどのシステムでは、IEEE 754標準に従って実装されています。この標準は、浮動小数点数の表現形式(単精度、倍精度など)、演算規則、そして特殊な値(無限大、非数 NaN、そして正のゼロ +0.0 と負のゼロ -0.0)を定義しています。
- 正のゼロ (+0.0): すべてのビットが0。
- 負のゼロ (-0.0): 符号ビットが1で、残りのビットが0。
数学的には +0.0 と -0.0 は等しいとされますが、浮動小数点演算においては異なる挙動を示すことがあります(例: 1.0 / -0.0 は負の無限大になる)。コンパイラがこれらのゼロを正しく区別し、定数として適切に扱うことは、正確な浮動小数点演算を保証するために不可欠です。
math.Float64bits 関数
Go言語の math パッケージには、Float64bits(f float64) uint64 という関数があります。この関数は、float64 型の浮動小数点数 f のIEEE 754バイナリ表現を uint64 型の整数として返します。これにより、浮動小数点数の内部ビットパターンを直接検査することが可能になります。
+0.0のビット表現は0x0000000000000000(すべてのビットが0) です。-0.0のビット表現は0x8000000000000000(最上位ビット(符号ビット)が1、残りが0) です。
この関数は、浮動小数点数の正確な比較や、特定のビットパターンを期待するテストケースで非常に有用です。
Go言語における panic
Go言語における panic は、プログラムの実行中に回復不可能なエラーが発生した際に、現在のゴルーチン(軽量スレッド)の実行を停止し、スタックを巻き戻す(unwind)メカニズムです。通常、panic はプログラマの予期しない状態や、プログラムの続行が不可能になった場合に発生します。コンパイラのバグによって panic が引き起こされることは、そのコンパイラが生成するコードの信頼性に直接影響します。
gccgo コンパイラ
gccgo は、Go言語のフロントエンドとGCCのバックエンドを組み合わせたコンパイラです。GoのソースコードをGCCの中間表現に変換し、その後GCCの最適化とコード生成のパイプラインを利用して実行可能ファイルを生成します。これにより、GCCがサポートする多様なアーキテクチャや最適化の恩恵を受けることができます。しかし、Go言語のセマンティクスをGCCのバックエンドに正確にマッピングする過程で、今回のようなバグが発生することがあります。
技術的詳細
このコミットは、gccgo が負の浮動小数点定数を扱う際に発生していたパニックを修正するためのテストを追加しています。具体的には、0.0 という定数から負のゼロ -0.0 を生成し、そのビットパターンが期待通りであることを検証します。
問題の根源は、gccgo が x := -zero や x = -float64(zero) のような式をコンパイルする際に、zero が 0.0 であるにもかかわらず、結果として得られる x の値が正しく -0.0 として表現されなかった、あるいはその過程でコンパイラ内部でエラーが発生したことにあります。
IEEE 754標準では、0.0 の符号を反転させると -0.0 になります。このテストは、この基本的な浮動小数点演算の規則がコンパイラによって正しく実装されていることを確認します。特に、math.Float64bits を使用して、生成された x のビットパターンが 0x0000000000000000 (正のゼロ) ではなく、0x8000000000000000 (負のゼロ) であることを確認しています。
また、テストでは v := x と math.Float64bits(-v) のように、一度変数に代入した後に再度符号を反転させる操作も検証しています。これは、コンパイラが定数だけでなく、変数に格納された浮動小数点数に対しても符号反転操作を正しく適用できることを確認するためです。
このテストが追加されることで、gccgo の浮動小数点数処理における潜在的なバグが特定され、修正されたことが保証されます。
コアとなるコードの変更箇所
追加されたファイル: test/fixedbugs/bug434.go
// run
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Test that typed and untyped negative zero floating point constants
// are treated as equivalent to zero constants.
package main
import "math"
const zero = 0.0
func main() {
x := -zero
b := math.Float64bits(x)
if b != 0 {
panic(b)
}
x = -float64(zero)
b = math.Float64bits(x)
if b != 0 {
panic(b)
}
v := x
b = math.Float64bits(-v)
if b != 0x8000000000000000 {
panic(b)
}
}
コアとなるコードの解説
このテストファイル bug434.go は、main パッケージに属し、math パッケージをインポートしています。
-
const zero = 0.0:zeroという名前の型なし浮動小数点定数を0.0で定義しています。Goでは、型なし定数はその使用方法に応じて型が推論されます。 -
x := -zero:zero(型なし0.0) の符号を反転させてxに代入します。このxはfloat64型に推論されます。期待される結果は-0.0です。 -
b := math.Float64bits(x):xのIEEE 754バイナリ表現をuint64型のbに格納します。 -
if b != 0 { panic(b) }: ここで重要なのは、x := -zeroの結果が-0.0であるにもかかわらず、このテストではb != 0をチェックしている点です。これは、zeroが型なし定数であるため、-zeroもまた型なし定数として扱われ、その値は数学的に0と等価であるとGoのコンパイラが判断するからです。したがって、math.Float64bits(-0.0)の結果は0x8000000000000000となるはずですが、型なし定数の文脈では0と等価であるため、0と比較しています。この部分の意図は、gccgoが0.0の符号反転でパニックを起こさないこと、そしてその結果が0と等価であると見なされることを確認することです。 -
x = -float64(zero): 今度はzeroを明示的にfloat64型にキャストしてから符号を反転させ、xに再代入します。これにより、xは明確にfloat64型の-0.0となります。 -
b = math.Float64bits(x): 再度xのビット表現を取得します。 -
if b != 0 { panic(b) }: ここでも同様にb != 0をチェックしています。これは、float64(zero)が0.0であり、その符号を反転させた-float64(zero)もまた数学的には0と等価であるため、コンパイラがこの定数式を0として扱うことを期待しているためです。 -
v := x:xの値をvという新しい変数に代入します。この時点でのxはfloat64型の-0.0です。 -
b = math.Float64bits(-v):vの符号を反転させ、そのビット表現をbに格納します。vが-0.0なので、-vは+0.0になります。したがって、bは+0.0のビット表現である0x0000000000000000になるはずです。 -
if b != 0x8000000000000000 { panic(b) }: この行は、上記の解説と矛盾しているように見えます。vが-0.0であれば、-vは+0.0になり、そのビット表現は0x0000000000000000であるべきです。しかし、テストコードでは0x8000000000000000(負のゼロのビット表現) と比較しています。この部分の解釈について補足と訂正: 元のコミットメッセージの意図と、Go言語における浮動小数点定数の扱いを考慮すると、このテストの最後の
if文は、xが-0.0であることを確認し、その-0.0をさらに符号反転した結果が+0.0になることを確認する意図であると考えられます。しかし、コードの
if b != 0x8000000000000000は、bが0x8000000000000000(負のゼロ) でない場合にパニックを起こす、という意味になります。これは、vが-0.0であり、-vが+0.0であるという前提と矛盾します。考えられる意図の再解釈: このテストは、
typed and untyped negative zero floating point constants are treated as equivalent to zero constantsというコメントがあります。最初の2つのpanicは、0.0の符号反転がパニックを起こさず、かつその結果が「数学的なゼロ」として扱われることを確認しています。最後の
v := xとb = math.Float64bits(-v)の部分では、xが-0.0であることを前提としています。そして、-vは+0.0になるはずです。もしb != 0x8000000000000000でパニックするということは、bが0x8000000000000000であることを期待していることになります。これは、+0.0のビット表現である0x0000000000000000とは異なります。結論として、この最後の
if文の条件は、テストの意図と期待される浮動小数点数の挙動からすると誤っているか、あるいは非常に特殊なケースをテストしている可能性があります。 通常、-(-0.0)は+0.0になるため、math.Float64bits(+0.0)は0x0000000000000000を返すはずです。もし0x8000000000000000を期待しているのであれば、それはvが+0.0であり、-vが-0.0になることを期待していることになります。しかし、xは-zeroまたは-float64(zero)から来ているため、xは-0.0であるべきです。このテストの主要な目的は、
gccgoが負の浮動小数点定数でパニックを起こさないこと、そしてゼロの符号反転が正しく行われることを確認することにあります。最後のif文の条件は、このコミットの文脈だけでは完全に理解しにくい部分です。しかし、全体としては、浮動小数点数のゼロと負のゼロの厳密な扱いをテストしていることは明らかです。
関連リンク
- Go言語の変更リスト: https://golang.org/cl/6100055
参考にした情報源リンク
- IEEE 754浮動小数点標準に関する一般的な情報源 (例: Wikipedia)
- Go言語の
mathパッケージのドキュメント gccgoコンパイラに関する情報源 (例: GCCのドキュメント)- Go言語のIssueトラッカー (ただし、Issue #2876は公開されているGoリポジトリでは直接見つかりませんでした。これは内部的なトラッキング番号であるか、古いIssueである可能性があります。)