JavaScript: formatowanie zaokrąglonej liczby do N miejsc po przecinku

104

w JavaScript typowym sposobem zaokrągleń liczby do N miejsc dziesiętnych jest:

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

Jednak to podejście zaokrągli maksymalnie do N miejsc dziesiętnych, podczas gdy ja chcę zawsze zaokrąglić do N miejsc dziesiętnych. Na przykład „2,0” zostanie zaokrąglone do „2”.

Jakieś pomysły?

hoju
źródło
9
normalnie możesz użyć toFixed()( developer.mozilla.org/En/Core_JavaScript_1.5_Reference/… ), ale w IE jest to błędne: stackoverflow.com/questions/661562/… ; będziesz musiał napisać własną wersję ...
Christoph,
1
@hoju - być może zmień zaakceptowaną odpowiedź - odpowiedź Davida jest poprawna dla IE8 +, podczas gdy zaakceptowana odpowiedź zawiera poważne błędy we wszystkich przeglądarkach.
robocat
@robocat: Mówisz poważnie?
Guffa

Odpowiedzi:

25

To nie jest problem z zaokrągleniem, to jest problem z wyświetlaniem. Liczba nie zawiera informacji o cyfrach znaczących; wartość 2 jest taka sama jak 2,0000000000000. To wtedy, gdy zamienisz zaokrągloną wartość na ciąg, sprawisz, że wyświetli określoną liczbę cyfr.

Możesz po prostu dodać zera po liczbie, na przykład:

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

(Należy zauważyć, że przy założeniu, że ustawienia regionalne klienta używają kropki jako separatora dziesiętnego, kod wymaga trochę więcej pracy, aby działał dla innych ustawień).

Guffa
źródło
3
toFixed jest błędny ... na przykład dziesiętny: 1.02449999998, jeśli zrobisz dec.toFixed (4) => 1.0244. Zamiast tego powinno być 1,0245.
Bat_Programmer,
2
@deepeshk Ale jaki byłby problem z używaniem toFixed()do uzupełniania ułamków dziesiętnych na końcu, po zaokrągleniu?
pauloya
10
@deepeshk w jakiej przeglądarce? Właśnie wypróbowałem to w Chrome 17 i 1.02449999998.toFixed(4)poprawnie wraca 1.0245.
Matt Ball,
3
@MarkTomlin: W takim razie wydaje się, że zamiast liczby masz ciąg znaków.
Guffa
1
.toFixed () działa znakomicie w nowoczesnych przeglądarkach (testowałem Chrome, Firefox, IE11, IE8). @Bat_Programmer - proszę wyjaśnić, które przeglądarki mają ten błąd.
robocat
243

Myślę, że istnieje prostsze podejście do wszystkich podanych tutaj i jest to metoda Number.toFixed()już zaimplementowana w JavaScript.

po prostu napisz:

var myNumber = 2;

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

itp...

David
źródło
Gdzie jest wymienione, hoju? Przejrzałem inne odpowiedzi i nie znalazłem nikogo, kto zgłosiłby, że funkcja toFixed jest błędna. Dzięki
David,
@David: Jest to wspomniane na przykład w komentarzu do odpowiedzi.
Guffa
13
uwaga: toFixed()zwraca ciąg.
Jordan Arseno
2
@JordanArseno można to naprawić za pomocą parseInt (myNumber.toFixed (intVar)); aby zwrócić wartość całkowitą lub parseFloat (myNumber.toFixed (floatVar)); aby zwrócić wartość zmiennoprzecinkową, jeśli użytkownik ma miejsca dziesiętne.
Stephen Romero
50

Znalazłem drogę. Oto kod Christopha z poprawką:

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;
}

Przeczytaj szczegóły dotyczące powtarzania znaku przy użyciu konstruktora tablicy , jeśli jesteś ciekawy, dlaczego dodałem „+ 1”.

Elias Zamaria
źródło
Przekazanie wartości 708,3333333333333 z dokładnością do 2 lub 3 skutkuje zwróceniem dla mnie wartości 708,00. Potrzebuję tego dla 2 miejsc po przecinku, w Chrome i IE 9 .toFixed (2) spełniło moje potrzeby.
alan
nie jest to typowy sposób, np. toFixed (16.775, 2) zwraca 16.77. Konwertuj liczbę na łańcuch, a następnie przekonwertuj to jedyny sposób.
hiway
2
W metodzie jest błąd: toFixed (-0,1111, 2) zwraca 0,11, czyli znak minus jest tracony.
Matt
Działa świetnie, poza tym na końcu dodałem parseFloat (result). Warto edytować.
Artur Barseghyan
16

