Javascript: Formatieren einer gerundeten Zahl auf N Dezimalstellen


104

In JavaScript ist die typische Methode zum Runden einer Zahl auf N Dezimalstellen wie folgt:

function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

Dieser Ansatz wird jedoch auf maximal N Dezimalstellen gerundet, während ich immer auf N Dezimalstellen runden möchte . Zum Beispiel würde "2.0" auf "2" gerundet.

Irgendwelche Ideen?


9
Normalerweise können Sie toFixed()( developer.mozilla.org/En/Core_JavaScript_1.5_Reference/… ) verwenden, aber im IE ist es fehlerhaft : stackoverflow.com/questions/661562/… ; Sie müssen Ihre eigene Version schreiben ...
Christoph

1
@hoju - möglicherweise akzeptierte Antwort ändern - Davids Antwort ist für IE8 + korrekt, während die akzeptierte Antwort in allen Browsern einige schwerwiegende Fehler aufweist.
Robocat

@robocat: Ist das dein Ernst?
Guffa

Antworten:


25

Das ist kein Rundungsproblem, das ist ein Anzeigeproblem. Eine Zahl enthält keine Informationen zu signifikanten Ziffern. Der Wert 2 entspricht 2.0000000000000. Wenn Sie den gerundeten Wert in eine Zeichenfolge umwandeln, wird eine bestimmte Anzahl von Ziffern angezeigt.

Sie können einfach Nullen nach der Zahl einfügen, etwa:

var s = number.toString();
if (s.indexOf('.') == -1) s += '.';
while (s.length < s.indexOf('.') + 4) s += '0';

(Beachten Sie, dass dies voraussetzt, dass die regionalen Einstellungen des Clients Punkt als Dezimaltrennzeichen verwenden. Der Code benötigt etwas mehr Arbeit, um für andere Einstellungen zu funktionieren.)


3
toFixed ist fehlerhaft. Zum Beispiel die Dezimalzahl: 1.02449999998, wenn Sie dec.toFixed (4) => 1.0244 ausführen. Es sollte stattdessen 1.0245 gewesen sein.
Bat_Programmer

2
@deepeshk Aber was wäre das Problem beim Auffüllen von toFixed()Dezimalstellen am Ende nach dem Runden?
Pauloya

10
@deepeshk in welchem ​​Browser? Habe es gerade in Chrome 17 versucht und 1.02449999998.toFixed(4)kehre korrekt zurück 1.0245.
Matt Ball

3
@ MarkTomlin: Dann scheint es, dass Sie eine Zeichenfolge anstelle einer Zahl haben.
Guffa

1
.toFixed () funktioniert hervorragend in modernen Browsern (ich habe Chrome, Firefox, IE11, IE8 getestet). @Bat_Programmer - Bitte klären Sie, welche Browser Ihrer Meinung nach diesen Fehler haben.
Robocat

242

Ich denke, dass es einen einfacheren Ansatz für alle hier gegebenen gibt und die Methode Number.toFixed()bereits in JavaScript implementiert ist.

schreibe einfach:

var myNumber = 2;

myNumber.toFixed(2); //returns "2.00"
myNumber.toFixed(1); //returns "2.0"

etc...


Wo wird erwähnt, hoju? Ich habe andere Antworten überprüft und niemanden gefunden, der berichtet, dass die Funktion toFixed fehlerhaft ist. Vielen Dank
David

@ David: Das wird zum Beispiel in einem Kommentar zur Antwort erwähnt.
Guffa

13
Vorsicht: toFixed()Gibt eine Zeichenfolge zurück.
Jordan Arseno

2
@JordanArseno dies kann mit parseInt (myNumber.toFixed (intVar)) behoben werden; um einen ganzzahligen Wert oder parseFloat (myNumber.toFixed (floatVar)) zurückzugeben; um einen Gleitkommawert zurückzugeben, wenn der Benutzer Dezimalstellen hat.
Stephen Romero

50

Ich habe einen Weg gefunden. Dies ist Christophs Code mit einem Fix:

function toFixed(value, precision) {
    var precision = precision || 0,
        power = Math.pow(10, precision),
        absValue = Math.abs(Math.round(value * power)),
        result = (value < 0 ? '-' : '') + String(Math.floor(absValue / power));

    if (precision > 0) {
        var fraction = String(absValue % power),
            padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
        result += '.' + padding + fraction;
    }
    return result;
}

Lesen Sie hier die Details zum Wiederholen eines Zeichens mit einem Array-Konstruktor , wenn Sie neugierig sind, warum ich "+ 1" hinzugefügt habe.


Die Übergabe eines Wertes von 708.333333333333333 mit einer Genauigkeit von 2 oder 3 führt für mich zu einem Rückgabewert von 708.00. Ich benötige dies für 2 Dezimalstellen, in Chrome und IE 9 .toFixed (2) erfüllte meine Anforderungen.
Alan

Dies ist keine übliche Methode, z. B. toFixed (16.775, 2) gibt 16.77 zurück. Konvertieren Sie die Nummer in einen String und konvertieren Sie dann nur so.
Hiway

2
Bei dieser Methode gibt es einen Fehler: toFixed (-0.1111, 2) gibt 0,11 zurück, dh das negative Vorzeichen geht verloren.
Matt

Funktioniert super, außer dass ich am Ende parseFloat (Ergebnis) hinzugefügt habe. Eine Bearbeitung wert.
Artur Barseghyan

16

Es gibt immer einen besseren Weg, um Dinge zu tun.

var number = 51.93999999999761;

Ich möchte eine Genauigkeit von vier Stellen erhalten: 51,94

