Warum ist eine negative Null wichtig?

64

Ich bin verwirrt darüber, warum uns unterschiedliche Darstellungen für positive und negative Nullen wichtig sind.

Ich erinnere mich vage an die Behauptungen, dass eine negative Nullrepräsentation bei der Programmierung mit komplexen Zahlen extrem wichtig ist. Ich hatte noch nie die Möglichkeit, Code mit komplexen Zahlen zu schreiben. Deshalb bin ich ein wenig ratlos darüber, warum dies der Fall ist.

Der Wikipedia-Artikel zum Konzept ist nicht besonders hilfreich. es macht nur vage Behauptungen über eine vorzeichenbehaftete Null, was bestimmte mathematische Operationen im Gleitkomma einfacher macht, wenn ich es richtig verstehe. In dieser Antwort werden einige Funktionen aufgelistet, die sich unterschiedlich verhalten. Wenn Sie mit deren Verwendung vertraut sind, können Sie möglicherweise auf die Beispiele zurückgreifen. (Obwohl das spezielle Beispiel der komplexen Quadratwurzeln geradezu falsch aussieht, da die beiden Zahlen mathematisch äquivalent sind, es sei denn, ich habe ein Missverständnis.) Aber ich konnte keine klare Aussage darüber finden, in welche Schwierigkeiten Sie geraten würden, wenn sie nicht da wären. Je mehr mathematische Ressourcen ich finden konnte, umso mehr konnte ich feststellen, dass es aus mathematischer Sicht keine Unterscheidung zwischen beiden gibt, und der Wikipedia-Artikel legt den Schluss nahe, dass dies außerhalb des Rechnens, abgesehen von der Beschreibung von Grenzen, nur selten vorkommt.

Warum ist eine negative Null beim Rechnen wertvoll? Ich bin sicher, ich vermisse nur etwas.

jpmc26
quelle
6
Eine negative Null kann einen Unterlauf in einer IEEE-Gleitkommazahl signalisieren, aber darüber hinaus scheint ihre Verwendung umstritten und unklar zu sein. Wenn ich raten würde, würde ich sagen, dass eine negative Null im IEEE-Gleitkomma dargestellt wird, weil ... Sie können. Für eine noch interessantere Fahrt lesen Sie die Informationen zur Fließkomma-Signalisierung von NaN.
Robert Harvey
1
Wenn das bestimmte Beispiel "1 / 0.0" / "1 / -0.0" ist, ist 0 ein Verzweigungsschnitt für 1 / x und die Grenze hängt davon ab, ob Sie sich ihm von unten oder von oben nähern.
Vatine
@Vatine Nein, das besondere Beispiel ist sqrt(-1+0i) = iund sqrt(-1-0i) = -i, obwohl es für einige Programmiersprachen die richtige Syntax hat, glaube ich. Ich werde bearbeiten, um es klarer zu machen.
jpmc26
3
Ich habe nach Programmierern , Stack Overflow , Informatik , Mathematik und Ingenieurwissenschaften gesucht . Die einzige Frage, die ich finden konnte, war Verwendet für einen negativen Gleitkommawert von Null? . Dies kann nicht nur das zweite Mal sein, dass dies geschah!
Ich bin wirklich überrascht, dass komplexe Zahlen in den Antworten überhaupt nicht auftauchen, insbesondere angesichts des Quadratwurzel-Beispiels, auf das ich hingewiesen habe.
jpmc26

Antworten:

69

Beachten Sie, dass in der FPU-Arithmetik 0 nicht unbedingt Null bedeuten muss, sondern auch einen zu kleinen Wert, um mit einem bestimmten Datentyp dargestellt zu werden, z

a = -1 / 1000000000000000000.0

a ist zu klein, um durch float (32 Bit) korrekt dargestellt zu werden, daher wird es auf -0 "gerundet".

Angenommen, unsere Berechnung wird fortgesetzt:

b = 1 / a

Da a float ist, führt dies zu -infinity, was weit von der korrekten Antwort von -10000000000000000.0 entfernt ist

Nun berechnen wir b, wenn es keine -0 gibt (also wird a auf +0 gerundet):

