Jak sformatować liczbę zmiennoprzecinkową w javascript?

427

W JavaScripcie, jak przekonwertować zmiennoprzecinkowe na ciąg, w jaki sposób mogę uzyskać tylko 2 cyfry po przecinku? Na przykład 0,34 zamiast 0,3445434.

jww
źródło
16
Tylko wybieranie nitów: czy chcesz odciąć wszystkie oprócz dwóch pierwszych cyfr, czy też chcesz zaokrąglić do dwóch cyfr?
xtofl

Odpowiedzi:

232
var result = Math.round(original*100)/100;

Szczegóły , w przypadku gdy kod nie jest zrozumiały.

edytuj: ... lub po prostu użyj toFixed, jak zaproponował Tim Büthe . Zapomniałem tego, dziękuję (i głosowanie) za przypomnienie :)

kkyy
źródło
1
Używałem tego w bibliotece „Highchart”, gdzie nie akceptuje wartości ciągów, dlatego toFixed nie działało dla mnie, Math.round rozwiązał mój problem, dzięki
Swapnil Chincholkar
4
toFixed()naśladuje to, co printf()robi coś w C. Jednak toFixed()i Math.round()zaokrągla inaczej. W takim przypadku toFixed()będzie miał taki sam efekt Math.floor(), jaki miałbyś (zakładając, że wcześniej pomnożono oryginał przez 10 ^ n i dzwonisz za toFixed()pomocą n cyfr). „Poprawność” odpowiedzi jest bardzo zależna od tego, czego chce tu PO, i oba są „poprawne” na swój sposób.
DDPWNAGE
838

Istnieją funkcje zaokrąglania liczb. Na przykład:

var x = 5.0364342423;
print(x.toFixed(2));

wydrukuje 5.04.

EDYTOWAĆ: Skrzypce

Tim Büthe
źródło
7
Odradzam
70
Uważaj na to, toFixed () zwraca ciąg: var x = 5.036432346; var y = x.toFixed(2) + 100; ybędzie równy"5.03100"
Vlad
5
Pamiętaj, że (1e100) .toFixed (2) === "1e + 100"
qbolec
7
Uważaj też na niespójne zaokrąglanie: (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2)
Skippy le Grand Gourou,
3
Jeśli szukasz odpowiednika toFixed, ale ze spójnym zaokrąglaniem, użyj toLocaleString: (0.345) .toLocaleString ('en-EN', {minimumFractionDigits: 2, maximumFractionDigits: 2})
fred727
185

Zachowaj ostrożność, używając toFixed():

Po pierwsze, zaokrąglanie liczby odbywa się za pomocą binarnej reprezentacji liczby, co może prowadzić do nieoczekiwanego zachowania. Na przykład

(0.595).toFixed(2) === '0.59'

zamiast '0.6'.

Po drugie, występuje błąd IE toFixed(). W IE (przynajmniej do wersji 7 nie sprawdzono IE8) spełnione są następujące warunki:

(0.9).toFixed(0) === '0'

Dobrym pomysłem może być skorzystanie z sugestii kkyy lub użycie niestandardowej toFixed()funkcji, np

function toFixed(value, precision) {
    var power = Math.pow(10, precision || 0);
    return String(Math.round(value * power) / power);
}
Christoph
źródło
Tak, może to być bardzo ważne przy tworzeniu kodu do przewidywania cen. Dzięki! +1
Fabio Milheiro,
10
Sugerowałbym dodanie .toFixed()metody natywnej do wartości zwracanej, która doda wymaganą dokładność, np .: return (Math.round(value * power) / power).toFixed(precision);a także zwróci wartość jako ciąg znaków. W przeciwnym razie precyzja 20 jest ignorowana dla mniejszych miejsc po przecinku
William Joss Crowcroft
3
Jedna uwaga dotycząca toFixed: zauważ, że zwiększenie precyzji może przynieść nieoczekiwane rezultaty: (1.2).toFixed(16) === "1.2000000000000000"podczas gdy (1.2).toFixed(17) === "1.19999999999999996"(w Firefox / Chrome; w IE8 ta ostatnia nie działa z powodu niższej precyzji, którą IE8 może zaoferować wewnętrznie).
jakub.g
2
Należy pamiętać, że nawet (0.598).toFixed(2)nie produkuje 0.6. Produkuje 0.60:)
qbolec
1
Także strzeżcie inconstistent zaokrąglenia: (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2).
Skippy le Grand Gourou,
36

Kolejnym problemem, o którym należy pamiętać, jest to, że toFixed()może generować niepotrzebne zera na końcu liczby. Na przykład:

var x=(23-7.37)
x
15.629999999999999
x.toFixed(6)
"15.630000"

Chodzi o to, aby wyczyścić dane wyjściowe za pomocą RegExp:

