Frage Ist Gleitkomma-Mathematik gebrochen?


Betrachten Sie den folgenden Code:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

Warum passieren diese Ungenauigkeiten?


2259
2018-02-25 21:39


Ursprung


Antworten:


Binär Gleitkomma Mathe ist so. In den meisten Programmiersprachen basiert es auf dem IEEE 754 Standard. JavaScript verwendet eine 64-Bit-Gleitkommadarstellung, die der von Java entspricht double. Der Kern des Problems ist, dass Zahlen in diesem Format als eine ganze Zahl mal eine Potenz von zwei dargestellt werden; rationale Zahlen (wie 0.1, welches ist 1/10) deren Nenner keine Zweierpotenz ist, kann nicht genau dargestellt werden.

Zum 0.1 im Standard binary64 Format, die Darstellung kann genau so geschrieben werden

  • 0.1000000000000000055511151231257827021181583404541015625 in Dezimal, oder
  • 0x1.999999999999ap-4 im C99 Hexfloat-Notation.

Im Gegensatz dazu die rationale Zahl 0.1, welches ist 1/10, kann genau wie geschrieben werden

  • 0.1 in Dezimal, oder
  • 0x1.99999999999999...p-4 in einem Analogon der C99 - Hexfloat - Notation, wobei der ... repräsentiert eine unendliche Folge von 9's.

Die Konstanten 0.2 und 0.3 in Ihrem Programm werden auch Annäherungen an ihre wahren Werte sein. Es kommt vor, dass das nächste ist double zu 0.2 ist größer als die rationale Zahl 0.2 aber das ist das nächste double zu 0.3 ist kleiner als die rationale Zahl 0.3. Die Summe von 0.1 und 0.2 windet sich größer als die rationale Zahl 0.3 und daher mit der Konstante in Ihrem Code nicht einverstanden.

Eine ziemlich umfassende Behandlung von Gleitkomma-Rechenproblemen ist Was jeder Informatiker über Gleitkommaarithmetik wissen sollte. Eine einfachere Erklärung finden Sie unter schwimm-point-gui.de.


1718
2018-04-18 11:52



Die Perspektive eines Hardware-Designers

Ich glaube, ich sollte eine Hardware-Designer-Perspektive dazu hinzufügen, da ich Gleitkomma-Hardware entwerfe und baue. Die Kenntnis des Ursprungs des Fehlers kann helfen, zu verstehen, was in der Software passiert, und letztendlich hoffe ich, dass dies hilft, die Gründe dafür zu erklären, warum Gleitkommafehler auftreten und sich im Laufe der Zeit anhäufen.

1. Übersicht

Aus technischer Sicht weisen die meisten Gleitkommaoperationen einige Fehler auf, da die Hardware, die die Gleitkommaberechnungen durchführt, nur einen Fehler von weniger als der Hälfte einer Einheit an der letzten Stelle aufweisen muss. Daher wird viel Hardware mit einer Genauigkeit angehalten, die nur notwendig ist, um einen Fehler von weniger als der Hälfte einer Einheit an der letzten Stelle für a zu ergeben einzelne Operation was besonders problematisch bei der Gleitkomma-Division ist. Was eine einzelne Operation ausmacht, hängt davon ab, wie viele Operanden die Einheit benötigt. Für die meisten sind es zwei, aber einige Einheiten benötigen 3 oder mehr Operanden. Aus diesem Grund gibt es keine Garantie, dass wiederholte Operationen zu einem wünschenswerten Fehler führen, da sich die Fehler im Laufe der Zeit addieren.

2. Standards

Die meisten Prozessoren folgen dem IEEE-754 Standard, aber einige verwenden denormalisierte oder andere Standards . Zum Beispiel gibt es in IEEE-754 einen denormalisierten Modus, der die Darstellung sehr kleiner Fließkommazahlen auf Kosten der Genauigkeit ermöglicht. Das Folgende wird jedoch den normalisierten Modus von IEEE-754 abdecken, der der typische Betriebsmodus ist.

