Jak zaokrąglić liczbę w JavaScript? .toFixed () zwraca ciąg?

176

Czy coś mi umyka?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Dlaczego nie.toFixed()zwróci ciąg?

Chcę zaokrąglić liczbę do 2 cyfr dziesiętnych.

Derek Adair
źródło
7
Ponieważ jest przeznaczony do zwracania ciągu znaków?
kennytm
2
Wydaje mi się to po prostu dziwne. .toFixed () działa tylko na liczbach ... prawda?
Derek Adair
10
Rozumiem, że Math.round () działa zgodnie z oczekiwaniami. Ja po prostu pyta, dlaczego to funkcja, która działa na liczbach zwraca ciąg ...
Derek Adair
3
Osoby żyjące w 2017 roku powinny korzystać z bibliotek takich jak lodash.com/docs/4.17.4#ceil
Yves M.
1
Tak robi _. liczyć? nie został jeszcze ulepszony do swojego brata.
Jenna Leaf

Odpowiedzi:

124

Zwraca łańcuch, ponieważ 0.1 i jego moce (które są używane do wyświetlania ułamków dziesiętnych) nie są reprezentowalne (przynajmniej nie z pełną dokładnością) w binarnych systemach zmiennoprzecinkowych.

Na przykład 0,1 to naprawdę 0,1000000000000000055511151231257827021181583404541015625, a 0,01 to naprawdę 0,01000000000000000020816681711721685132943093776702880859375. (Dzięki BigDecimalza udowodnienie mojej racji. :-P)

Dlatego (w przypadku braku dziesiętnego typu zmiennoprzecinkowego lub wymiernego), wyprowadzenie go jako łańcucha jest jedynym sposobem, aby uzyskać dokładną precyzję wymaganą do wyświetlenia.

Chris Jester-Young
źródło
28
przynajmniej javascript mógłby mi zaoszczędzić trochę palca pracy i przekształcić ją z powrotem do szeregu ... sheesh ...
Derek Adair
10
@Derek: Tak, ale kiedy zamienisz to z powrotem na liczbę, znowu napotkasz te same problemy z niedokładnością. :-P JS nie ma dziesiętnych liczb zmiennoprzecinkowych ani wymiernych.
Chris Jester-Young
1
@DerekAdair Niedawno napisałem post, który wyjaśnia to jeszcze dokładniej i może Cię zainteresować. Miłej zabawy! stackoverflow.com/a/27030789/13
Chris Jester-Young
7
Właściwie to spowodowało, że przeprowadziłem dość obszerne badania na ten temat! dzięki za całą twoją pomoc!
Derek Adair
2
Twoja odpowiedź jest nieco myląca: toFixedto funkcja formatująca, której jedynym celem jest konwersja liczby na ciąg znaków, formatowanie jej przy użyciu określonej liczby miejsc po przecinku. Powodem, dla którego zwraca ciąg, jest to, że powinien zwrócić ciąg, a gdyby toStringFixedzamiast tego został nazwany , OP nie byłby zaskoczony wynikami. Jedynym problemem jest to, że OP spodziewał się, że będzie działać tak Math.round, jak bez konsultacji z referencjami JS.
Groo
177

Number.prototype.toFixedto funkcja przeznaczona do formatowania liczby przed jej wydrukowaniem. To z rodziny toString, toExponentiali toPrecision.

Aby zaokrąglić liczbę, wykonaj następujące czynności:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

.

Jeśli potrzebujesz funkcji „ natywnej ”, możesz rozszerzyć prototyp:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

Należy jednak pamiętać, że zanieczyszczenie prototypu jest uważane za złe podczas pisania modułu, ponieważ moduły nie powinny mieć żadnych skutków ubocznych. Tak więc w przypadku modułu użyj pierwszej funkcji .

m93a
źródło
12
Myślę, że to najlepsza odpowiedź. Pozwala to uniknąć konwersji typów. Wspaniały sos!
Phil
1
Świetna odpowiedź! Jednak ... Robię JavaScript od około 20 lat, ale nie mogę zrozumieć, dlaczego używasz tej konstrukcji + (...) wokół wartości zwracanej? Dzięki @sam za wtarcie :) Ponieważ nigdy nie jestem za stara, żeby się uczyć, proszę o wyjaśnienie :-)
HammerNL
1
@HammerNL Wbrew przekonaniu Sama, to faktycznie nic nie robi :) To tylko praktyka - sprawia, że ​​IDE rozpoznają tę funkcję jako type Number. Rzecz w tym, że +(anyValue)zawsze zwraca liczbę - np. +("45")zwraca 45, +(new Number(42))zwraca 42. To trochę jak silne wpisywanie funkcji. Jeśli
nabierzesz
Dlaczego nie jest to upieczone w rdzeniu javascript: s
webmaster
2
Rezultatem someNumber = Math.round( 42.008 * 1e2 ) / 1e2;nie jest 42.01, to jest ~42.0099999999999980. Przyczyna: numer 42.01nie istnieje i jest zaokrąglany do najbliższej istniejącej liczby. btw, sprawdź numery, toPrecision(18)aby wydrukować je ze wszystkimi odpowiednimi cyframi.
Wiimm
118