function humanize(x){
  return x.toFixed(6).replace(/\.?0*$/,'');
}

RegExpDopasowuje zer (i opcjonalnie przecinek), aby upewnić się, że wygląda dobrze dla liczb całkowitych, jak również.

humanize(23-7.37)
"15.63"
humanize(1200)
"1200"
humanize(1200.03)
"1200.03"
humanize(3/4)
"0.75"
humanize(4/3)
"1.333333"
qbolec
źródło
Number.prototype.minFixed = function (decimals) {return this.toFixed (decimals) .replace (/ \.? 0 * $ /, ""); }
Luc Bloom
Należy to zaakceptować, końcowe zera są denerwujące, szukałem właśnie tego rozwiązania, gj.
exoslav
1
Końcowe zera mogą być denerwujące, ale dokładnie to obiecuje nazwa metody „toFixed”;)
ksadowski
11
var x = 0.3445434
x = Math.round (x*100) / 100 // this will make nice rounding
Ilya Birman
źródło
Math.round (1.015 * 100) / 100 daje 1.01, podczas gdy spodziewalibyśmy się, że będzie to 1.02?
gaurav5430,
6

Problem polega na tym, że wszystkie te rozwiązania zmieniają się przy użyciu mnożników. Rozwiązania zarówno Kkyy, jak i Christopha są niestety błędne.

Sprawdź kod pod kątem liczby 551.175 z 2 miejscami po przecinku - zaokrągli on do 551.17, a powinien mieć 551,18 ! Ale jeśli testujesz na ex. 451,175 wszystko będzie dobrze - 451,18. Dlatego trudno jest zauważyć ten błąd na pierwszy rzut oka.

Problem polega na pomnożeniu: spróbuj 551,175 * 100 = 55117.49999999999 (ups!)

Więc moim pomysłem jest potraktowanie go za pomocą toFixed () przed użyciem Math.round ();

function roundFix(number, precision)
{
    var multi = Math.pow(10, precision);
    return Math.round( (number * multi).toFixed(precision + 1) ) / multi;
}
ArteQ
źródło
1
To jest problem z arytmetyką w js: (551,175 * 10 * 10)! == (551,175 * 100). Musisz użyć przyrostu dziesiętnego, aby przesunąć przecinek, aby uzyskać nierealne wyniki dziesiętne.
Kir Kanos
+1 za zauważenie tego, ale toFixedto również wpływa - (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2)… Bez względu na zastosowaną metodę, lepiej dodać epsilon przed zaokrągleniem.
Skippy le Grand Gourou,
5

Wydaje mi się, że kluczem jest prawidłowe zaokrąglenie w górę, a następnie konwersja do String.

function roundOf(n, p) {
    const n1 = n * Math.pow(10, p + 1);
    const n2 = Math.floor(n1 / 10);
    if (n1 >= (n2 * 10 + 5)) {
        return (n2 + 1) / Math.pow(10, p);
    }
    return n2 / Math.pow(10, p);
}

// All edge cases listed in this thread
roundOf(95.345, 2); // 95.35
roundOf(95.344, 2); // 95.34
roundOf(5.0364342423, 2); // 5.04
roundOf(0.595, 2); // 0.60
roundOf(0.335, 2); // 0.34
roundOf(0.345, 2); // 0.35
roundOf(551.175, 2); // 551.18
roundOf(0.3445434, 2); // 0.34

Teraz możesz bezpiecznie sformatować tę wartość za pomocą toFixed (p). W przypadku konkretnego przypadku:

roundOf(0.3445434, 2).toFixed(2); // 0.34
Steven
źródło
3

Jeśli chcesz ciąg bez rundy, możesz użyć tego RegEx (może nie jest to najbardziej wydajny sposób ... ale jest naprawdę łatwy)

(2.34567778).toString().match(/\d+\.\d{2}/)[0]
// '2.34'
Pablo Andres Diaz Mazzaro
źródło
1
function trimNumber(num, len) {
  const modulu_one = 1;
  const start_numbers_float=2;
  var int_part = Math.trunc(num);
  var float_part = String(num % modulu_one);
      float_part = float_part.slice(start_numbers_float, start_numbers_float+len);
  return int_part+'.'+float_part;
}
ofir_aghai
źródło
świetna odpowiedź, z wyjątkiem tego, że brakuje średników (nie, w es6 średniki nie są przestarzałe i nadal musimy ich używać w niektórych przypadkach). Musiałem też edytować ostatni wiersz: return float_part ? int_part+'.'+float_part : int_part;inaczej jeśli przeszedł całkowitą, to zwrócony liczby z kropką na końcu (na przykład wejście: 2100, moc: 2100.)
Kuba Simonovsky
0

Może chcesz też separator dziesiętny? Oto funkcja, którą właśnie stworzyłem:

