Warum bleibt das mehrmalige Hinzufügen von 0,1 verlustfrei?

Lesezeit: 9 Minuten

Warum bleibt das mehrmalige Hinzufugen von 01 verlustfrei
Eis

Ich kenne die 0.1 Dezimalzahl kann nicht exakt mit einer endlichen Binärzahl dargestellt werden (Erläuterung), damit double n = 0.1 wird etwas Präzision verlieren und wird nicht genau sein 0.1. Andererseits 0.5 kann genau dargestellt werden, weil es ist 0.5 = 1/2 = 0.1b.

Allerdings ist es verständlich, dass das Hinzufügen 0.1 drei Mal wird nicht genau geben 0.3 so wird der folgende Code gedruckt false:

double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
    sum += d;
System.out.println(sum == 0.3); // Prints false, OK

Aber wie kommt es dann zu dem Hinzufügen 0.1 fünf Mal wird genau geben 0.5? Der folgende Code wird gedruckt true:

double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
    sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?

Wenn 0.1 kann nicht genau dargestellt werden, wie kommt es, dass das 5-fache Hinzufügen genau ergibt 0.5 was lässt sich genau darstellen?

  • Wenn Sie wirklich recherchieren, können Sie es sicher herausfinden, aber Gleitkommazahlen sind voller “Überraschungen”, und manchmal ist es besser, einfach nur staunend zuzusehen.

    – Heiße Licks

    30. September 2014 um 12:11 Uhr

  • Sie denken auf mathematische Weise darüber nach. Fließkommaarithmetik ist in keiner Weise Mathematik.

    – Jakob

    1. Oktober 2014 um 5:57 Uhr

  • @HotLicks das ist sehr viel die falsche Einstellung zu haben.

    – Hobbs

    2. Oktober 2014 um 7:12 Uhr

  • @RussellBorogove selbst wenn es wegoptimiert wäre, wäre es nur dann eine gültige Optimierung, wenn sum hatte denselben Endwert, als ob die Schleife wirklich ausgeführt worden wäre. Im C++-Standard wird dies als „Als-ob-Regel“ oder „gleiches beobachtbares Verhalten“ bezeichnet.

    – Hobbs

    2. Oktober 2014 um 7:42 Uhr

  • @Jakob stimmt überhaupt nicht. Fließkomma-Arithmetik ist streng definiert, mit guter mathematischer Behandlung von Fehlergrenzen und dergleichen. Es ist nur so, dass viele Programmierer entweder nicht bereit sind, die Analyse durchzuführen, oder sie glauben fälschlicherweise, dass “Gleitkomma ist ungenau” alles ist, was man wissen muss, und dass es sich nicht lohnt, sich mit der Analyse zu beschäftigen.

    – Hobbs

    2. Oktober 2014 um 7:49 Uhr

Warum bleibt das mehrmalige Hinzufugen von 01 verlustfrei
Peter Lawrey

Der Rundungsfehler ist nicht zufällig und die Art und Weise, wie er implementiert wird, versucht, den Fehler zu minimieren. Dies bedeutet, dass manchmal der Fehler nicht sichtbar ist oder kein Fehler vorliegt.

Zum Beispiel 0.1 ist nicht genau 0.1 dh new BigDecimal("0.1") < new BigDecimal(0.1) aber 0.5 ist genau 1.0/2

Dieses Programm zeigt Ihnen die wahren Werte, die damit verbunden sind.

BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
    System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
    x = x.add(_0_1);
}

Drucke

0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0

Beachten Sie, dass 0.3 ist etwas abseits, aber wenn Sie dazu kommen 0.4 die Bits müssen um eins nach unten verschoben werden, um in die 53-Bit-Grenze zu passen, und der Fehler wird verworfen. Auch hier schleicht sich wieder ein Fehler ein 0.6 und 0.7 aber für 0.8 zu 1.0 der Fehler wird verworfen.

Wenn Sie es fünfmal hinzufügen, sollte der Fehler kumulieren, nicht abgebrochen werden.

Der Grund für einen Fehler liegt in der begrenzten Genauigkeit. dh 53 Bit. Dies bedeutet, dass je größer die Zahl wird, desto mehr Bits verwendet werden, desto mehr Bits müssen am Ende weggelassen werden. Dies führt zu Rundungen, die in diesem Fall zu Ihren Gunsten sind.
Sie können den gegenteiligen Effekt erzielen, wenn Sie eine kleinere Anzahl erhalten, z 0.1-0.0999 => 1.0000000000000286E-4
und Sie sehen mehr Fehler als zuvor.