Im IEEE-754-Standard wird Hardware-Entwicklern jeder Wert von Fehler / Epsilon erlaubt, solange es weniger als die Hälfte einer Einheit an der letzten Stelle ist, und das Ergebnis muss nur weniger als die Hälfte einer Einheit in der letzten sein Platz für eine Operation. Dies erklärt, warum sich bei wiederholten Operationen die Fehler addieren. Für die doppelte IEEE-754-Genauigkeit ist dies das 54. Bit, da 53 Bits verwendet werden, um den numerischen Teil (normalisiert), auch Mantisse genannt, der Gleitkommazahl (z. B. 5.3 in 5.3e5) darzustellen. In den nächsten Abschnitten werden die Ursachen von Hardwarefehlern bei verschiedenen Gleitkommaoperationen näher erläutert.

3. Ursache für Rundungsfehler in der Division

Die Hauptursache für den Fehler bei der Gleitkommadivision sind die Divisionalgorithmen, die zur Berechnung des Quotienten verwendet werden. Die meisten Computersysteme berechnen Division durch Multiplikation mit einer Inversen, hauptsächlich in Z=X/Y, Z = X * (1/Y). Eine Division wird iterativ berechnet, d. H. Jeder Zyklus berechnet einige Bits des Quotienten, bis die gewünschte Genauigkeit erreicht ist, was für IEEE-754 alles mit einem Fehler von weniger als einer Einheit an der letzten Stelle ist. Die Tabelle der Kehrwerte von Y (1 / Y) ist als die Quotientenauswahltabelle (QST) in der langsamen Division bekannt, und die Größe in Bits der Quotientenauswahltabelle ist üblicherweise die Breite der Radix oder eine Anzahl von Bits von der in jeder Iteration berechnete Quotient plus ein paar Guard-Bits. Für den IEEE-754-Standard, doppelte Genauigkeit (64 Bit), wäre es die Größe der Radix des Teilers plus einige Schutzbits k, wo k>=2. So wäre zum Beispiel eine typische Quotientenauswahltabelle für einen Teiler, der 2 Bits des Quotienten gleichzeitig berechnet (Radix 4) 2+2= 4 Bits (plus ein paar optionale Bits).

3.1 Division Rundungsfehler: Approximation von reziprok

Welche Kehrwerte in der Quotientenauswahltabelle sind, hängt von der Trennungsmethode: langsame Division wie SRT Division oder schnelle Division wie Goldschmidt Division; Jeder Eintrag wird gemäß dem Divisionsalgorithmus modifiziert, um einen möglichst geringen Fehler zu erzielen. In jedem Fall sind jedoch alle Kehrwerte Annäherungen des tatsächlichen reziproken und ein Element des Fehlers vorstellen. Sowohl langsame Divisions- als auch schnelle Divisionsmethoden berechnen den Quotienten iterativ, dh es wird jede Anzahl von Bits des Quotienten in jedem Schritt berechnet, dann wird das Ergebnis vom Dividend subtrahiert, und der Dividierer wiederholt die Schritte, bis der Fehler kleiner als die Hälfte ist Einheit an der letzten Stelle. Langsame Division Methoden berechnen eine feste Anzahl von Stellen des Quotienten in jedem Schritt und sind in der Regel weniger teuer zu bauen, und schnelle Division Methoden berechnen eine variable Anzahl von Ziffern pro Schritt und sind in der Regel teurer zu bauen. Der wichtigste Teil der Divisionsmethoden ist, dass die meisten von ihnen auf die wiederholte Multiplikation mit einem Annäherung von einem reziproken, so sind sie anfällig für Fehler.

4. Rundungsfehler in anderen Operationen: Trunkierung

Ein weiterer Grund für die Rundungsfehler in allen Operationen sind die verschiedenen Arten der Kürzung der endgültigen Antwort, die IEEE-754 erlaubt. Es ist abgeschnitten, rund-gegen-null, auf den nächsten (Standard), Abrundung und Zusammenfassung. Alle Methoden führen ein Fehlerelement von weniger als einer Einheit an der letzten Stelle für eine einzelne Operation ein. Im Laufe der Zeit und bei wiederholten Operationen addiert die Abschneidung den resultierenden Fehler auch kumulativ. Dieser Verkürzungsfehler ist besonders problematisch bei der Potenzierung, die eine Form der wiederholten Multiplikation beinhaltet.

5. Wiederholte Operationen