b = 1 / +0
b = +infinity

Das Ergebnis ist wieder falsch wegen der Rundung, aber jetzt ist es "falscher" - nicht nur numerisch, sondern vor allem wegen des unterschiedlichen Vorzeichens (Ergebnis der Berechnung ist + unendlich, korrektes Ergebnis ist -1000000000000000000.0).

Man könnte immer noch sagen, dass es nicht wirklich wichtig ist, da beide falsch sind. Wichtig ist, dass es viele numerische Anwendungen gibt, bei denen das wichtigste Ergebnis der Berechnung das Vorzeichen ist. Wenn Sie beispielsweise mit einem Algorithmus für maschinelles Lernen entscheiden, ob Sie an der Kreuzung nach links oder rechts abbiegen, können Sie einen positiven Wert interpretieren => abbiegen links, negativer Wert => rechts abbiegen, tatsächliche "Größe" des Wertes ist nur "Konfidenzkoeffizient".

qbd
quelle
Haben Sie eine Idee, ob das Unterlaufzeichen bei der Berechnung von imaginären / komplexen Zahlen besonders wichtig sein könnte?
jpmc26
@qbd: Weißt du, was diese numerischen Anwendungen sind? Ich würde sagen , dass Programme , die auslöst und verwendet +infund -infim Normalbetrieb abgehört werden.
Björn Lindqvist,
@ BjörnLindqvist Wenn du konkrete, herunterladbare Anwendungen willst - dann kenne ich keine. Ich denke nicht, dass es notwendigerweise fehlerhaft ist - anstelle von float / double könnten Sie so etwas wie BigDecimal mit unbegrenzter Präzision verwenden. Aber lohnt es sich, wenn das Programm genau die gleichen Ergebnisse liefert wie das Programm mit float / double, aber mit viel schlechterer Leistung?
qbd
Sie haben "numerische Anwendungen geschrieben, bei denen das wichtigste Ergebnis der Berechnung das Vorzeichen ist". Ich kann das glauben, aber ich kann nicht glauben, dass es gut geschriebene Anwendungen gibt, die auf -0 und auf Werten als +infund beruhen -inf. Wenn dein Programm einen Gleitkomma-Unterlauf verursacht, ist das der Fehler und was danach passiert, ist nicht so interessant, imho. Wir vermissen immer noch praktische Beispiele, in denen -0 nützlich ist.
Björn Lindqvist
1
@ BjörnLindqvist Ein Großteil von x265 wird in der Baugruppe ausgeführt. Dabei stützen sich die Informationen auf die dunklen Details (die von der CPU-Architektur abhängen), die nur wenige im Namen der Leistung kennen. Ist es falsch? Sich für eine einfache, gut verstandene Funktion im Namen der Leistung auf den allgemein implementierten 30-jährigen Standard (der hier bleiben wird) zu verlassen, scheint plötzlich nicht mehr so ​​schlimm.
qbd
8

Wie erstelle ich eine -0? Es gibt zwei Möglichkeiten: (1) Führen Sie eine Gleitkommaoperation aus, bei der das mathematische Ergebnis negativ ist, aber so nahe an Null liegt, dass es auf Null und nicht auf eine Zahl ungleich Null gerundet wird. Diese Berechnung ergibt eine -0. (b) Bestimmte Operationen mit Nullen: Multiplizieren Sie eine positive Null mit einer negativen Zahl oder dividieren Sie eine positive Null mit einer negativen Zahl oder negieren Sie eine positive Null.

Eine negative Null zu haben, vereinfacht die Multiplikation und Division ein wenig. Das Vorzeichen von x * y oder x / y ist immer das Vorzeichen von x, exclusive oder das Vorzeichen von y. Ohne negative Null müsste eine zusätzliche Prüfung durchgeführt werden, um -0 durch +0 zu ersetzen.

Es gibt einige sehr seltene Situationen, in denen es nützlich ist. Sie können überprüfen, ob das Ergebnis einer Multiplikation oder Division mathematisch größer oder kleiner als Null ist, auch wenn ein Unterlauf vorliegt (sofern Sie wissen, dass das Ergebnis keine mathematische Null ist). Ich kann mich nicht erinnern, jemals Code geschrieben zu haben, bei dem es einen Unterschied macht.