Ein Beispiel dafür ist, warum in Java 6 Warum Math.round(0.49999999999999994) 1 zurückgibt. In diesem Fall führt der Verlust eines Bits in der Berechnung zu einem großen Unterschied zur Antwort.

  • Wo wird das umgesetzt?

    – EpicPandaForce

    30. September 2014 um 12:08 Uhr

  • @Zhuinden Die CPU folgt dem IEEE-754-Standard. Java gibt Ihnen Zugriff auf die zugrunde liegenden CPU-Anweisungen und mischt sich nicht ein. en.wikipedia.org/wiki/IEEE_floating_point

    – Peter Lawrey

    30. September 2014 um 12:10 Uhr

  • @PeterLawrey: Nicht unbedingt die CPU. Auf einer Maschine ohne Fließkomma in der CPU (und ohne separate FPU in Verwendung) wird die IEEE-Arithmetik von Software durchgeführt. Und wenn die Host-CPU Fließkomma hat, aber nicht den IEEE-Anforderungen entspricht, wäre eine Java-Implementierung für diese CPU meiner Meinung nach verpflichtet, auch Soft Float zu verwenden …

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    30. September 2014 um 20:29 Uhr

  • @R .. in diesem Fall weiß ich nicht, was passieren würde, wenn Sie es verwenden würden strictfp Zeit, Festkomma-Ganzzahlen zu betrachten, denke ich. (oder BigDecimal)

    – Peter Lawrey

    30. September 2014 um 20:32 Uhr

  • @eugene das Hauptproblem sind die begrenzten Werte, die Gleitkommazahlen darstellen können. Diese Einschränkung kann zu einem Informationsverlust und mit zunehmender Anzahl zu einem Fehlerverlust führen. Es wird gerundet, aber in diesem Fall wird abgerundet, sodass eine Zahl, die etwas zu groß gewesen wäre, da 0,1 etwas zu groß ist, in den richtigen Wert umgewandelt wird. Genau 0,5

    – Peter Lawrey

    1. Oktober 2014 um 6:56 Uhr

Warum bleibt das mehrmalige Hinzufugen von 01 verlustfrei
Pascal Cuoq

Außer Überlauf, in Fließkommazahl, x + x + x ist genau die korrekt gerundete (dh nächste) Gleitkommazahl zur reellen 3*x, x + x + x + x ist genau 4*xund x + x + x + x + x ist wieder die korrekt gerundete Fließkommanäherung für 5*x.

Das erste Ergebnis, z x + x + xergibt sich daraus, dass x + x ist genau. x + x + x ist somit das Ergebnis nur einer Rundung.

Das zweite Ergebnis ist schwieriger, eine Demonstration davon wird diskutiert Hier (und Stephen Canon spielt auf eine weitere Beweis-für-Fall-Analyse der letzten 3 Ziffern von an x). Zusammenfassend entweder 3*x ist im selben Binade als 2*x oder es ist in der gleichen binade wie 4*xund in jedem Fall kann man ableiten, dass der Fehler bei der dritten Addition den Fehler bei der zweiten Addition aufhebt (wobei die erste Addition, wie gesagt, exakt ist).

Das dritte Ergebnis „x + x + x + x + x ist korrekt gerundet“, leitet sich aus dem zweiten auf die gleiche Weise ab, wie sich der erste aus der Genauigkeit von ableitet x + x.


Das zweite Ergebnis erklärt warum 0.1 + 0.1 + 0.1 + 0.1 ist genau die Gleitkommazahl 0.4: Die rationalen Zahlen 1/10 und 4/10 werden auf die gleiche Weise angenähert, mit dem gleichen relativen Fehler, wenn sie in Fließkommazahlen umgewandelt werden. Diese Fließkommazahlen haben untereinander ein Verhältnis von genau 4. Das zeigen das erste und dritte Ergebnis 0.1 + 0.1 + 0.1 und 0.1 + 0.1 + 0.1 + 0.1 + 0.1 Es ist zu erwarten, dass sie weniger Fehler aufweisen, als durch eine naive Fehleranalyse gefolgert werden könnte, aber sie selbst beziehen sich nur auf die jeweiligen Ergebnisse 3 * 0.1 und 5 * 0.1von denen erwartet werden kann, dass sie nahe, aber nicht unbedingt identisch sind 0.3 und 0.5.