Da die Hardware, die die Fließkommaberechnungen ausführt, nur ein Ergebnis mit einem Fehler von weniger als der Hälfte einer Einheit an der letzten Stelle für eine einzelne Operation ergeben muss, wird der Fehler bei wiederholten Operationen größer, wenn er nicht beobachtet wird. Dies ist der Grund dafür, dass Mathematiker bei Berechnungen, die einen begrenzten Fehler erfordern, Methoden verwenden, wie z. B. die Verwendung von "run-to-nearest" gerade Ziffer an letzter Stelle von IEEE-754, weil sich die Fehler mit der Zeit gegenseitig aufheben und Intervall Arithmetik kombiniert mit Variationen der IEEE 754 Rundungsmodi Rundungsfehler vorhersagen und korrigieren. Wegen seines relativ niedrigen Fehlers im Vergleich zu anderen Rundungsmodi ist der Standardrundungsmodus von IEEE-754 auf die nächste gerade Ziffer (an letzter Stelle) gerundet.

Beachten Sie, dass der standardmäßige Rundungsmodus auf den nächsten Wert gerundet wird gerade Ziffer an letzter Stelle, garantiert einen Fehler von weniger als der Hälfte einer Einheit an der letzten Stelle für eine Operation. Die Verwendung von Abschneiden, Aufrunden und Abrunden allein kann zu einem Fehler führen, der an der letzten Stelle größer als die Hälfte einer Einheit ist, an der letzten Stelle jedoch kleiner als eine Einheit. Daher werden diese Modi nicht empfohlen, es sei denn, sie sind in Intervallarithmetik verwendet.

6. Zusammenfassung

Kurz gesagt, ist der Hauptgrund für die Fehler in Gleitkommaoperationen eine Kombination aus der Verkürzung der Hardware und der Verkürzung eines Reziprokwertes im Falle einer Division. Da der IEEE-754-Standard nur einen Fehler von weniger als der Hälfte einer Einheit an der letzten Stelle für eine einzelne Operation erfordert, addieren sich die Gleitkommafehler bei wiederholten Operationen, wenn sie nicht korrigiert werden.


490
2018-02-25 21:43



Wenn Sie .1 oder 1/10 in Basis 2 (binär) konvertieren, erhalten Sie ein sich wiederholendes Muster hinter dem Dezimalpunkt, genau wie in der Basis 10, um 1/3 darzustellen. Der Wert ist nicht exakt, und deshalb können Sie nicht Genaue Mathematik mit normalen Gleitkomma-Methoden.


356
2017-11-20 02:39



Die meisten Antworten behandeln diese Frage in sehr trockenen, technischen Begriffen. Ich möchte dies mit Begriffen ansprechen, die normale Menschen verstehen können.

Stellen Sie sich vor, Sie versuchen Pizzen in Scheiben zu schneiden. Sie haben einen Roboter-Pizzaschneider, der Pizzascheiben schneiden kann genau entzwei. Es kann eine ganze Pizza halbieren, oder es kann eine bestehende Scheibe halbieren, aber in jedem Fall ist die Halbierung immer genau.

Dieser Pizzaschneider hat sehr feine Bewegungen, und wenn Sie mit einer ganzen Pizza beginnen, dann halbieren Sie diese und fahren fort, die kleinste Scheibe jedes Mal zu halbieren, Sie können die Halbierung durchführen 53 mal bevor die Scheibe für ihre hochpräzisen Fähigkeiten zu klein ist. An diesem Punkt können Sie das sehr dünne Segment nicht mehr halbieren, sondern es entweder ein- oder ausschließen.

Nun, wie würdest du alle Scheiben so schneiden, dass sie ein Zehntel (0,1) oder ein Fünftel (0,2) einer Pizza ergeben? Denk wirklich darüber nach und probiere es aus. Sie können sogar versuchen, eine echte Pizza zu verwenden, wenn Sie einen mythischen Präzisions-Pizzaschneider zur Hand haben. :-)


Die meisten erfahrenen Programmierer kennen natürlich die wahre Antwort, nämlich dass es keine Möglichkeit gibt, ein Stück zusammen zu stellen genau Zehnten oder Fünften der Pizza mit diesen Scheiben, egal wie fein Sie sie schneiden. Sie können eine ziemlich gute Annäherung machen, und wenn Sie die Annäherung von 0.1 mit der Annäherung von 0.2 addieren, erhalten Sie eine ziemlich gute Annäherung von 0.3, aber es ist immer noch genau das, eine Annäherung.

