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?

hoju
quelle
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.)

Guffa
quelle
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...

David
quelle
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.

Elias Zamaria
quelle
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

de.la.ru.
quelle
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 ()

JohnnyBizzle
quelle
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;
}
Christoph
quelle
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

KRISHNA TEJA
quelle
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);
}
George Birbilis
quelle
Ü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;
}
Juan Carrey
quelle
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