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

[インデックス 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言語の設計思想として、異なるコンパイラ(例: gcgccgo)間での挙動の一貫性は非常に重要です。特定のコンパイラでのみ発生するパニックは、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 を生成し、そのビットパターンが期待通りであることを検証します。

問題の根源は、gccgox := -zerox = -float64(zero) のような式をコンパイルする際に、zero0.0 であるにもかかわらず、結果として得られる x の値が正しく -0.0 として表現されなかった、あるいはその過程でコンパイラ内部でエラーが発生したことにあります。

IEEE 754標準では、0.0 の符号を反転させると -0.0 になります。このテストは、この基本的な浮動小数点演算の規則がコンパイラによって正しく実装されていることを確認します。特に、math.Float64bits を使用して、生成された x のビットパターンが 0x0000000000000000 (正のゼロ) ではなく、0x8000000000000000 (負のゼロ) であることを確認しています。

また、テストでは v := xmath.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 パッケージをインポートしています。

  1. const zero = 0.0: zero という名前の型なし浮動小数点定数を 0.0 で定義しています。Goでは、型なし定数はその使用方法に応じて型が推論されます。

  2. x := -zero: zero (型なし 0.0) の符号を反転させて x に代入します。この xfloat64 型に推論されます。期待される結果は -0.0 です。

  3. b := math.Float64bits(x): x のIEEE 754バイナリ表現を uint64 型の b に格納します。

  4. if b != 0 { panic(b) }: ここで重要なのは、x := -zero の結果が -0.0 であるにもかかわらず、このテストでは b != 0 をチェックしている点です。これは、zero が型なし定数であるため、-zero もまた型なし定数として扱われ、その値は数学的に 0 と等価であるとGoのコンパイラが判断するからです。したがって、math.Float64bits(-0.0) の結果は 0x8000000000000000 となるはずですが、型なし定数の文脈では 0 と等価であるため、0 と比較しています。この部分の意図は、gccgo0.0 の符号反転でパニックを起こさないこと、そしてその結果が 0 と等価であると見なされることを確認することです。

  5. x = -float64(zero): 今度は zero を明示的に float64 型にキャストしてから符号を反転させ、x に再代入します。これにより、x は明確に float64 型の -0.0 となります。

  6. b = math.Float64bits(x): 再度 x のビット表現を取得します。

  7. if b != 0 { panic(b) }: ここでも同様に b != 0 をチェックしています。これは、float64(zero)0.0 であり、その符号を反転させた -float64(zero) もまた数学的には 0 と等価であるため、コンパイラがこの定数式を 0 として扱うことを期待しているためです。

  8. v := x: x の値を v という新しい変数に代入します。この時点での xfloat64 型の -0.0 です。

  9. b = math.Float64bits(-v): v の符号を反転させ、そのビット表現を b に格納します。v-0.0 なので、-v+0.0 になります。したがって、b+0.0 のビット表現である 0x0000000000000000 になるはずです。

  10. 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 は、b0x8000000000000000 (負のゼロ) でない場合にパニックを起こす、という意味になります。これは、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 := xb = math.Float64bits(-v) の部分では、x-0.0 であることを前提としています。そして、-v+0.0 になるはずです。もし b != 0x8000000000000000 でパニックするということは、b0x8000000000000000 であることを期待していることになります。これは、+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 文の条件は、このコミットの文脈だけでは完全に理解しにくい部分です。しかし、全体としては、浮動小数点数のゼロと負のゼロの厳密な扱いをテストしていることは明らかです。

関連リンク

参考にした情報源リンク

  • IEEE 754浮動小数点標準に関する一般的な情報源 (例: Wikipedia)
  • Go言語の math パッケージのドキュメント
  • gccgo コンパイラに関する情報源 (例: GCCのドキュメント)
  • Go言語のIssueトラッカー (ただし、Issue #2876は公開されているGoリポジトリでは直接見つかりませんでした。これは内部的なトラッキング番号であるか、古いIssueである可能性があります。)