Für Zahlen mit doppelter Genauigkeit (das ist die Genauigkeit, mit der Sie Ihre Pizza 53 Mal zu halbieren), sind die Zahlen sofort weniger und größer als 0,1 0,09999999999999999167332731531132594682276248931884765625 und 0,1000000000000000055511151231257827021181583404541015625. Letzteres ist 0,1 näher als das erste, so dass ein numerischer Parser bei einer Eingabe von 0,1 den letzteren bevorzugen wird.

(Der Unterschied zwischen diesen beiden Zahlen ist der "kleinste Ausschnitt", den wir entweder berücksichtigen müssen, der eine nach oben gerichtete Verzerrung einführt oder ausschließt, die eine Verzerrung nach unten bewirkt. Der technische Ausdruck für diese kleinste Scheibe ist ein Ulp.)

Im Fall von 0.2 sind die Zahlen alle gleich, nur um einen Faktor 2 hochskaliert. Wir bevorzugen wiederum den Wert, der etwas höher als 0,2 ist.

Beachten Sie, dass in beiden Fällen die Annäherungen für 0,1 und 0,2 eine leichte Tendenz nach oben haben. Wenn wir genug von diesen Voreingenommenheiten addieren, werden sie die Zahl weiter und weiter weg von dem, was wir wollen, schieben, und tatsächlich ist im Fall von 0,1 + 0,2 die Verzerrung hoch genug, dass die resultierende Zahl nicht mehr die nächste Zahl ist bis 0,3.

Insbesondere 0,1 + 0,2 ist wirklich 0,1000000000000000055511151231257827021181583404541015625 + 0.20000000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, während die Zahl, die am nächsten an 0.3 liegt, tatsächlich 0.2999999999999999888977697537484345957636831319091796875 ist.


P.S. Einige Programmiersprachen bieten auch Pizzaschneider, die das können teile Scheiben in exakte Zehntel. Obwohl solche Pizzaschneider ungewöhnlich sind, wenn Sie Zugang zu einem haben, sollten Sie es verwenden, wenn es wichtig ist, genau ein Zehntel oder ein Fünftel einer Scheibe zu erhalten.

(Ursprünglich auf Quora gepostet.)


225
2018-02-25 21:41



Gleitkomma-Rundungsfehler 0.1 kann nicht so genau in Basis-2 dargestellt werden wie in Basis-10 aufgrund des fehlenden Primums von 5. Genau wie 1/3 eine unendliche Anzahl von Ziffern zur Darstellung in Dezimalzahlen annimmt, aber in Basis-3 "0.1" ist, 0.1 nimmt eine unendliche Anzahl von Ziffern in der Basis-2, wo es nicht in der Basis-10 ist. Und Computer haben nicht unendlich viel Speicher.


199
2018-04-09 12:25



Zusätzlich zu den anderen richtigen Antworten möchten Sie möglicherweise Ihre Werte skalieren, um Probleme mit Gleitkommaarithmetik zu vermeiden.

Beispielsweise:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... Anstatt von:

var result = 0.1 + 0.2;     // result === 0.3 returns false

Der Ausdruck 0.1 + 0.2 === 0.3 kehrt zurück false in JavaScript, aber glücklicherweise ist Integer-Arithmetik in Fließkommazahl genau, so dass Dezimaldarstellungsfehler durch Skalierung vermieden werden können.

Als praktisches Beispiel wird empfohlen, Floating-Point-Probleme zu vermeiden, bei denen die Genauigkeit im Vordergrund steht1 Geld als Ganzzahl behandeln, die die Anzahl der Cents darstellt: 2550 Cent statt 25.50 Dollar.


1 Douglas Crockford: JavaScript: Die guten Teile: Anhang A - Schreckliche Teile (Seite 105).


98
2018-02-23 17:15



Meine Antwort ist ziemlich lang, also habe ich sie in drei Abschnitte aufgeteilt. Da es sich bei der Frage um Gleitkommathematik handelt, habe ich den Schwerpunkt darauf gelegt, was die Maschine tatsächlich macht. Ich habe es auch spezifisch für die doppelte (64 Bit) Genauigkeit gemacht, aber das Argument gilt gleichermaßen für jede Gleitkomma-Arithmetik.

Präambel

Ein IEEE 754 binäres Fließkomma-Format mit doppelter Genauigkeit (binary64) Zahl steht für eine Nummer des Formulars

Wert = (-1) ^ s * (1.m51m50... m2m1m0)2 * 2e-1023

in 64 Bits:

  • Das erste Bit ist das Zeichenbit: 1 wenn die Nummer negativ ist, 0 Andernfalls1.
  • Die nächsten 11 Bits sind die Exponent, welches ist Offset Mit anderen Worten, nach dem Lesen der Exponenten-Bits von einer doppelt-genauen Zahl muss 1023 subtrahiert werden, um die Potenz von zwei zu erhalten.
  • Die restlichen 52 Bits sind die Signifikant (oder Mantisse). In der Mantisse ein "implizierter" 1. ist immer2 entfällt, da das höchstwertige Bit eines beliebigen binären Wertes ist 1.

1 - IEEE 754 ermöglicht das Konzept eines signiert Null - +0 und -0 werden anders behandelt: 1 / (+0) ist positive Unendlichkeit; 1 / (-0) ist eine negative Unendlichkeit. Für Null-Werte sind die Mantissen- und Exponenten-Bits alle Null. Hinweis: Nullwerte (+0 und -0) sind explizit nicht denormal2.

2 - Dies ist nicht der Fall für abnormale Zahlen, die einen Offset - Exponenten von Null haben (und eine implizite 0.). Der Bereich der denormalen Zahlen mit doppelter Genauigkeit ist dMindest ≤ | x | ≤ dmax, wo dMindest (die kleinste darstellbare Nicht-Null-Zahl) ist 2-1023 - 51 (≈ 4.94 * 10-324) und dmax (die größte Denormalzahl, für die die Mantisse ausschließlich aus 1s) ist 2-1023 + 1 - 2-1023 - 51 (≈ 2.225 * 10-308).


Eine doppelte Genauigkeitszahl in eine binäre Zahl umwandeln

Es gibt viele Online-Konverter, um eine Gleitkommazahl mit doppelter Genauigkeit in Binärwerte umzuwandeln (z. B. bei binärconvert.com), aber hier ist ein Beispiel C # -Code, um die IEEE 754-Darstellung für eine doppelte Genauigkeitszahl zu erhalten (Ich teile die drei Teile mit Doppelpunkten (:):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Auf den Punkt gebracht: die ursprüngliche Frage

(Springe nach unten für die TL; DR-Version)

Cato Johnston (der Fragesteller fragte, warum 0,1 + 0,2! = 0,3).

In Binärform (mit Doppelpunkten, die die drei Teile trennen) geschrieben, sind die IEEE 754-Darstellungen der Werte:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Beachten Sie, dass die Mantisse aus wiederkehrenden Ziffern besteht 0011. Das ist Schlüssel Warum gibt es einen Fehler bei den Berechnungen - 0.1, 0.2 und 0.3 können nicht binär dargestellt werden genau in einem endlich Anzahl der binären Bits mehr als 1/9, 1/3 oder 1/7 kann genau in dargestellt werden Dezimalziffern.

Konvertieren der Exponenten in Dezimal, Entfernen des Offsets und erneutes Hinzufügen des Implizierten 1 (in eckigen Klammern), 0,1 und 0,2 sind:

0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

Um zwei Zahlen hinzuzufügen, muss der Exponent gleich sein, d.h.

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

Da die Summe nicht von der Form 2 istn * 1. {bbb} Wir erhöhen den Exponenten um eins und verschieben die Dezimalzahl (binär) Punkt zu bekommen:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

Es gibt jetzt 53 Bits in der Mantisse (der 53. ist in der oberen Zeile in eckigen Klammern). Der Standard Rundungsmodus für IEEE 754 ist 'Rund um den nächsten'- d.h. wenn eine Nummer x fällt zwischen zwei Werten ein und bwird der Wert gewählt, bei dem das niedrigstwertige Bit Null ist.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

Beachten Sie, dass ein und b unterscheiden sich nur im letzten Bit; ...0011 + 1 = ...0100. In diesem Fall ist der Wert mit dem niedrigstwertigen Bit Null b, also ist die Summe:

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL; DR

Schreiben 0.1 + 0.2 in einer binären Darstellung nach IEEE 754 (mit Doppelpunkten, die die drei Teile trennen) und vergleichen sie mit 0.3, das ist (ich habe die einzelnen Bits in eckige Klammern gesetzt):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

Zurück in das Dezimalformat konvertiert, sind diese Werte:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

Der Unterschied ist genau 2-54das ist ~ 5.5511151231258 × 10-17 - unbedeutend (für viele Anwendungen) im Vergleich zu den ursprünglichen Werten.

