IEEE 754における負のゼロ

IEEE 754 > IEEE 754における負のゼロ 現在多くのコンピュータやプログラミング言語が採用している浮動小数点数の標準であるIEEE 754には通常の 0（以下 +0 と書く)の他に負のゼロである −0がある。本項では、IEEE 754における負のゼロと通常の 0との取り扱いについて述べる。 IEEE 754 の仕様策定の際、符号付きのゼロを採用するといくつかのクリティカルな問題で数値的な正確さ（accuracy）の達成が（精度（precision）ではない）容易になると主張され、特に複素数の初等関数の計算が挙げられた。 正のゼロと負のゼロは算術比較演算では等しいと判定されるが、一部演算では異なる結果を生じる（「ビットパターンが異なるため」ではない。#複素数などの節を参照のこと）。 IEEE 754 の二進浮動小数点数では、ゼロは指数部と仮数部がゼロで表され、負のゼロの場合はさらに符号ビットが 1 となる。計算結果が負の極めて小さい値で算術アンダーフローとなった場合、負のゼロが結果として得られる。また、−1.0 * 0.0 の計算結果も負のゼロとなる。そのプログラミング言語のリテラルが対応していれば、単に −0.0 と記述しても負のゼロになる。 IEEE 754 の十進浮動小数点数では、負のゼロの指数部は任意の正規の値で、仮数部は全てゼロであり、符号ビットが 1 で表される。 IEEE 754 では、正のゼロと負のゼロを各種演算で使用したときの振る舞いを規定している。計算結果は丸めモードの設定に影響される。 標準において、その計算は正と負の無限大を含む拡大実数を対象としており、1/−0 = −∞ および 1/+0 = +∞ となるような2つのゼロが存在する。すなわちこの場合に限っては0をある種の無限小のように扱っている。標準では一般に任意の非ゼロ数のゼロ除算は、正負どちらかの無限大になり、ゼロのゼロ除算は NaN になる。 それ以外の乗除算は通常の符号の組み合わせと同じように扱われる。 − 0 | x | = − 0 {\displaystyle {\frac {-0}{\left|x\right|}}=-0\,\!} （ x {\displaystyle x} は0以外） ( − 0 ) ⋅ ( − 0 ) = + 0 {\displaystyle (-0)\cdot (-0)=+0\,\!} | x | ⋅ ( − 0 ) = − 0 {\displaystyle \left|x\right|\cdot (-0)=-0\,\!} 加減算は値が相殺される場合特別に扱われる。 x + ( ± 0 ) = x {\displaystyle x+(\pm 0)=x\,\!} ( − 0 ) + ( − 0 ) = ( − 0 ) − ( + 0 ) = − 0 {\displaystyle (-0)+(-0)=(-0)-(+0)=-0\,\!} ( + 0 ) + ( + 0 ) = ( + 0 ) − ( − 0 ) = + 0 {\displaystyle (+0)+(+0)=(+0)-(-0)=+0\,\!} x − x = x + ( − x ) = + 0 {\displaystyle x-x=x+(-x)=+0\,\!} （任意の有限の x {\displaystyle x} について、負方向への丸めの場合は −0） 負のゼロが存在するため、浮動小数点数の変数 x、y、z を使った式 z = -(x - y) や z = (-x) - (-y) を z = y - x と最適化することはできない。 他に次のような特別規則がある。 − 0 = − 0 {\displaystyle {\sqrt {-0}}=-0\,\!} − 0 − ∞ = + 0 {\displaystyle {\frac {-0}{-\infty }}=+0\,\!} （除算の符号規則に従う） | x | − 0 = − ∞ {\displaystyle {\frac {\left|x\right|}{-0}}=-\infty \,\!} （ x {\displaystyle x} がゼロでない場合、除算の符号規則に従う） ± 0 × ± ∞ = NaN {\displaystyle {\pm 0}\times {\pm \infty }={\mbox{NaN}}\,\!} （NaNまたは割り込み発生） ± 0 ± 0 = NaN {\displaystyle {\frac {\pm 0}{\pm 0}}={\mbox{NaN}}\,\!} 一般に複素数などの極座標表示においては、その偏角に 2 n π {\displaystyle 2n\pi } を加減しても複素数としては同じ値を示す、という性質がある。通常は代表値として、偏角を θ {\displaystyle \theta } とすると − π < θ ≤ + π {\displaystyle -\pi <\theta \leq +\pi } に制限するなどするが、マイナスゼロがある場合、 ( − 1.0 , + 0.0 i ) {\displaystyle (-1.0,+0.0i)} の偏角を + π {\displaystyle +\pi } とするのに対し ( − 1.0 , − 0.0 i ) {\displaystyle (-1.0,-0.0i)} の偏角を − π {\displaystyle -\pi } とする、といったように使い分ける用例がある。複素数を使わない場合でも、たとえば atan2(-0.0, -1.0) が − π {\displaystyle -\pi } になる実装がある。 標準では、C言語やJavaの == 演算子のような通常の（数値としての）比較では、負のゼロと正のゼロは等しいと判定されることとしている。 特に負のゼロのみの判定が可能な API としては、IEEE 754 の中では、copysign() 関数で、ゼロの符号をゼロでない何らかの数にコピーすることで正負を明確化することができる。C言語ではC99で標準となった。Java では、Double クラスでの equals メソッドは負のゼロと正のゼロを区別する。例えば、 Double negativeZero = new Double(-0.0); negativeZero.equals(-0.0); // Result: true negativeZero.equals( 0.0); // Result: false 以下は、後で紹介するものほどトリック的な方法となる。まず、(int)var のように普通に型キャストすると、負のゼロのない2の補数表現の整数では単なるゼロになってしまうので比較できない。 任意の非ゼロの値を除算して、正のゼロと負のゼロを区別できる。 1.0 / +0.0 = +∞ 1.0 / −0.0 = −∞ 型のパンニング（英語版）により、整数型としてアクセスし、ビットパターンとして比較する。C言語では、可搬（portable）な技法ではないが（標準ではそのような操作の結果は「未定義」である。strict aliasing rule）、var が IEEE 754の単精度である場合、 *(uint32_t *)&var == 0x80000000UL で、負のゼロかどうか比較できる。 共用体を利用すれば、このようなアクセスが標準では「処理系定義」であるので、可搬性（portability）が少しはマシである。