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

31voto

Douglas Punkte 34870

Sie suchen einen sprintf Implementierung für JavaScript, so dass Sie Floats mit kleinen Fehlern (da sie im Binärformat gespeichert sind) in einem Format ausgeben können, das Sie erwarten.

Pruebe javascript-sprintf würden Sie es so nennen:

var yourString = sprintf("%.2f", yourNumber);

um Ihre Zahl als Gleitkommazahl mit zwei Dezimalstellen auszudrucken.

Sie können auch Folgendes verwenden Zahl.toFixed() für Anzeigezwecke, wenn Sie nicht noch mehr Dateien nur für die Gleitkommarundung auf eine bestimmte Genauigkeit einbeziehen möchten.

25voto

shawndumas Punkte 1377
var times = function (a, b) {
    return Math.round((a * b) * 100)/100;
};

---oder---

var fpFix = function (n) {
    return Math.round(n * 100)/100;
};

fpFix(0.1*0.2); // -> 0.02

---auch---

var fpArithmetic = function (op, x, y) {
    var n = {
            '*': x * y,
            '-': x - y,
            '+': x + y,
            '/': x / y
        }[op];        

    return Math.round(n * 100)/100;
};

--- wie in ---

fpArithmetic('*', 0.1, 0.2);
// 0.02

fpArithmetic('+', 0.1, 0.2);
// 0.3

fpArithmetic('-', 0.1, 0.2);
// -0.1

fpArithmetic('/', 0.2, 0.1);
// 2

22voto

Softwareddy Punkte 651

Sie können verwenden parseFloat() y toFixed() wenn Sie dieses Problem für eine kleine Operation umgehen wollen:

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;

20voto

Gabriel Punkte 17644

Diese Funktion ermittelt aus der Multiplikation zweier Fließkommazahlen die benötigte Genauigkeit und gibt ein Ergebnis mit der entsprechenden Genauigkeit zurück. Elegant ist das allerdings nicht.

function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  return (a * atens) * (b * btens) / (atens * btens); 
}

15voto

ZXX Punkte 4586

Man muss sich nur entscheiden, wie viele Nachkommastellen man tatsächlich haben möchte - man kann nicht alles haben und auch noch essen :-)

Numerische Fehler häufen sich mit jeder weiteren Operation, und wenn man sie nicht frühzeitig abstellt, werden sie immer größer. Numerische Bibliotheken, die Ergebnisse präsentieren, die sauber aussehen, schneiden einfach die letzten 2 Ziffern bei jedem Schritt ab, numerische Koprozessoren haben aus demselben Grund eine "normale" und eine "volle" Länge. Cuf-offs sind billig für einen Prozessor, aber sehr teuer für Sie in einem Skript (Multiplizieren und Dividieren und die Verwendung von pov(...)). Eine gute Mathe-Lib würde floor(x,n) zur Verfügung stellen, um den Cut-Off für Sie zu erledigen.

Sie sollten also zumindest eine globale Variable/Konstante mit pov(10,n) erstellen - was bedeutet, dass Sie sich für die benötigte Genauigkeit entschieden haben :-) Dann mach das:

Math.floor(x*PREC_LIM)/PREC_LIM  // floor - you are cutting off, not rounding

Sie könnten auch weiter rechnen und erst am Ende abschneiden - vorausgesetzt, dass Sie die Ergebnisse nur anzeigen und nicht mit Wenns arbeiten. Wenn Sie das tun können, dann könnte .toFixed(...) effizienter sein.

Wenn Sie if-s/Vergleiche durchführen und nicht abschneiden wollen, benötigen Sie außerdem eine kleine Konstante, die in der Regel eps heißt und eine Dezimalstelle höher ist als der maximale erwartete Fehler. Nehmen wir an, die letzten beiden Dezimalstellen sind Ihre Abgrenzung - dann hat Ihr eps eine 1 an der drittletzten Stelle (drittniedrigstwertigste Stelle) und Sie können damit vergleichen, ob das Ergebnis im eps-Bereich des erwarteten Fehlers liegt (0,02 -eps < 0,1*0,2 < 0,02 +eps).

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