Ein Vergleich der letzten paar Bits einer Fließkommazahl ist inhärent gefährlich, wie jeder, der den berühmten "Was jeder Informatiker über Gleitkommaarithmetik wissen sollte"(was alle wichtigen Teile dieser Antwort abdeckt) wird es wissen.

Die meisten Rechner verwenden zusätzliche Wachziffern um dieses Problem zu umgehen, wie 0.1 + 0.2 Würde geben 0.3: Die letzten paar Bits sind abgerundet.


80
2018-03-16 05:27



Fließkommazahlen, die in dem Computer gespeichert sind, bestehen aus zwei Teilen, einer Ganzzahl und einem Exponenten, zu dem die Basis verwendet wird, und werden mit dem ganzzahligen Teil multipliziert.

Wenn der Computer in der Basis 10 arbeitete, 0.1 wäre 1 x 10⁻¹, 0.2 wäre 2 x 10⁻¹, und 0.3 wäre 3 x 10⁻¹. Ganzzahlmathematik ist einfach und genau, also addieren 0.1 + 0.2 wird offensichtlich ergeben 0.3.

Computer arbeiten normalerweise nicht in der Basis 10, sie arbeiten in der Basis 2. Sie können z. B. immer noch genaue Ergebnisse für einige Werte erhalten 0.5 ist 1 x 2⁻¹ und 0.25 ist 1 x 2⁻²und fügt sie hinzu 3 x 2⁻², oder 0.75. Genau.

Das Problem kommt mit Zahlen, die genau in der Basis 10, aber nicht in der Basis 2 dargestellt werden können. Diese Zahlen müssen auf ihre nächste Entsprechung gerundet werden. Angenommen, das sehr gängige IEEE 64-Bit Gleitkommaformat ist die nächstliegende Zahl 0.1 ist 3602879701896397 x 2⁻⁵⁵und die nächste Nummer zu 0.2 ist 7205759403792794 x 2⁻⁵⁵; addieren sie zusammen ergibt 10808639105689191 x 2⁻⁵⁵oder ein exakter Dezimalwert von 0.3000000000000000444089209850062616169452667236328125. Fließkommazahlen sind in der Regel zur Anzeige gerundet.


48
2018-02-25 21:42



Gleitkomma-Rundungsfehler Von Was jeder Informatiker über Gleitkommaarithmetik wissen sollte:

Das Zusammendrücken unendlich vieler reeller Zahlen zu einer endlichen Anzahl von Bits erfordert eine ungefähre Darstellung. Obwohl es unendlich viele Ganzzahlen gibt, kann in den meisten Programmen das Ergebnis von Ganzzahlberechnungen in 32 Bits gespeichert werden. Im Gegensatz dazu ergeben die meisten Berechnungen mit reellen Zahlen, die irgendeine feste Anzahl von Bits aufweisen, Größen, die unter Verwendung dieser vielen Bits nicht exakt dargestellt werden können. Daher muss das Ergebnis einer Gleitkommaberechnung oft gerundet werden, um wieder in seine endliche Darstellung zu passen. Dieser Rundungsfehler ist das charakteristische Merkmal der Gleitkommaberechnung.


40
2017-12-26 06:51



Meine Problemumgehung:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

Präzision bezieht sich auf die Anzahl der Stellen, die Sie nach dem Dezimalpunkt während des Hinzufügens beibehalten möchten.


29
2017-10-05 18:39



Es wurden viele gute Antworten gepostet, aber ich möchte noch eins hinzufügen.

Nicht alle Nummern können über dargestellt werden schwimmt/Doppel Zum Beispiel wird die Zahl "0,2" als "0,200000003" mit einfacher Genauigkeit im IEEE754-Gleitkomma-Standard dargestellt.

Modell für Shop reelle Zahlen unter der Motorhaube stellen Gleitkommazahlen dar

enter image description here

Auch wenn Sie tippen können 0.2 leicht, FLT_RADIX und DBL_RADIX ist 2; nicht 10 für einen Computer mit FPU, der "IEEE Standard für binäre Gleitkommaarithmetik (ISO / IEEE Std 754-1985)" verwendet.

Es ist also ein bisschen schwierig, solche Zahlen genau darzustellen. Auch wenn Sie diese Variable explizit ohne Zwischenrechnung angeben.


26
2018-02-02 23:49