web-dev-qa-db-ja.com

負のゼロが重要なのはなぜですか?

正のゼロと負のゼロの異なる表現を気にする理由に困惑しています。

複素数を含むプログラミングでは、負のゼロ表現を持つことが非常に重要であるという主張を読んだことを漠然と覚えています。私は複素数を含むコードを書く機会がなかったので、なぜこれが当てはまるのかについて少し困惑しています。

ウィキペディアの記事 コンセプトについては特に役に立ちません。私が正しく理解していれば、符号付きゼロについて曖昧な主張をするだけで、特定の数学演算を浮動小数点で単純化できます。 この回答 は、動作が異なるいくつかの関数をリストしています。おそらく、それらの使用方法に精通している場合は、例から推測される可能性があります。 (ただし、複雑な平方根の特定の例は平らに見えますwrong、2つの数値は数学的に同等なので、誤解がない限り。)しかし私がそこにいなかった場合に発生するであろうトラブルの種類についての明確な声明を見つけることができませんでした。私が見つけることができるより多くの数学的リソースは、数学的観点から2つを区別できないと述べており、Wikipediaの記事は、制限を説明することを除いて、これがコンピューティング以外ではほとんど見られないことを示唆しているようです。

では、負のゼロはなぜコンピューティングにとって価値があるのでしょうか?私はちょうど何かが欠けていると確信しています。

67
jpmc26

FPU演算では、0は必ずしも正確に0を意味する必要はありませんが、値が小さすぎて、特定のデータ型を使用して表現できないことに注意してください。

a = -1 / 1000000000000000000.0

aは小さすぎてfloat(32ビット)で正しく表現できないため、-0に「丸め」られます。

さて、計算が続くとしましょう:

b = 1 / a

Aは浮動小数点であるため、結果は-infinityになり、正解-1000000000000000000.0からはかなり遠くなります。

-0がない場合はbを計算してみましょう(aは+0に丸められます)。

b = 1 / +0
b = +infinity

丸めのために結果は再び間違っていますが、現在は「もっと間違っています」-数値的にだけでなく、さらに重要なことに、異なる符号のために(計算結果は+無限大で、正しい結果は-1000000000000000000.0です)。

両方が間違っているので、それは本当に問題ではないと言うこともできます。重要なことは、計算の最も重要な結果が符号である数値アプリケーションがたくさんあることです。機械学習アルゴリズムを使用して交差点で左折するか右折するかを決定するとき、正の値=>左折、負の値=>右折と解釈できます。値の実際の「大きさ」は、単に「信頼係数」です。

76
qbd

まず、どのように-0を作成しますか? 2つの方法があります。(1)数学的な結果が負であるが、ゼロに非常に近いため、ゼロ以外の数値ではなくゼロに丸められる浮動小数点演算を実行します。その計算は-0になります。 (b)ゼロを含む特定の演算:正のゼロに負の数を乗算するか、正のゼロを負の数で除算するか、正のゼロを否定します。

負のゼロがあると、乗算と除算が少し簡単になります。x* yまたはx/yの符号は、常にxの符号で、排他的またはyの符号です。負のゼロがない場合、-0を+0に置き換えるには、追加のチェックが必要になります。

それが役に立つ非常にまれな状況がいくつかあります。アンダーフローがある場合でも、乗算または除算の結果が数学的にゼロより大きいか小さいかを確認できます(結果が数学的なゼロでないことがわかっている場合)。それが違いを生む場所でコードを書いたことを覚えていない。

最適化コンパイラは-0を嫌います。たとえば、xが-0.0の場合、結果はxにならないため、x + 0.0をxに置き換えることはできません。 x <0.0またはxが-0.0の場合、結果は-0.0になるため、x * 0.0を0.0に置き換えることはできません。

8
gnasher729

IEEE 754に準拠するC#Double

_    double a = 3.0;
    double b = 0.0;
    double c = -0.0;

    Console.WriteLine(a / b);
    Console.WriteLine(a / c);