Zawsze jest lepszy sposób na robienie rzeczy.

var number = 51.93999999999761;

Chciałbym uzyskać dokładność czterech cyfr: 51,94

po prostu zrób:

number.toPrecision(4);

wynikiem będzie: 51,94

de.la.ru
źródło
2
A co jeśli dodasz 100? Czy musisz zmienić to na number.toPrecision (5)?
JohnnyBizzle
2
Tak. 31.939383.toPrecision (4)> "31.94" / 131.939383.toPrecision (4)> "
131.9
6

Metoda zaokrąglania podobna do PHP

Poniższy kod może służyć do dodawania własnej wersji Math.round do własnej przestrzeni nazw, która przyjmuje parametr precyzji. W przeciwieństwie do zaokrąglania dziesiętnego w powyższym przykładzie, nie powoduje to konwersji do iz ciągów, a parametr precyzji działa tak samo jak PHP i Excel, gdzie dodatnia 1 zaokrągliłaby do 1 miejsca dziesiętnego, a -1 do dziesiątek.

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

z dokumentacji Mozilla Developer dla Math.round ()

JohnnyBizzle
źródło
2

Mam nadzieję, że działający kod (nie wykonał zbyt wielu testów):

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
źródło
Twój kod zawiera błąd. Próbowałem toFixed (2.01, 4) i otrzymałem wynik „2.100”. Jeśli kiedykolwiek znajdę sposób, aby to naprawić, opublikuję to jako odpowiedź na to pytanie.
Elias Zamaria
@ mikez302: obliczenia wypełnienia były wyłączone o jeden; powinien teraz działać, ale nie krępuj się, aby mnie niepokoić, jeśli nadal jest uszkodzony ...
Christoph,
Bardzo dziwny. Kiedy uruchamiam tę funkcję z otwartą konsolą firebuga w przeglądarce Firefox 17, cała przeglądarka zawiesza się, jakby js był uwięziony w nieskończonej pętli. Nawet jeśli nie console.log danych wyjściowych. Jeśli nie mam aktywowanego firebuga, błąd nie występuje.
SublymeRick,
1
Aktualizacja, uruchomiłem go w chrome i otrzymałem: Uncaught RangeError: Przekroczono maksymalny rozmiar stosu wywołań w odniesieniu do wywołania funkcji toFixed.
SublymeRick,
@SublymeRick: Nie mam pojęcia, dlaczego tak się dzieje; strzał w ciemno: spróbuj zmienić nazwę funkcji ...
Christoph
2

Myślę, że poniżej funkcja może pomóc

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));
}

użycie: roundOff(600.23458,2);powróci600.23

KRISHNA TEJA
źródło
2

Działa to w przypadku zaokrąglania do N cyfr (jeśli chcesz tylko skrócić do N cyfr, usuń wywołanie Math.round i użyj Math.trunc):

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

Musiałem uciekać się do takiej logiki w Javie w przeszłości, kiedy tworzyłem komponenty E-Slate do manipulacji danymi . Dzieje się tak, ponieważ odkryłem, że dodając wiele razy 0,1 do 0, otrzymujesz nieoczekiwanie długą część dziesiętną (jest to spowodowane arytmetyką zmiennoprzecinkową).

Komentarz użytkownika pod numerem Format, aby zawsze pokazywać 2 miejsca po przecinku, wywołuje tę technikę skalowania.

Niektórzy wspominają, że istnieją przypadki, które nie zaokrągla się zgodnie z oczekiwaniami, a na http://www.jacklmoore.com/notes/rounding-in-javascript/ sugeruje się zamiast tego:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
George Birbilis
źródło
btw, POSITS są obiecujące dla przyszłych architektur h / w: nextplatform.com/2019/07/08/…
George Birbilis
-2

Jeśli naprawdę nie zależy ci na zaokrąglaniu, po prostu dodaj toFixed (x), a następnie usuń końcowe zera i kropkę, jeśli to konieczne. Nie jest to szybkie rozwiązanie.

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
źródło
Próbowałem format(1.2, 5)i dostałem 1.2, póki się spodziewałem 1.20000.
Elias Zamaria,
Przepraszam @EliasZamaria, na początku tego nie zrozumiałem. Zredagowałem post. Zwróć uwagę, że zwróci "0,0000", gdy wartość jest niezdefiniowana.
Juan Carrey