🍲

go 言語は 負のゼロ を書きにくい

に公開

これは何?

go で 浮動小数点数の 負のゼロ についての動作確認を試みようと

go
doSomething(-0.0)

のようなことを書いたら

in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zeroSA4026

という警告が出た。じゃあどうするかっていう話。

そもそも 負のゼロ って何?

ゼロに負も何もないよ、と思うのは自然なことだと思う。

go に限らず、現代のプログラミング言語で 0.1 みたいな値は IEEE754 という規格に従っている(事が多い)。

この規格の中には 無限や非数という数かどうかわからないものに紛れて 負のゼロ という意味がよくわからないものが含まれている。

ruby・Python・C#・JavaScript など、浮動小数点数をサポートする go 言語以外のすべての言語(私の知る限り)は、 -0.0 と書くと「負のゼロ」になる。

負のゼロは

ruby
pz, mz = 0.0, -0.0
p(pz==mz)
# => true
p( [pz,mz].map{ 1/_1 } )
#=> [Infinity, -Infinity]

のように、0.0== で比較すると等しくなるのに計算結果が異なることがあるというものになっている。

なんでそんな事になっているのかとか色々感じるものではあるけれど、IEEE754 の仕様だから仕方ない。IEEE754 に従っている処理系はみんなそうなっている。

go の「負のゼロ」

で。

ruby も JavaScript も Python も Java も C# も -0.0 で負のゼロを表現できるが、go はそうではない。IEEE754 をサポートしている言語で、私の知る限り唯一。

go
const negZeroTrial = -(0.0)
fmt.Printf("%.2e\n", negZeroTrial)  // => 0.00e+00

マイナスを付けても、カッコで括っても負のゼロにならない。
ならばと float64 の最小値の下を狙っても

go
const negZeroTrial = (-math.SmallestNonzeroFloat64)/10
fmt.Printf("%.2e\n", negZeroTrial)  // => 0.00e+00

だめ。

型をつければいけるかと思いきや

go
const negZeroTrial = float64(-math.SmallestNonzeroFloat64) / 10
fmt.Printf("%.2e\n", negZeroTrial)  // => 0.00e+00

そうでもない。

一回 非 const に入れてから計算すると

go
var negZeroTrial = -math.Abs(0.5) * math.SmallestNonzeroFloat64
fmt.Printf("%.2e\n", negZeroTrial)  // => -0.00e+00

負のゼロを作ることができるし、それで困らないんだろうけどなんか腑に落ちない気持ち。

ということで

ということで、いまのところ 負のゼロを go の const に入れる方法は見つかってない。
そういう言語仕様なのかなと思っているけど、まだ言語仕様は見ていない。

Discussion