Sind +0 und -0 gleich?

171

Lesen Sie die ECMAScript 5.1-Spezifikation durch +0und -0unterscheiden Sie sich.

Warum +0 === -0bewertet dann true?

Zufälliges Blau
quelle
mögliches Duplikat der Unterscheidung von +0 und -0
GolezTrol
6
Beachten Sie, dass Sie in ES2015 Object.is+0 und -0 unterscheiden können
Benjamin Gruenbaum
David Flanagan aus JS zitiert den endgültigen Leitfaden : Unterlauf tritt auf, wenn das Ergebnis einer numerischen Operation näher an Null liegt als die kleinste darstellbare Zahl. In diesem Fall gibt JavaScript 0 zurück. Wenn ein Unterlauf aufgrund einer negativen Zahl auftritt, gibt JavaScript einen speziellen Wert zurück, der als "negative Null" bezeichnet wird.
RBT

Antworten:

193

JavaScript verwendet den IEEE 754-Standard zur Darstellung von Zahlen. Aus Wikipedia :

Vorzeichen Null ist Null mit einem zugehörigen Vorzeichen. In der gewöhnlichen Arithmetik ist –0 = +0 = 0. Bei der Berechnung erlauben jedoch einige Zahlendarstellungen die Existenz von zwei Nullen, die häufig mit –0 (negative Null) und +0 (positive Null) bezeichnet werden . Dies tritt in einigen vorzeichenbehafteten Zahlendarstellungen für Ganzzahlen und in den meisten Gleitkommazahlendarstellungen auf. Die Zahl 0 wird normalerweise als +0 codiert, kann aber entweder durch +0 oder –0 dargestellt werden.

Der IEEE 754-Standard für Gleitkomma-Arithmetik (derzeit von den meisten Computern und Programmiersprachen verwendet, die Gleitkommazahlen unterstützen) erfordert sowohl +0 als auch -0. Die Nullen können als eine Variante der erweiterten reellen Zahlenlinie betrachtet werden, so dass 1 / −0 = −∞ und 1 / + 0 = + ∞, Division durch Null nur für ± 0 / ± 0 und ± ∞ / ± ∞ undefiniert ist .

Der Artikel enthält weitere Informationen zu den verschiedenen Darstellungen.

Dies ist der Grund, warum technisch gesehen beide Nullen unterschieden werden müssen.

Allerdings +0 === -0true ausgewertet. Warum ist das so (...) ?

Dieses Verhalten wird in Abschnitt 11.9.6 , dem Algorithmus für den Vergleich strikter Gleichheit (Betonung teilweise von mir), explizit definiert :

Der Vergleich x === y, wobei xund yWerte sind, ergibt wahr oder falsch . Ein solcher Vergleich wird wie folgt durchgeführt:

(...)

  • Wenn Typ (x) Nummer ist, dann

    1. Wenn x NaN ist, geben Sie false zurück.
    2. Wenn y NaN ist, geben Sie false zurück.
    3. Wenn x der gleiche Zahlenwert wie y ist, geben Sie true zurück.
    4. Wenn x +0 und y −0 ist, geben Sie true zurück.
    5. Wenn x –0 und y +0 ist, geben Sie true zurück.
    6. Falsch zurückgeben.

(...)

(Gleiches gilt +0 == -0übrigens.)

Es scheint logisch +0und -0gleich zu behandeln . Andernfalls müssten wir dies in unserem Code berücksichtigen und ich persönlich möchte das nicht tun;)


Hinweis:

ES2015 führt eine neue Vergleichsmethode ein Object.is. Object.isunterscheidet explizit zwischen -0und +0:

Object.is(-0, +0); // false
Felix Kling
quelle
15
In der Tat 1/0 === Infinity; // trueund 1/-0 === -Infinity; // true.
user113716
48
Also haben wir 1 === 1und +0 === -0aber 1/+0 !== 1/-0. Wie seltsam!
Randomblue
8
@Random: Ich denke es ist sicherlich besser als +0 !== -0;) Das könnte wirklich Probleme verursachen.
Felix Kling
@FelixKling oder 0 !== +0/ 0 !== -0, was in der Tat auch Probleme verursachen würde!
Yanick Rochon
5
Tatsächlich begrenzen diese Verhaltensmodelle die Berechnung in der Mathematik. Zum Beispiel hat die Funktion 1 / x einen Wert unendlich in 0, sie wird jedoch getrennt, wenn wir uns 0 von der positiven von der negativen Seite nähern; im ersteren ist das Ergebnis + inf, im letzteren -inf.
Agoston Horvath
19

