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?


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ń).


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


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”.


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


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 ()


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

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


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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.