Optimierende Compiler hassen -0. Beispielsweise können Sie x + 0.0 nicht durch x ersetzen, da das Ergebnis nicht x sein sollte, wenn x -0.0 ist. Sie können x * 0.0 nicht durch 0.0 ersetzen, da das Ergebnis -0.0 sein sollte, wenn x <0 ist oder x -0.0 ist.

gnasher729
quelle
7
Ich wünschte, IEEE-754 hätte vier Nullen enthalten: "genau", positiv infinitesimal, negativ infinitesimal und ohne Vorzeichen (letzteres ist der Unterschied zwischen nicht unterscheidbaren Werten). Dies hätte viele Gleitkomma-Axiome funktionieren lassen - darunter x + 0,0 Äquiv. X-0,0 Äquiv. X, xy Äquiv. X + (- 1,0) * y und 1,0 / x Äquiv. -1,0 / (- 1,0 * x) [wenn x eine positive Null ist, wären beide pos-inf; wenn neg-zero, beide neg-inf; wenn genau oder ohne Vorzeichen, beide NaN].
Supercat
Ich konnte eine negative Null bekommen, indem ich überging -5und 5hinein ging fmod(). Es ist ziemlich ärgerlich für meinen Anwendungsfall.
Aaron Franke
6

C # Double, das IEEE 754 entspricht

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

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

druckt:

Infinity
-Infinity

eigentlich ein wenig zu erklären ...

Double d = -0.0; 

Dies bedeutet etwas viel näher an d = The Limit of x as x approaches 0-oder The Limit of x as x approaches 0 from the negatives.


Um Philipps Kommentar anzusprechen ...

Grundsätzlich bedeutet negative Null Unterlauf.

Es gibt sehr wenig praktischen Nutzen für eine negative Null, wenn überhaupt ...