Ich werde dies als Antwort hinzufügen, da ich den Kommentar von @ user113716 übersehen habe.

Sie können dies auf -0 testen:

function isMinusZero(value) {
  return 1/value === -Infinity;
}

isMinusZero(0); // false
isMinusZero(-0); // true
Laktak
quelle
6
Sollte wahrscheinlich auch nach == 0 gesucht werden, gibt das obige isMinusZero (-1e-323) true zurück!
Chris
1
@Chris, die Grenze des Exponenten mit doppelter Genauigkeit ist e±308, dass Ihre Nummer nur in denormalisierter Form dargestellt werden kann und verschiedene Implementierungen unterschiedliche Meinungen darüber haben, wo sie überhaupt unterstützt werden sollen oder nicht. Der Punkt ist, dass auf einigen Maschinen in einigen Gleitkomma-Modi Ihre Nummer als -0und auf anderen als denormalisierte Nummer dargestellt wird 0.000000000000001e-308. Solche Schwimmer, so lustig
Schwachstellen
Dies kann auch für andere Sprachen funktionieren (ich habe auf C getestet und dies funktioniert)
Mukul Kumar
11

Ich bin gerade auf ein Beispiel gestoßen, bei dem sich +0 und -0 tatsächlich sehr unterschiedlich verhalten:

Math.atan2(0, 0);  //returns 0
Math.atan2(0, -0); //returns Pi

Seien Sie vorsichtig: Selbst wenn Sie Math.round für eine negative Zahl wie -0.0001 verwenden, ist es tatsächlich -0 und kann einige nachfolgende Berechnungen wie oben gezeigt vermasseln.

Eine schnelle und schmutzige Möglichkeit, dies zu beheben, besteht darin, Folgendes zu tun:

if (x==0) x=0;

oder nur:

x+=0;

Dies wandelt die Zahl in +0 um, falls sie -0 war.

Foxcode
quelle
Vielen Dank. So seltsam, wie das Hinzufügen von Null das Problem beheben würde, auf das ich gestoßen bin. "Wenn alles andere fehlschlägt, addieren Sie Null." Eine Lektion fürs Leben.
Microsis
Ich bin gerade auf Math.atan (y / x) gestoßen, das (vielleicht überraschend) positiv oder negativ unendlich "y / x" verarbeiten kann, außer es gibt die falsche Antwort für den Fall, dass x -0 ist. Das Ersetzen von "x" durch "(x + 0)" behebt das Problem.
Jacob C. sagt Reinstate Monica
5

In dem IEEE 754-Standard, der zur Darstellung des Zahlentyps in JavaScript verwendet wird, wird das Vorzeichen durch ein Bit dargestellt (eine 1 gibt eine negative Zahl an).

Infolgedessen gibt es sowohl einen negativen als auch einen positiven Wert für jede darstellbare Zahl, einschließlich 0.

Deshalb existieren beide -0und +0.

Arnaud Le Blanc
quelle
3
Das Zweierkomplement verwendet ebenfalls ein Bit für das Vorzeichen, hat jedoch nur eine Null (positiv).
Felix Kling
1
Ja, aber im Zweierkomplement ist das negative Bit ebenfalls Teil des Werts. Sobald Sie das negative Bit gesetzt haben, ist es nicht mehr Null.
Arnaud Le Blanc
3

Beantwortung des Originaltitels Are +0 and -0 the same?:

brainslugs83(in Kommentaren der Antwort von Spudley) wies auf einen wichtigen Fall hin, in dem +0 und -0 in JS nicht gleich sind - implementiert als Funktion:

var sign = function(x) {
    return 1 / x === 1 / Math.abs(x);
}

Dies gibt anders als der Standard Math.signdas korrekte Vorzeichen von +0 und -0 zurück.