function formatFloat(num,casasDec,sepDecimal,sepMilhar) {
    if (num < 0)
    {
        num = -num;
        sinal = -1;
    } else
        sinal = 1;
    var resposta = "";
    var part = "";
    if (num != Math.floor(num)) // decimal values present
    {
        part = Math.round((num-Math.floor(num))*Math.pow(10,casasDec)).toString(); // transforms decimal part into integer (rounded)
        while (part.length < casasDec)
            part = '0'+part;
        if (casasDec > 0)
        {
            resposta = sepDecimal+part;
            num = Math.floor(num);
        } else
            num = Math.round(num);
    } // end of decimal part
    while (num > 0) // integer part
    {
        part = (num - Math.floor(num/1000)*1000).toString(); // part = three less significant digits
        num = Math.floor(num/1000);
        if (num > 0)
            while (part.length < 3) // 123.023.123  if sepMilhar = '.'
                part = '0'+part; // 023
        resposta = part+resposta;
        if (num > 0)
            resposta = sepMilhar+resposta;
    }
    if (sinal < 0)
        resposta = '-'+resposta;
    return resposta;
}
Rodrigo
źródło
0

Nie ma sposobu, aby uniknąć niespójnego zaokrąglania cen z wartością x.xx5 jako wartością rzeczywistą za pomocą mnożenia lub dzielenia. Jeśli musisz obliczyć prawidłowe ceny po stronie klienta, powinieneś zachować wszystkie kwoty w centach. Wynika to z natury wewnętrznej reprezentacji wartości liczbowych w JavaScript. Zauważ, że Excel ma te same problemy, więc większość ludzi nie zauważy małych błędów spowodowanych tym zjawiskiem. Jednak błędy mogą się kumulować, ilekroć dodasz dużo obliczonych wartości, istnieje cała teoria obejmująca kolejność obliczeń i inne metody, aby zminimalizować błąd w wyniku końcowym. Aby podkreślić problemy z wartościami dziesiętnymi, należy pamiętać, że 0,1 + 0,2 nie jest dokładnie równe 0,3 w JavaScript, podczas gdy 1 + 2 jest równe 3.

Dony
źródło
rozwiązaniem byłoby oddzielenie całej części i części dziesiętnej i reprezentowanie ich jako liczb całkowitych w bazie 10, zamiast używania liczb zmiennoprzecinkowych, tutaj działa bez problemu dla prettyPrint, ale ogólnie musisz wybrać między bazą a inną, reprezentacja dla liczby rzeczywiste i inne, każdy ma swoje problemy
zjeżdża
„Program Excel ma te same problemy”. źródło ?
gaurav5430,
0
/** don't spend 5 minutes, use my code **/
function prettyFloat(x,nbDec) { 
    if (!nbDec) nbDec = 100;
    var a = Math.abs(x);
    var e = Math.floor(a);
    var d = Math.round((a-e)*nbDec); if (d == nbDec) { d=0; e++; }
    var signStr = (x<0) ? "-" : " ";
    var decStr = d.toString(); var tmp = 10; while(tmp<nbDec && d*tmp < nbDec) {decStr = "0"+decStr; tmp*=10;}
    var eStr = e.toString();
    return signStr+eStr+"."+decStr;
}

prettyFloat(0);      //  "0.00"
prettyFloat(-1);     // "-1.00"
prettyFloat(-0.999); // "-1.00"
prettyFloat(0.5);    //  "0.50"
ponownie się łączy
źródło
0

Używam tego kodu do formatowania liczb zmiennoprzecinkowych. Opiera się na, toPrecision()ale usuwa niepotrzebne zera. Z zadowoleniem przyjmuję sugestie dotyczące uproszczenia wyrażenia regularnego.

function round(x, n) {
    var exp = Math.pow(10, n);
    return Math.floor(x*exp + 0.5)/exp;
}

Przykład użycia:

function test(x, n, d) {
    var rounded = rnd(x, d);
    var result = rounded.toPrecision(n);
    result = result.replace(/\.?0*$/, '');
    result = result.replace(/\.?0*e/, 'e');
    result = result.replace('e+', 'e');
    return result;  
}

document.write(test(1.2000e45, 3, 2) + '=' + '1.2e45' + '<br>');
document.write(test(1.2000e+45, 3, 2) + '=' + '1.2e45' + '<br>');
document.write(test(1.2340e45, 3, 2) + '=' + '1.23e45' + '<br>');
document.write(test(1.2350e45, 3, 2) + '=' + '1.24e45' + '<br>');
document.write(test(1.0000, 3, 2) + '=' + '1' + '<br>');
document.write(test(1.0100, 3, 2) + '=' + '1.01' + '<br>');
document.write(test(1.2340, 4, 2) + '=' + '1.23' + '<br>');
document.write(test(1.2350, 4, 2) + '=' + '1.24' + '<br>');
znak
źródło