Wenn Sie weiter hinzufügen 0.1 nach der vierten Addition werden Sie schließlich Rundungsfehler bemerken, die „0.1 n mal zu sich selbst addiert“ abweichen n * 0.1, und weichen sogar noch mehr von n/10 ab. Wenn Sie die Werte von „0,1 n-mal zu sich selbst addiert“ als Funktion von n auftragen würden, würden Sie Linien konstanter Steigung durch Binaden beobachten (sobald das Ergebnis der n-ten Addition dazu bestimmt ist, in eine bestimmte Binade zu fallen, die Eigenschaften der Zugabe sind voraussichtlich ähnlich wie bei früheren Zugaben, die zu einem Ergebnis in der gleichen Binade führten). Innerhalb derselben Binade wird der Fehler entweder wachsen oder schrumpfen. Wenn Sie sich die Abfolge der Steigungen von Binade zu Binade ansehen würden, würden Sie die sich wiederholenden Ziffern von erkennen 0.1 für eine Weile binär. Danach würde eine Absorption beginnen, und die Kurve würde flach werden.

  • In der ersten Zeile sagen Sie, dass x + x + x genau richtig ist, aber aus dem Beispiel in der Frage ist es nicht.

    – Alboz

    30. September 2014 um 12:15 Uhr

  • @Alboz das sage ich x + x + x ist genau das Richtige gerundet Fließkommazahl zur reellen 3*x. „korrekt gerundet“ bedeutet in diesem Zusammenhang „am nächsten“.

    – Pascal Cuoq

    30. September 2014 um 12:22 Uhr

  • +1 Dies sollte die akzeptierte Antwort sein. Es bietet tatsächlich Erklärungen / Beweise dafür, was vor sich geht, und nicht nur vage Allgemeinheiten.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    30. September 2014 um 20:31 Uhr

  • @Alboz (was alles in der Frage vorgesehen ist). Diese Antwort erklärt jedoch, wie sich die Fehler zufällig aufheben, anstatt sich im schlimmsten Fall zu summieren.

    – Hobbs

    2. Oktober 2014 um 7:37 Uhr

  • @chebus 0.1 ist 0x1.999999999999999999999…p-4 in Hexadezimal (eine unendliche Folge von Ziffern). Es wird mit doppelter Genauigkeit als 0x1.99999ap-4 angenähert. 0,2 ist 0x1,999999999999999999999…p-3 im Hexadezimalformat. Aus demselben Grund, aus dem 0,1 als 0x1,99999ap-4 angenähert wird, wird 0,2 als 0x1,99999ap-3 angenähert. Unterdessen ist 0x1.99999ap-3 auch genau 0x1.99999ap-4+0x1.99999ap-4.

    – Pascal Cuoq

    4. Mai 2016 um 15:51 Uhr


Fließkommasysteme bewirken verschiedene Zauber, einschließlich ein paar zusätzlicher Genauigkeiten zum Runden. Somit wird der sehr kleine Fehler aufgrund der ungenauen Darstellung von 0,1 auf 0,5 abgerundet.

Stellen Sie sich Fließkommazahlen als eine großartige, aber UNEXAKTE Art vor, Zahlen darzustellen. Nicht alle möglichen Zahlen lassen sich leicht in einem Computer darstellen. Irrationale Zahlen wie PI. Oder wie SQRT(2). (Symbolische mathematische Systeme können sie darstellen, aber ich sagte „leicht“.)

Der Fließkommawert kann sehr nahe, aber nicht genau sein. Es kann so nah sein, dass Sie zu Pluto navigieren und Millimeter davon entfernt sein könnten. Aber immer noch nicht exakt im mathematischen Sinne.

Verwenden Sie keine Fließkommazahlen, wenn Sie genau und nicht ungefähr sein müssen. Beispielsweise möchten Buchhaltungsanwendungen eine bestimmte Anzahl von Cents auf einem Konto genau verfolgen. Ganzzahlen sind dafür gut, weil sie genau sind. Das Hauptproblem, auf das Sie bei Ganzzahlen achten müssen, ist der Überlauf.

Die Verwendung von BigDecimal für Währungen funktioniert gut, da die zugrunde liegende Darstellung eine ganze Zahl ist, wenn auch eine große.

Angesichts der Tatsache, dass Gleitkommazahlen ungenau sind, haben sie dennoch viele Verwendungsmöglichkeiten. Koordinatensysteme für die Navigation oder Koordinaten in Grafiksystemen. Astronomische Werte. Wissenschaftliche Werte. (Sie können die genaue Masse eines Baseballs wahrscheinlich ohnehin nicht auf die Masse eines Elektrons genau kennen, daher spielt Ungenauigkeit keine Rolle.)

Verwenden Sie für Zählanwendungen (einschließlich Buchhaltung) ganze Zahlen. Um die Anzahl der Personen zu zählen, die ein Tor passieren, verwenden Sie int oder long.

  • Die Frage ist markiert [java]. Die Java-Sprachdefinition hat Nein Vorkehrung für „wenige zusätzliche Bits an Genauigkeit“, nur für wenige zusätzliche Exponentenbits (und das gilt nur, wenn Sie es nicht verwenden strictfp). Nur weil Sie darauf verzichtet haben, etwas zu verstehen, heißt das nicht, dass es unergründlich ist oder dass andere darauf verzichten sollten, es zu verstehen. Sehen Sie sich unter stackoverflow.com/questions/18496560 ein Beispiel dafür an, wie weit Java-Implementierungen gehen werden, um die Sprachdefinition zu implementieren (die weder Bestimmungen für Bits mit zusätzlicher Genauigkeit noch mit strictfpfür jedes zusätzliche exp-Bit)

    – Pascal Cuoq

    5. Oktober 2014 um 23:24 Uhr


996000cookie-checkWarum bleibt das mehrmalige Hinzufügen von 0,1 verlustfrei?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy