832 Stimmen

Wie geht man mit der Genauigkeit von Fließkommazahlen in JavaScript um?

Ich habe das folgende Dummy-Testskript:

function test() {
  var x = 0.1 * 0.2;
  document.write(x);
}
test();

So wird das Ergebnis gedruckt 0.020000000000000004 während es einfach drucken sollte 0.02 (wenn Sie Ihren Taschenrechner benutzen). Soweit ich verstanden habe, ist dies auf Fehler in der Genauigkeit der Fließkommamultiplikation zurückzuführen.

Hat jemand eine gute Lösung, damit ich in einem solchen Fall das richtige Ergebnis erhalte? 0.02 ? Ich weiß, es gibt Funktionen wie toFixed oder Rundung wäre eine weitere Möglichkeit, aber ich möchte wirklich die ganze Zahl ohne Kürzung und Rundung gedruckt haben. Ich wollte nur wissen, ob jemand von Ihnen eine schöne, elegante Lösung hat.

Natürlich werde ich sonst auf 10 Stellen oder so aufrunden.

157 Stimmen

Eigentlich liegt der Fehler daran, dass es keine Möglichkeit gibt, die 0.1 in eine endliche binäre Gleitkommazahl.

19 Stimmen

Die meisten Brüche lassen sich nicht mit exakter Genauigkeit in eine Dezimalzahl umwandeln. Eine gute Erklärung finden Sie hier: docs.python.org/release/2.5.1/tut/node16.html

8 Stimmen

5voto

Bernesto Punkte 1095

Elegant, vorhersehbar und wiederverwendbar

Lassen Sie uns das Problem auf eine elegante und wiederverwendbare Weise lösen. Mit den folgenden sieben Zeilen können Sie die gewünschte Fließkommagenauigkeit für jede beliebige Zahl erreichen, indem Sie einfach Folgendes anhängen .decimal an das Ende der Zahl, der Formel oder des eingebauten Math Funktion.

// First extend the native Number object to handle precision. This populates
// the functionality to all math operations.

Object.defineProperty(Number.prototype, "decimal", {
  get: function decimal() {
    Number.precision = "precision" in Number ? Number.precision : 3;
    var f = Math.pow(10, Number.precision);
    return Math.round( this * f ) / f;
  }
});

// Now lets see how it works by adjusting our global precision level and 
// checking our results.

console.log("'1/3 + 1/3 + 1/3 = 1' Right?");
console.log((0.3333 + 0.3333 + 0.3333).decimal == 1); // true

console.log(0.3333.decimal); // 0.333 - A raw 4 digit decimal, trimmed to 3...

Number.precision = 3;
console.log("Precision: 3");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0.001

Number.precision = 2;
console.log("Precision: 2");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 1;
console.log("Precision: 1");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 0;
console.log("Precision: 0");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

Zum Wohl!

5voto

MSN Punkte 51308

Die meisten Dezimalbrüche lassen sich mit binären Fließkommatypen (die ECMAScript zur Darstellung von Fließkommawerten verwendet) nicht exakt darstellen. Es gibt also keine elegante Lösung, es sei denn, Sie verwenden Arithmetiktypen mit beliebiger Genauigkeit oder einen dezimalbasierten Fließkommatyp. Zum Beispiel, die mit Windows gelieferte Taschenrechner-Anwendung verwendet jetzt Arithmetik mit beliebiger Genauigkeit, um dieses Problem zu lösen .

4voto

Marius Punkte 55729

Werfen Sie einen Blick auf Festkomma-Arithmetik . Damit lässt sich Ihr Problem wahrscheinlich lösen, wenn der Zahlenbereich, mit dem Sie arbeiten wollen, klein ist (z. B. eine Währung). Ich würde es auf ein paar Dezimalwerte abrunden, was die einfachste Lösung ist.

4voto

Karlsson Punkte 185

Ich konnte keine Lösung finden, indem ich den eingebauten Number.EPSILON die bei dieser Art von Problemen helfen soll, hier ist also meine Lösung:

function round(value, precision) {
  const power = Math.pow(10, precision)
  return Math.round((value*power)+(Number.EPSILON*power)) / power
}

Dabei wird die bekannte kleinste Differenz zwischen 1 und der kleinsten Fließkommazahl größer als eins verwendet, um die EPSILON Rundungsfehler von nur einem Punkt EPSILON unterhalb der Aufrundungsschwelle.

Die maximale Genauigkeit beträgt 15 for 64bit Gleitkomma und 6 for 32bit Gleitkomma. Ihr Javascript ist wahrscheinlich 64bit.

3voto

mcgeo52 Punkte 51

Ich hatte ein böses Rundungsfehlerproblem mit Mod 3. Manchmal, wenn ich 0 erhalten sollte, bekam ich .000...01. Das ist leicht zu beheben, man muss nur auf <= .01 testen. Aber dann bekam ich manchmal 2,99999999999998. AUTSCH!

BigNumbers löste das Problem, führte aber ein weiteres, etwas ironisches Problem ein. Als ich versuchte, 8,5 in BigNumbers zu laden, wurde mir mitgeteilt, dass es in Wirklichkeit 8,4999 war und mehr als 15 signifikante Ziffern hatte. Das bedeutete, dass BigNumbers sie nicht akzeptieren konnte (ich glaube, ich erwähnte, dass dieses Problem etwas ironisch war).

Einfache Lösung für ein ironisches Problem:

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X