Zum Beispiel dieser Code (wieder C #):

double a = -0.0;
double b = 0.0;

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

ergibt dieses Ergebnis:

True
True
0

Zur informellen Erläuterung: Alle Sonderwerte, die ein Gleitkomma nach IEEE 754 haben kann (positive Unendlichkeit, negative Unendlichkeit, NAN, -0,0), haben im praktischen Sinne keine Bedeutung. Sie können keinen physikalischen Wert oder einen Wert darstellen, der in der Berechnung der "realen Welt" sinnvoll ist. Was sie bedeuten, ist im Grunde das:

  • Positive Unendlichkeit bedeutet einen Überlauf am positiven Ende, den ein Gleitkomma darstellen kann
  • Negative Unendlichkeit bedeutet einen Überlauf am positiven Ende, den ein Gleitkomma darstellen kann
  • Eine negative Null bedeutet einen Unterlauf und die Operanden hatten entgegengesetzte Vorzeichen
  • Eine positive Null kann einen Unterlauf bedeuten und die Operanden hatten das gleiche Vorzeichen
  • NAN bedeutet , dass Ihre Berechnung gefällig nicht definiert ist, wie sqrt(-7), oder es funktioniert nicht eine Grenze , wie hat 0/0oder wiePositiveInfinity/PositiveInfinity
AK_
quelle
7
Ja, aber warum ist das wichtig? Können Sie ein praktisches, reales Beispiel geben, bei dem es auf den Unterschied ankommt?
Philipp
5

Die Frage, inwiefern dies mit Berechnungen komplexer Zahlen zusammenhängt, ist der Kern der Frage, warum sowohl +0 als auch -0 im Gleitkomma existieren. Wenn Sie sich mit Komplexanalyse beschäftigen, stellen Sie schnell fest, dass stetige Funktionen von Komplex zu Komplex normalerweise nicht als "einwertig" behandelt werden können, es sei denn, man nimmt die "höfliche Fiktion" an, dass die Ergebnisse eine sogenannte "Riemann-Oberfläche" bilden. Zum Beispiel weist der komplexe Logarithmus jedem Eingang unendlich viele Ausgänge zu; Wenn Sie sie zu einer kontinuierlichen Ausgabe verbinden, werden alle realen Teile um den Ursprung herum zu einer unendlichen Korkenzieheroberfläche. Eine kontinuierliche Kurve, die die reale Achse von der positiv-imaginären Seite nach unten schneidet, und eine weitere Kurve, die sich um den Pol schlängelt und die reale Achse schneidet.

Wenden Sie dies nun auf ein numerisches Programm an, das mit komplexen Gleitkommazahlen berechnet. Die nach einer bestimmten Berechnung ausgeführten Aktionen können sehr unterschiedlich sein, je nachdem, auf welchem ​​Blatt sich das Programm gerade befindet. Das Vorzeichen des zuletzt berechneten Ergebnisses gibt wahrscheinlich Auskunft darüber, auf welchem ​​Blatt sich das Blatt befindet. Angenommen, das Ergebnis war Null? Denken Sie daran, hier bedeutet "Null" wirklich "zu klein, um richtig darzustellen". Wenn die Berechnung jedoch vorsehen könnte, dass das Vorzeichen erhalten bleibt (dh, dass Sie sich merken, welches "Blatt" ist), wenn das Ergebnis Null ist, kann der Code das Vorzeichen überprüfen und auch in dieser Situation die richtige Aktion ausführen.

PJM
quelle
1

Der Grund ist einfacher als üblich

Natürlich gibt es eine Menge Hacks, die sehr gut aussehen und nützlich sind (wie das Runden auf -0.0oder +0.0aber nehmen wir an, wir haben eine Darstellung von signiertem int mit einem Minus / Plus-Zeichen am Anfang (ich weiß, das wird durch U2-Binärcode aufgelöst) in ganzen Zahlen normalerweise aber nehmen Sie eine weniger komplizierte Darstellung von doppelt an):

0 111 = 7
^ sign

Was ist, wenn es eine negative Zahl gibt?

1 111 = -7

Okay, so einfach. Stellen wir also 0 dar:

0 000 = 0

Das ist auch gut so. Aber was ist mit 1 000? Muss es eine verbotene Nummer sein? Besser nicht.

Nehmen wir also an, es gibt zwei Arten von Nullen:

0 000 = +0
1 000 = -0

Nun, das wird unsere Berechnungen vereinfachen und zum Glück einige zusätzliche Funktionen abrunden. Das +0und -0kommt also nur von binären Darstellungsproblemen.

Dawid Pura
quelle
6
Wenn ich das richtig lese, sagen Sie im Grunde nur, dass die Leute, die die Standards definieren oder implementieren, sich nicht die Mühe machen wollten, es zu verbieten. Ich denke nicht, dass diese Argumentation der Tatsache entspricht, dass das 2er-Komplement die "negative Null" -Darstellung für eine ganz andere Zahl verwendet und keine Darstellung der negativen Null hat. Siehe den Wikipedia-Artikel, den ich verlinkt habe.
jpmc26
1
@ jpmc26 Ich denke, dass es tatsächlich etwas Wahres gibt, was bedeutet, dass man keine Implementierungen benötigt, um einen Sonderfall zu haben. So wie es ist, hat jede Zahl ein Vorzeichenbit und kann durch Umschalten des Vorzeichenbits negiert werden. Sogar NaNs sind signiert, und Implementierungen können (müssen aber nicht) ein geeignetes Zeichen für die Erstellung eines NaN auswählen. Wenn keine negative Null vorhanden wäre, müsste jede Berechnung, die zu 0 führte, zusätzliche Arbeit leisten, um das Vorzeichenbit usw. zu reparieren
hobbs
4
@ jpmc26 (dh bei jeder zweiten Multiplikation von zwei Zahlen ist das Vorzeichen des Ergebnisses das X oder das Vorzeichen der Multiplikanden, und die Größe ist das Produkt der beiden Größen. Im wirklichen Leben funktioniert dies für -1 * 0 = - 0. Aber wenn Null mit dem umgedrehten Vorzeichen-Bit ein spezieller Wert ungleich Null wäre, müsste jedes Produkt, das 0 erzeugen könnte, überprüfen und sicherstellen, dass es nicht versehentlich diesen speziellen Wert erzeugt.)
hobbs