mach einfach:

number.toPrecision(4);

Das Ergebnis ist: 51,94


2
Und was ist, wenn Sie 100 hinzufügen? Müssen Sie es in number.toPrecision (5) ändern?
JohnnyBizzle

2
Ja. 31.939383.toPrecision (4)> "31.94" / 131.939383.toPrecision (4)> "131.9"
ReSpawN

6

PHP-ähnliche Rundungsmethode

Mit dem folgenden Code können Sie Ihre eigene Version von Math.round zu Ihrem eigenen Namespace hinzufügen, der einen Präzisionsparameter verwendet. Im Gegensatz zur Dezimalrundung im obigen Beispiel führt dies keine Konvertierung in und von Zeichenfolgen durch, und der Genauigkeitsparameter funktioniert genauso wie PHP und Excel, wobei eine positive 1 auf 1 Dezimalstelle und -1 auf die Zehner gerundet wird.

var myNamespace = {};
myNamespace.round = function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
};

myNamespace.round(1234.5678, 1); // 1234.6
myNamespace.round(1234.5678, -1); // 1230

von Mozilla Developer Referenz für Math.round ()


2

Hoffentlich funktionierender Code (hat nicht viel getestet):

function toFixed(value, precision) {
    var precision = precision || 0,
        neg = value < 0,
        power = Math.pow(10, precision),
        value = Math.round(value * power),
        integral = String((neg ? Math.ceil : Math.floor)(value / power)),
        fraction = String((neg ? -value : value) % power),
        padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');

    return precision ? integral + '.' +  padding + fraction : integral;
}

Ihr Code hat einen Fehler. Ich habe versucht, (2.01, 4) zu fixieren und habe ein Ergebnis von "2.100" erhalten. Wenn ich jemals einen Weg finde, das Problem zu beheben, werde ich es als Antwort auf diese Frage veröffentlichen.
Elias Zamaria

@ mikez302: Die Auffüllberechnung wurde um eins verschoben. sollte jetzt funktionieren, aber zögern Sie nicht, mich erneut zu nerven, wenn es noch kaputt ist ...
Christoph

Sehr eigenartig. Wenn ich diese Funktion bei geöffneter Firebug-Konsole in Firefox 17 ausführe, friert sie den gesamten Browser ein, als wäre js in einer Endlosschleife gefangen. Auch wenn ich die Ausgabe nicht console.log. Wenn ich Firebug nicht aktiviert habe, tritt der Fehler nicht auf.
SublymeRick

1
Update, ich habe es in Chrome ausgeführt und ich bekomme: Uncaught RangeError: Maximale Aufrufstapelgröße in Bezug auf den Funktionsaufruf toFixed überschritten.
SublymeRick

@ SublymeRick: Ich habe keine Ahnung, warum dies passiert; Schuss im Dunkeln: versuchen Sie, die Funktion
Christoph

2

Ich denke unten Funktion kann helfen

function roundOff(value,round) {
   return (parseInt(value * (10 ** (round + 1))) - parseInt(value * (10 ** round)) * 10) > 4 ? (((parseFloat(parseInt((value + parseFloat(1 / (10 ** round))) * (10 ** round))))) / (10 ** round)) : (parseFloat(parseInt(value * (10 ** round))) / ( 10 ** round));
}

Verwendung: roundOff(600.23458,2);wird zurückkehren600.23


2

Dies funktioniert zum Runden auf N Ziffern (wenn Sie nur auf N Ziffern kürzen möchten, entfernen Sie den Aufruf Math.round und verwenden Sie den Aufruf Math.trunc):

function roundN(value, digits) {
   var tenToN = 10 ** digits;
   return /*Math.trunc*/(Math.round(value * tenToN)) / tenToN;
}

Ich musste in der Vergangenheit bei Java auf diese Logik zurückgreifen, als ich E-Slate-Komponenten für die Datenmanipulation verfasste . Das liegt daran, dass ich herausgefunden hatte, dass das mehrfache Hinzufügen von 0,1 zu 0 zu einem unerwartet langen Dezimalteil führen würde (dies liegt an der Gleitkomma-Arithmetik).

Ein Benutzerkommentar unter Formatnummer, bei dem immer 2 Dezimalstellen angezeigt werden, ruft diese Technikskalierung auf.

Einige erwähnen, dass es Fälle gibt, die nicht wie erwartet runden, und unter http://www.jacklmoore.com/notes/rounding-in-javascript/ wird stattdessen Folgendes vorgeschlagen:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}

Übrigens sind POSITS vielversprechend für zukünftige H / W-Architekturen: nextplatform.com/2019/07/08/…
George Birbilis

-2

Wenn Sie sich nicht wirklich für das Runden interessieren, fügen Sie einfach ein toFixed (x) hinzu und entfernen Sie gegebenenfalls nachfolgende 0en und den Punkt. Es ist keine schnelle Lösung.

function format(value, decimals) {
    if (value) {
        value = value.toFixed(decimals);            
    } else {
        value = "0";
    }
    if (value.indexOf(".") < 0) { value += "."; }
    var dotIdx = value.indexOf(".");
    while (value.length - dotIdx <= decimals) { value += "0"; } // add 0's

    return value;
}

Ich habe versucht format(1.2, 5)und bekommen 1.2, während ich erwartet hatte 1.20000.
Elias Zamaria

Sorry @EliasZamaria, habe es zuerst nicht verstanden. Ich habe den Beitrag bearbeitet. Beachten Sie nur, dass "0.0000" zurückgegeben wird, wenn der Wert nicht definiert ist.
Juan Carrey
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.