Rozwiązałem ten problem, zmieniając to:

someNumber = someNumber.toFixed(2)

...do tego:

someNumber = +someNumber.toFixed(2);

Jednak spowoduje to przekonwertowanie liczby na ciąg i ponownie ją przeanalizuje, co będzie miało znaczący wpływ na wydajność. Jeśli zależy Ci na wydajności lub bezpieczeństwie typu, sprawdź również pozostałe odpowiedzi.

Ewa Juan
źródło
39
Nie nie nie nie nie! Nie rób tego! Konwersja liczb na łańcuchy tylko do zaokrąglenia jest bardzo złą praktyką ! Zamiast tego zrób someNumber = Math.round(someNumber * 1e2) / 1e2! Zobacz moją odpowiedź w bardziej ogólny sposób.
m93a
@ m93a - dlaczego to taka zła praktyka?
jczaplew
3
@jczaplew Bo jeśli zrobisz to w ten sposób, 32-bitowa liczba binarna zostanie zamieniona na łańcuch, używając 16 bitów na każdą cholerną cyfrę dziesiętną ! (Nawiasem mówiąc, przechowywanie liczb w UTF-16 nie jest najwygodniejszą rzeczą, do której byś się zdecydował.) Następnie łańcuch jest konwertowany z powrotem na 32-bitową liczbę zmiennoprzecinkową. Cyfra po cyfrze. (Jeśli zignoruję wszystkie testy, które należy wykonać wcześniej, aby wybrać właściwy algorytm analizy). A wszystko to na próżno, biorąc pod uwagę, że można to zrobić za pomocą 3 szybkich operacji na float.
m93a
2
@ m93a, więc to ze względu na wydajność? czy rzeczywiście ma to zauważalny wpływ na wydajność?
Sebastianb
2
@jczaplew, Ponieważ ciągi znaków są (1) praktycznie wolne i (2) teoretycznie niepoprawne.
Pacerier
29

Dlaczego nie używać parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
sirlunchalot
źródło
15

Rozwiązałem to, konwertując go z powrotem na liczbę za pomocą Number()funkcji JavaScript

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
źródło
12

Oczywiście zwraca ciąg. Jeśli chcesz zaokrąglić zmienną numeryczną, użyjesz zamiast tego Math.round (). Punkt toFixed służy do formatowania liczby ze stałą liczbą miejsc dziesiętnych do wyświetlenia użytkownikowi .

Joel Coehoorn
źródło
4

Możesz po prostu użyć „+”, aby przekonwertować wynik na liczbę.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
meisam
źródło
3

Czego spodziewałbyś się, że zwróci, gdy ma sformatować liczbę? Jeśli masz liczbę, nie możesz prawie nic z nią zrobić, ponieważ np. 2 == 2.0 == 2.00Itd., Więc musi to być ciąg.

Tomas Vana
źródło
3

Aby podać przykład, dlaczego musi to być ciąg:

Jeśli sformatujesz 1. naFixed (2), otrzymasz „1,00”.

To nie to samo, co 1, ponieważ 1 nie ma 2 miejsc po przecinku.


Wiem, że JavaScript nie jest dokładnie językiem wydajnościowym , ale jest szansa, że ​​uzyskasz lepszą wydajność zaokrąglania, jeśli użyjesz czegoś takiego: roundedValue = Math.round (value * 100) * 0,01

Pyro
źródło
2

Ponieważ jego głównym zastosowaniem jest wyświetlanie liczb? Jeśli chcesz zaokrąglić liczby, użyj Math.round()odpowiednich współczynników.

Christoph
źródło
ale wyświetla LICZBY, więc czy nie powinien zwracać „liczby”?
Derek Adair
3
@Derek: Tylko w ten sposób, że '42'jest to liczba ... a to nie jest. Tylko dlatego, że ciąg zawiera tylko cyfry, nie czyni go liczbą. To nie jest PHP. :-P
Chris Jester-Young
lol. To nie jest ciąg zawierający liczbę ... To liczba przekazywana do metody. Metoda przyjmuje liczbę i zwraca ciąg.
Derek Adair
@DerekAdair ma rację, ale przeglądarka nie może wyświetlić liczby, wyświetla ciągi znaków, a więc konwersję.
Nick M
1

Oto nieco bardziej funkcjonalna wersja m93audzielonej odpowiedzi .

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
źródło
jest to przydatna funkcja, dla niezdefiniowanego typu nie działa, dodałem kod dla tego przypadku; if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Dino Liu