_

プリント:

_Infinity
-Infinity
_

実際に少し説明する...

_Double d = -0.0; 
_

これは、d = _The Limit of x as x approaches 0-_または_The Limit of x as x approaches 0 from the negatives_により近いものを意味します。


フィリップのコメントに対処するには...

基本的に負のゼロはアンダーフローを意味します。

負のゼロがあっても、実用的な使用はほとんどありません...

たとえば、次のコード(ここでもC#):

_double a = -0.0;
double b = 0.0;

Console.WriteLine(a.Equals(b));
Console.WriteLine(a==b);
Console.WriteLine(Math.Sign(a));
_

この結果が得られます:

_True
True
0
_

非公式に説明すると、IEEE 754浮動小数点が持つことができるすべての特別な値(正の無限大、負の無限大、NAN、-0.0)は、実用的な意味では意味がありません。それらは、物理的な値、または「現実の世界」の計算で意味のある値を表すことはできません。彼らが意味することは基本的にこれです:

  • 正の無限大は、浮動小数点が表すことができる正の端でのオーバーフローを意味します
  • 負の無限大は、浮動小数点が表すことができる正の端でのオーバーフローを意味します
  • 負のゼロはアンダーフローを意味し、オペランドは反対の符号を持っていました
  • 正のゼロmayはアンダーフローを意味し、オペランドは同じ符号を持っていました
  • NANは、計算がsqrt(-7)のように完全に未定義であること、または_0/0_のような制限がないか_PositiveInfinity/PositiveInfinity_のような制限がないことを意味します
6
AK_

これが複素数の計算にどのように関係するかという問題は、+ 0と-0の両方が浮動小数点で存在する理由の中心になっています。 Complex Analysisをまったく研究していると、出力が「リーマン面」と呼ばれるものを形成する「丁寧なフィクション」を採用しない限り、ComplexからComplexへの連続関数は通常「単一値」として処理できないことがすぐにわかります。たとえば、複素数対数は各入力を無限に多くの出力に割り当てます。それらを「接続」して連続出力を形成すると、すべてのリアルパーツがOriginの周りに「無限のコルク抜き」サーフェスを形成することになります。 「正の虚数側から下向きに」実軸と交差する連続曲線と「極を包み込み」、「負の虚数側から上向きに」実軸と交差する別の曲線は、実軸上で異なる値を取ります。それらはリーマン表面の異なる「シート」を通過します。

次に、複素数の浮動小数点を使用して計算する数値プログラムにそれを適用します。プログラムが現在「オン」になっている「シート」に応じて、特定の計算後に実行されるアクションは大きく異なる可能性があり、最後に計算された結果の符号からおそらく「シート」がわかります。結果がゼロだったとしましょうか?ここで、「ゼロ」は本当に「正しく表現するには小さすぎる」という意味です。しかし、結果がゼロのときに計算が-signを保持するように調整できる場合(つまり、どの「シート」を覚えているか)、コードはこの状況でも署名をチェックして正しいアクションを実行できます。

5
PJM

理由はいつもより簡単です

もちろんhacksはたくさんありますが、これは本当に見栄えがよく、便利です(-0.0または+0.0への丸めなど)。ただし、signed intが先頭のマイナス/プラス記号(通常、整数のU2バイナリコードで解決されますが、doubleのより複雑でない表現を想定しています):

0 111 = 7
^ sign

負の数がある場合はどうなりますか?

1 111 = -7

わかりました。したがって、0を表すことにしましょう。

0 000 = 0

それも結構です。しかし、1 000はどうですか?禁止番号でなければなりませんか?いいえ。

したがって、2つのタイプのゼロがあると仮定しましょう。

0 000 = +0
1 000 = -0

まあ、それは私たちの計算を単純化し、幸いにもいくつかの追加機能を切り上げます。したがって、+0-0は、バイナリ表現の問題のみから発生しています。

1
Dawid Pura