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

1voto

Antonio Max Punkte 8299

Das funktioniert bei mir:

function round_up( value, precision ) { 
    var pow = Math.pow ( 10, precision ); 
    return ( Math.ceil ( pow * value ) + Math.ceil ( pow * value - Math.ceil ( pow * value ) ) ) / pow; 
}

round_up(341.536, 2); // 341.54

1voto

Novarender Punkte 37

Normalerweise verwende ich etwas in der Art.

function pf(n) {
    return Math.round(n * 1e15) / 1e15;
}

Ich behaupte nicht, dass dies in irgendeiner Weise optimal ist, aber ich mag es wegen seiner Einfachheit. Es rundet die Zahl auf 15 Dezimalstellen oder so ab. Ich habe nicht erlebt, dass es ungenaue Floats zurückgibt, obwohl es seltsam ist, dass es das getan hat, wenn ich * 1e-15 am Ende, aber nicht mit dieser Methode.

Diese Lösung eignet sich eher für den gelegentlichen Gebrauch - und nicht für präzise mathematische Anwendungen -, bei denen Präzisionsfehler den Code durcheinander bringen.

1voto

Himadri Punkte 8042

Verwenden Sie

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);

0voto

ShortFuse Punkte 5071

Wenn Sie nicht jedes Mal an den Aufruf von Funktionen denken wollen, können Sie eine Klasse erstellen, die die Konvertierung für Sie übernimmt.

class Decimal {
  constructor(value = 0, scale = 4) {
    this.intervalValue = value;
    this.scale = scale;
  }

  get value() {
    return this.intervalValue;
  }

  set value(value) {
    this.intervalValue = Decimal.toDecimal(value, this.scale);
  }

  static toDecimal(val, scale) {
    const factor = 10 ** scale;
    return Math.round(val * factor) / factor;
  }
}

Verwendung:

const d = new Decimal(0, 4);
d.value = 0.1 + 0.2;              // 0.3
d.value = 0.3 - 0.2;              // 0.1
d.value = 0.1 + 0.2 - 0.3;        // 0
d.value = 5.551115123125783e-17;  // 0
d.value = 1 / 9;                  // 0.1111

Natürlich gibt es beim Umgang mit Decimal einige Vorbehalte:

d.value = 1/3 + 1/3 + 1/3;   // 1
d.value -= 1/3;              // 0.6667
d.value -= 1/3;              // 0.3334
d.value -= 1/3;              // 0.0001

Idealerweise verwenden Sie eine hohe Skala (z. B. 12) und konvertieren sie dann herunter, wenn Sie sie präsentieren oder irgendwo aufbewahren müssen. Ich persönlich habe mit der Erstellung eines UInt8Array experimentiert und versucht, einen Präzisionswert zu erstellen (ähnlich wie der SQL Decimal-Typ), aber da Javascript nicht zulässt, dass Sie Operatoren überladen, wird es einfach ein bisschen mühsam nicht in der Lage, grundlegende mathematische Operatoren zu verwenden ( + , - , / , * ) und stattdessen Funktionen zu verwenden wie add() , substract() , mult() . Für meine Bedürfnisse ist es das nicht wert.

Wenn Sie jedoch diese Präzision benötigen und bereit sind, die Verwendung von Funktionen für die Mathematik zu ertragen, dann empfehle ich die dezimal.js Bibliothek.

0voto

Ste Punkte 1213

Ich habe die gleiche Lösung gesucht und herausgefunden, dass, wenn man eine ganze Zahl wie 1 hinzufügt und diese auswertet console.log(0.1 * 0.2 + 1); . Dies führt zu 1.02 . Dies kann zum Runden des ursprünglichen x auf den korrekten Betrag.

Sobald die Länge der Dezimalstellen 2 in Ihrem Beispiel abgerufen wird, können wir es dann mit dem toFixed() Funktion zum Runden des ursprünglichen x Variable korrekt.

Sehen Sie sich im Code an, was diese Funktion in den kommentierten Abschnitten tut.

var myX= 0.2 * 0.1;
var myX= 42.5-42.65;
var myX= 123+333+3.33+33333.3+333+333;

console.log(myX);
// Outputs (example 1): 0.020000000000000004
// Outputs (example 2): -0.14999999999999858
// Outputs (example 3): 34458.630000000005
// Wrong

function fixRoundingError(x) {
// 1. Rounds to the nearest 10
//    Also adds 1 to round of the value in some other cases, original x variable will be used later on to get the corrected result.
var xRound = eval(x.toFixed(10)) + 1;
// 2. Using regular expression, remove all digits up until the decimal place of the corrected equation is evaluated..
var xDec = xRound.toString().replace(/\d+\.+/gm,'');
// 3. Gets the length of the decimal places.
var xDecLen = xDec.length;
// 4. Uses the original x variable along with the decimal length to fix the rounding issue.
var x = eval(x).toFixed(xDecLen);
// 5. Evaluate the new x variable to remove any unwanted trailing 0's should there be any.
return eval(x);
}

console.log(fixRoundingError(myX));
// Outputs (example 1): 0.02
// Outputs (example 2): -0.15
// Outputs (example 3): 34458.63
// Correct

Er gibt in jedem Fall, den ich ausprobiert habe, denselben Wert zurück wie der Taschenrechner in Windows und rundet das Ergebnis auch ab, wenn es irgendwelche nachstehenden 0 ist automatisch.

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