BM
quelle
2

Es gibt zwei mögliche Werte (Bitdarstellungen) für 0. Dies ist nicht eindeutig. Insbesondere bei Gleitkommazahlen kann dies auftreten. Das liegt daran, dass Gleitkommazahlen tatsächlich als eine Art Formel gespeichert werden.

Ganzzahlen können auch auf separate Weise gespeichert werden. Sie können einen numerischen Wert mit einem zusätzlichen Vorzeichenbit haben, sodass Sie in einem 16-Bit-Bereich einen 15-Bit-Ganzzahlwert und ein Vorzeichenbit speichern können. In dieser Darstellung sind der Wert 1000 (hex) und 0000 beide 0, aber einer von ihnen ist +0 und der andere ist -0.

Dies könnte vermieden werden, indem 1 vom ganzzahligen Wert subtrahiert wird, so dass er im Bereich von -1 bis -2 ^ 16 liegt. Dies wäre jedoch unpraktisch.

Ein üblicherer Ansatz besteht darin, Ganzzahlen in "zwei Ergänzungen" zu speichern, aber anscheinend hat ECMAscript entschieden, dies nicht zu tun. Bei dieser Methode reichen die Zahlen von 0000 bis 7FFF positiv. Negative Zahlen beginnen bei FFFF (-1) bis 8000.

Natürlich gelten die gleichen Regeln auch für größere Ganzzahlen, aber ich möchte nicht, dass sich mein F abnutzt. ;)

GolezTrol
quelle
4
Aber findest du das nicht +0 === -0ein bisschen komisch? Denn jetzt haben wir 1 === 1und +0 === -0aber 1/+0 !== 1/-0...
Randomblue
2
Natürlich ist +0 -0. Es ist beides nichts. Aber es gibt einen großen Unterschied zwischen + unendlich und -unendlich, oder? Diese Unendlichkeitszahlen können sogar der Grund sein, warum ECMA sowohl +0 als auch -1 unterstützt.
GolezTrol
Sie erklären nicht, warum, +0 === -0obwohl die zwei Bit-Darstellungen unterschiedlich sind.
Randomblue
1
+0 ist -0 ist 0, nichts, nada, niente. Es macht Sinn, dass sie gleich sind. Warum ist der Himmel blau? 4 + 3 ist auch dasselbe wie 1 + 6, obwohl die Darstellungen unterschiedlich sind. Sie haben unterschiedliche Darstellungen (und damit einen unterschiedlichen Bitwert), aber im Vergleich werden sie als dieselbe Null behandelt, die sie sind.
GolezTrol
1
Sie sind nicht gleich. Beispiele hierfür finden Sie unter stackoverflow.com/questions/7223717/differentiating-0-and-0 .
Randomblue
2

Wir können Object.is+0 und -0 und noch etwas unterscheiden NaN==NaN.

Object.is(+0,-0) //false

Object.is(NaN,NaN) //true
terryc
quelle
1

Ich würde es der Methode des strengen Gleichheitsvergleichs ('===') zuschreiben. Schauen Sie sich Abschnitt 4d an Geben Sie hier die Bildbeschreibung ein

siehe 7.2.13 Strenger Gleichheitsvergleich zur Spezifikation

Bar Horing
quelle
0

Wikipedia hat einen guten Artikel, um dieses Phänomen zu erklären: http://en.wikipedia.org/wiki/Signed_zero

Kurz gesagt, es sind sowohl +0 als auch -0 in den IEEE-Gleitkomma-Spezifikationen definiert. Beide unterscheiden sich technisch von 0 ohne Vorzeichen, was eine ganze Zahl ist. In der Praxis werden sie jedoch alle mit Null bewertet, sodass die Unterscheidung für alle praktischen Zwecke ignoriert werden kann.

Spudley
quelle
2
Das ist nicht ganz richtig - 1 / -0 == 1/0 wird beispielsweise in Javascript als falsch ausgewertet. Sie "bewerten" nicht zu einer magischen vorzeichenlosen Null, da es in IEEE 754 kein solches Konzept wie "eine vorzeichenlose ganzzahlige Null" gibt.
BrainSlugs83