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

0voto

Christiyan Punkte 319

Mir gefällt der Ansatz mit dem Korrekturfaktor, und hier sind meine verkürzten Entscheidungen für die beiden Standards ES6 und ES5. Der Vorteil gegenüber dem toFixed Methode ist, dass sie keine unnötigen Nullen am Ende der Zahl lässt, wenn wir auf Hunderter runden wollen, aber die Ergebniszahl ist eine Zehntelzahl:

ES6-Variante:

// .1 + .2
((a,b,crr) => (a*crr + b*crr)/crr)(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
((a,b,crr) => a*crr*b/crr)(.1,.2,100);//0.02

ES5-Variante:

// .1 + .2
(function(a,b,crr){ return (a*crr + b*crr)/crr; })(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
(function(a,b,crr){ return a*crr*b/crr; })(.1,.2,100);//0.02

0voto

Biró Dani Punkte 344

Wenn Sie Gleitkommaberechnungen mit beliebiger Genauigkeit durchführen müssen, können Sie meine NPM-Bibliothek namens gmp-wasm die auf GMP + MPFR-Bibliotheken basiert. Sie können problemlos jede gewünschte Genauigkeit einstellen und das Ergebnis mit fester Genauigkeit zurückgeben.

<script src="https://cdn.jsdelivr.net/npm/gmp-wasm"></script>
<script>
  gmp.init().then(({ getContext }) => {
    const ctx = getContext({ precisionBits: 100 });
    const result = ctx.Float('0.1').mul(ctx.Float('0.2'));
    document.write(`0.1 * 0.2 = ` + result.toFixed(2));
    ctx.destroy();
  });
</script>

0voto

Leslie Wong Punkte 61

Diese npm-Bibliothek ist gebaut, um dieses Problem zu lösen, von meinem eigenen Anwendungsfall, und wurde in großem Maßstab Produktion eingesetzt. Ich hoffe, dass sie für andere hilfreich ist.

npm i jsbi-Rechner

Es basiert auf dem GoogleChromeLabs/jsbi Projekt und verwendet stringifizierte Ausdrücke, um beliebige rationale Berechnungen durchzuführen, ie11-kompatibel.

Verwendung für Browser:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Jsbi-calculator Test</title>
    <script src="https://cdn.jsdelivr.net/npm/jsbi-calculator/dist/jsbi-calculator-umd.js"></script>
  </head>
  <body></body>
  <script type="text/javascript">
    const expressionOne = "((10 * (24 / ((9 + 3) * (-2)))) + 17) + 5.2";
    const resultOne = JBC.calculator(expressionOne);
    console.log(resultOne);
    // -> '12.2'

    const userAgent = navigator.userAgent;
    const isIE11 =
      userAgent.indexOf("Trident") > -1 && userAgent.indexOf("rv:11.0") > -1;
    let max;
    // MAX_SAFE_INTEGER not available in IE11
    max = isIE11 ? "9007199254740991" : String(Number.MAX_SAFE_INTEGER);

    console.log(max);
    // -> '9007199254740991'
    const expressionTwo = max + " + 2.2";
    const resultTwo = JBC.calculator(expressionTwo);
    console.log(resultTwo);
    // -> '9007199254740993.2'
  </script>
</html>

-1voto

John Punkte 164

Ich habe dies auf der Grundlage der Antwort von @SheetJs zusammengestellt, die mir gefallen hat:

  getCorrectionFactor(numberToCheck: number): number {
    var correctionFactor: number = 1;

    if (!Number.isInteger(numberToCheck)) {
      while (!Number.isInteger(numberToCheck)) {
        correctionFactor *= 10;
        numberToCheck *= correctionFactor;
      }
    }

    return correctionFactor;
  }

-1voto

TecHunter Punkte 5971

Ich habe hier eine Abhilfe. Einfach mit 10E^x zu multiplizieren, funktioniert zum Beispiel nicht mit 1,1.

function sum(a,b){
    var tabA = (a + "").split(".");
    var tabB = (b + "").split(".");
    decA = tabA.length>1?tabA[1].length:0;
    decB = tabB.length>1?tabB[1].length:0;
    a = (tabA[0]+tabA[1])*1.0;
    b = (tabB[0]+tabB[1])*1.0;
    var diff = decA-decB;
    if(diff >0){
        //a has more decimals than b
        b=b*Math.pow(10,diff);
        return (a+b)/Math.pow(10,decA);
    }else if (diff<0){
        //a has more decimals than b
        a=a*Math.pow(10,-diff);
                return (a+b)/Math.pow(10,decB);
    }else{
        return (a+b)/Math.pow(10,decA);
    }       
}

beängstigend, aber es funktioniert :)

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