Chciałbym zaokrąglić maksymalnie 2 miejsca po przecinku, ale tylko w razie potrzeby .
Wejście:
10
1.7777777
9.1
Wynik:
10
1.78
9.1
Jak mogę to zrobić w JavaScript?
javascript
rounding
decimal-point
śmierdzacy
źródło
źródło
Number.EPSILON
. ZastosowanieMath.round( num * 100 + Number.EPSILON ) / 100
.Number.EPSILON
tutaj?Odpowiedzi:
Posługiwać się
Math.round(num * 100) / 100
Edycja: aby upewnić się, że rzeczy takie jak 1.005 są prawidłowe, używamy
Math.round((num + Number.EPSILON) * 100) / 100
źródło
Math.round((num + 0.00001) * 100) / 100
. SpróbujMath.round((1.005 + 0.00001) * 100) / 100
iMath.round((1.0049 + 0.00001) * 100) / 100
Jeśli wartość jest typem tekstu:
Jeśli wartość jest liczbą:
Minusem jest to, że wartości takie jak 1,5 dadzą „1,50” jako wynik. Poprawka sugerowana przez @minitech:
Wydaje się, że
Math.round
jest to lepsze rozwiązanie. Ale to nie jest! W niektórych przypadkach NIE zaokrągli się poprawnie:toFixed () również NIE będzie poprawnie zaokrąglać w niektórych przypadkach (testowane w Chrome v.55.0.2883.87)!
Przykłady:
Myślę, że to dlatego, że 1.555 jest właściwie czymś w rodzaju pływaka 1.55499994 za kulisami.
Rozwiązaniem 1 jest użycie skryptu z wymaganym algorytmem zaokrąglania, na przykład:
https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview
UWAGA: To nie jest uniwersalne rozwiązanie dla wszystkich. Istnieje kilka różnych algorytmów zaokrąglania, implementacja może być różna, zależy od wymagań. https://en.wikipedia.org/wiki/Rounding
Rozwiązaniem 2 jest uniknięcie obliczeń frontonu i pobranie zaokrąglonych wartości z serwera zaplecza.
źródło
parseFloat(number.toFixed(decimalPlaces));
@PerLundbergparseFloat("55.555").toFixed(2)
powraca"55.55"
w konsoli programisty Chrome.Możesz użyć
Znalazłem to w MDN . Ich sposób unika się problemu z 1,005, który został wymieniony .
źródło
+(val)
jest odpowiednikiem użycia przymusuNumber(val)
. Łączenie „e-2” z liczbą zaowocowało ciągiem, który musiał zostać przekonwertowany z powrotem na liczbę.roundToTwo(1.0049999999999999)
pojawia się jako 1.01 (nieuchronnie od tego czasu1.0049999999999999 == 1.005
). Wydaje mi się, że liczba zmiennoprzecinkowa, którą otrzymujesz, jeśli wpiszesznum = 1.005
„oczywiście”, powinna zaokrąglić do 1,00, ponieważ dokładna wartość parametru num jest mniejsza niż 1,005. Oczywiście wydaje mi się również, że ciąg „1.005” „oczywiście” powinien zostać zaokrąglony do 1,01. Fakt, że różni ludzie wydają się mieć różne intuicje na temat tego, jakie jest właściwe prawidłowe zachowanie, jest częścią tego, dlaczego jest to skomplikowane.1.0049999999999999
i1.005
tak z definicji, jest to ta sama liczba. Nazywa się to cięciem dedekindowym.1.00499 < 1.005
jesttrue
,1.0049999999999999 < 1.005
ocenia nafalse
.Odpowiedź MarkG jest poprawna. Oto ogólne rozszerzenie dla dowolnej liczby miejsc po przecinku.
Stosowanie:
Test jednostkowy:
źródło
function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
(-1.005).round(2) === -1
Powinieneś użyć:
Nikt nie zdaje sobie z tego sprawy
Number.EPSILON
.Warto również zauważyć, że nie jest to dziwność JavaScript, jak niektórzy twierdzą.
Jest to po prostu sposób, w jaki liczby zmiennoprzecinkowe działają w komputerze. Podobnie jak 99% języków programowania, JavaScript nie ma domowych liczb zmiennoprzecinkowych; w tym celu wykorzystuje procesor / FPU. Komputer używa binarnych, a w binarnych nie ma żadnych liczb takich
0.1
, ale jedynie binarne przybliżenie. Dlaczego? Z tego samego powodu, co 1/3, nie można zapisać dziesiętnie: jego wartość wynosi 0.33333333 ... z nieskończonością trzech.Tu przyjść
Number.EPSILON
. Liczba ta jest różnicą między 1 a kolejną liczbą występującą w liczbach zmiennoprzecinkowych podwójnej precyzji. To wszystko: nie ma liczby między1
a 1 +Number.EPSILON
.EDYTOWAĆ:
Jak pytamy w komentarzach, wyjaśnijmy jedną rzecz: dodawanie
Number.EPSILON
jest istotne tylko wtedy, gdy wartość do zaokrąglenia jest wynikiem operacji arytmetycznej, ponieważ może połknąć pewną deltę błędu zmiennoprzecinkowego.Nie jest to przydatne, gdy wartość pochodzi z bezpośredniego źródła (np. Dosłowność, dane wejściowe użytkownika lub czujnik).
EDYCJA (2019):
Jak zauważyli @maganap i niektórzy ludzie, najlepiej dodać
Number.EPSILON
przed pomnożeniem:EDYCJA (grudzień 2019):
Ostatnio używam funkcji podobnej do tej do porównywania liczb świadomych epsilon:
Mój przypadek użycia to biblioteka asercji + sprawdzania poprawności, którą rozwijam od wielu lat.
W rzeczywistości w kodzie używam
ESPILON_RATE = 1 + 4 * Number.EPSILON
iEPSILON_ZERO = 4 * Number.MIN_VALUE
(czterokrotnie epsilon), ponieważ chcę, aby moduł sprawdzania równości był wystarczająco luźny, aby kumulować błąd zmiennoprzecinkowy.Jak na razie wygląda mi to idealnie. Mam nadzieję, że to pomoże.
źródło
Math.round( (num + Number.EPSILON) * 100) / 100
.. Zgadzam się również, że jest to właściwa metoda prawidłowego zaokrąglania (chociaż nie jest to dokładnie to, o co pytano w tym pytaniu).0.004999999999999999
wynik złożonego błędu zmiennoprzecinkowego, a matematycznie poprawny wynik to prawdopodobnie 0,005. Czy to odczyt z czujnika? Nie tak bardzo.Math.round(1.5)
= 2, aleMath.round(-1.5)
= -1. Jest to więc całkowicie spójne. Tutaj -1 jest większe niż -2, tak jak -1000 jest większe niż -1000.01. Nie mylić z większymi liczbami bezwzględnymi .Można użyć
.toFixed(NumberOfDecimalPlaces)
.źródło
+(1.005).toFixed(2)
która zwraca1
zamiast1.01
.Number(9).toFixed(2).replace(/0+$/, '')
=> „9.”To pytanie jest skomplikowane.
Załóżmy, że mamy funkcję,
roundTo2DP(num)
która przyjmuje zmienną jako argument i zwraca wartość zaokrągloną do 2 miejsc po przecinku. Co powinno oceniać każde z tych wyrażeń?roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
„Oczywistą” odpowiedzią jest to, że pierwszy przykład powinien zaokrąglić do 0,01 (ponieważ jest bliżej 0,01 niż do 0,02), podczas gdy pozostałe dwa powinny zaokrąglić do 0,02 (ponieważ 0,0150000000000000001 jest bliższy 0,02 niż do 0,01, a ponieważ 0,015 jest dokładnie w połowie drogi między i istnieje konwencja matematyczna, że takie liczby są zaokrąglane w górę).
Haczyk, który być może zgadłeś, polega na tym, że
roundTo2DP
nie można go wprowadzić w celu udzielenia tych oczywistych odpowiedzi, ponieważ wszystkie trzy przekazane mu liczby są tej samej liczby . Binarne liczby zmiennoprzecinkowe IEEE 754 (używane przez JavaScript) nie mogą dokładnie reprezentować większości liczb niecałkowitych, więc wszystkie trzy literały liczbowe powyżej są zaokrąglane do pobliskiej prawidłowej liczby zmiennoprzecinkowej. Ta liczba, jak to się dzieje, jest dokładnie0,01499999999999999944488848768742172978818416595458984375
co jest bliższe 0,01 niż 0,02.
Możesz zobaczyć, że wszystkie trzy liczby są takie same w konsoli przeglądarki, powłoce węzła lub innym tłumaczu JavaScript. Po prostu porównaj je:
Więc kiedy piszę
m = 0.0150000000000000001
, dokładna wartość tegom
, z czym się skończyłem, jest bliższa0.01
niż jest0.02
. A jednak, jeśli przekonwertujęm
na ciąg znaków ...... Dostaję 0,015, co powinno zaokrąglić do 0,02, i który wyraźnie nie jest liczbą z 56 miejsc po przecinku, którą wcześniej powiedziałem, że wszystkie te liczby są dokładnie równe. Co to za mroczna magia?
Odpowiedź można znaleźć w specyfikacji ECMAScript, w sekcji 7.1.12.1: ToString zastosowane do typu Number . Ustanowiono tu zasady konwertowania pewnej liczby m na ciąg znaków. Kluczową częścią jest punkt 5, w którym generowana jest liczba całkowita s, której cyfry zostaną wykorzystane w reprezentacji ciągu m :
Kluczową częścią jest tutaj wymóg, aby „ k było tak małe, jak to możliwe”. Co wymóg ten wynosi Jest to wymóg, że otrzymuje numer
m
, wartośćString(m)
musi mieć możliwie najmniejszą liczbę cyfr , a jednocześnie spełniający wymagania tegoNumber(String(m)) === m
. Ponieważ już to wiemy0.015 === 0.0150000000000000001
, teraz jest jasne, dlaczegoString(0.0150000000000000001) === '0.015'
musi to być prawda.Oczywiście, żadna z tych dyskusji nie odpowiedziała bezpośrednio na to, co
roundTo2DP(m)
powinno powrócić. Jeślim
dokładna wartość wynosi 0,01499999999999999944488848768742172978818416595458984375, ale jej ciąg reprezentuje „0,015”, to jaka jest prawidłowa odpowiedź - matematycznie, praktycznie, filozoficznie lub cokolwiek innego - kiedy zaokrąglamy ją do dwóch miejsc po przecinku?Nie ma jednej poprawnej odpowiedzi na to pytanie. To zależy od twojego przypadku użycia. Prawdopodobnie chcesz uszanować reprezentację ciągu i zaokrąglić w górę, gdy:
Z drugiej strony, prawdopodobnie chcesz przestrzegać binarnej wartości zmiennoprzecinkowej i zaokrąglać w dół, gdy twoja wartość pochodzi z naturalnie ciągłej skali - na przykład, jeśli jest to odczyt z czujnika.
Te dwa podejścia wymagają innego kodu. Aby uszanować reprezentację ciągu w postaci liczby, możemy (z dość rozsądnie subtelnym kodem) zaimplementować własne zaokrąglanie, które działa bezpośrednio na reprezentację ciągu, cyfra po cyfrze, przy użyciu tego samego algorytmu, którego używałbyś w szkole uczono, jak zaokrąglać liczby. Poniżej znajduje się przykład, który spełnia wymóg OP dotyczący reprezentowania liczby do 2 miejsc po przecinku „tylko w razie potrzeby” poprzez usunięcie końcowych zer po przecinku; możesz oczywiście dostosować go do swoich konkretnych potrzeb.
Przykładowe użycie:
Powyższa funkcja jest prawdopodobnie tym, czego chcesz użyć, aby uniknąć zauważania przez użytkowników nieprawidłowo zaokrąglonego numeru.
(Alternatywnie można również wypróbować bibliotekę round10, która zapewnia podobnie zachowującą się funkcję z zupełnie inną implementacją).
Ale co, jeśli masz drugi rodzaj liczby - wartość zaczerpnięta z ciągłej skali, gdzie nie ma powodu sądzić, że przybliżone reprezentacje dziesiętne z mniejszą liczbą miejsc dziesiętnych są dokładniejsze niż te z większą liczbą? W takim przypadku nie chcemy respektować reprezentacji String, ponieważ ta reprezentacja (jak wyjaśniono w specyfikacji) jest już nieco zaokrąglona; nie chcemy popełnić błędu mówiąc „0,014999999 ... 375 zaokrągla w górę do 0,015, co zaokrągla w górę do 0,02, a więc 0,014999999 ... 375 zaokrągla w górę do 0,02”.
Tutaj możemy po prostu użyć wbudowanej
toFixed
metody. Zauważ, że wywołującNumber()
zwrócony ciągtoFixed
, otrzymujemy liczbę, której reprezentacja ciągu nie ma zer zerowych (dzięki temu, jak JavaScript oblicza reprezentację ciągu liczby, omówioną wcześniej w tej odpowiedzi).źródło
roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2)
. Oczekiwany rezultat (jak w PHPecho round(362.42499999999995, 2)
)362.43
. Rzeczywisty wynik:362.42
round
daje 362,43. To wydaje się intuicyjnie niepoprawne, ponieważ 362.42499999999995 jest mniejszy niż 362,425 (w matematyce i kodzie -362.42499999999995 < 362.425
jest to prawda zarówno w JS, jak i PHP). Od tego czasu odpowiedź PHP nie minimalizuje odległości między oryginalnymi a zaokrąglonymi liczbami zmiennoprzecinkowymi362.43 - 362.42499999999995 > 362.42499999999995 - 362.42
. Według php.net/manual/en/function.round.php , PHP jestround
zgodne ze standardem C99; Będę musiał zapuścić się do krainy C, aby zrozumieć, co się dzieje.Rozważ
.toFixed()
i.toPrecision()
:http://www.javascriptkit.com/javatutors/formatnumber.shtml
źródło
Precyzyjna metoda zaokrąglania. Źródło: Mozilla
Przykłady:
źródło
Math.round10(3544.5249, -2)
zwraca 3544,52 zamiast 3544,533544.5249
do 2 miejsc po przecinku to3544.52
(błąd = 0,0049). Gdyby tak było3544.53
, błąd wynosiłby 0,0051. Robisz kolejne zaokrąglanie, tj. Math.round10 (Math.round10 (3544.5249, -3), -2), co daje większy błąd zaokrąglania i dlatego nie jest pożądane.number += 0.00011
Math.round10( Math.round10(3544.5249, -3) , -2)
Żadna z odpowiedzi tutaj nie jest poprawna . @stinkycheeseman poprosił o zaokrąglenie w górę , wszyscy zaokrągliliście liczbę.
Aby zaokrąglić w górę, użyj tego:
źródło
Math.ceil(1.1 * 100)/100;
- zwraca1.11
, ponieważ 1,1 * 100 jest110.00000000000001
zgodny z nowymi nowoczesnymi przeglądarkami Firefox, Chrome, Safari i Opera ... IE, w starym stylu, wciąż myśli1.1*100=1100
.Math.ceil(num.toFixed(4) * 100) / 100
Math.ceil((1.1).toFixed(4) * 100) / 100
powróci również1.11
w Firefoksie, problem współczesnych przeglądarek / błąd związany jest z mnożeniem i ludzie powinni o tym wiedzieć (pracowałem na przykład nad loterią).Oto prosty sposób, aby to zrobić:
Możesz jednak zrobić osobną funkcję, aby to zrobić za Ciebie:
Wtedy po prostu podasz wartość.
Możesz go zwiększyć do zaokrąglania do dowolnej liczby miejsc po przecinku, dodając drugi parametr.
źródło
źródło
+(0.015).toFixed(2) == 0.01
.Dla mnie Math.round () nie dawał poprawnej odpowiedzi. Odkryłem, że toFixed (2) działa lepiej. Poniżej znajdują się przykłady obu:
źródło
1.005
.(1.005).toFixed(2)
nadal wyniki dla1.00
.Użyj tej funkcji
Number(x).toFixed(2);
źródło
Number
ponownie, jeśli nie chcesz, aby był zwracany jako ciąg:Number(Number(x).toFixed(2));
Number
Połączenie nie jest konieczne,x.toFixed(2)
działa.(1).toFixed(2)
zwraca1.00
, ale1
w tym przypadku potrzebny jest pytający .1.005.toFixed(2)
daje,"1"
kiedy powinno być"1.01"
.2017
Wystarczy użyć kodu natywnego
.toFixed()
Jeśli musisz być ścisły i dodawać cyfry tylko w razie potrzeby, możesz go użyć
replace
źródło
toFixed
zasugerowało wiele odpowiedzi wiele lat wcześniej, ale nie spełnia warunku „tylko w razie potrzeby” w pytaniu;(1).toFixed(2)
daje,"1.00"
gdzie pytający chciał"1"
.Wypróbuj to lekkie rozwiązanie:
źródło
return Number(x.toFixed(digits))
?.toFixed()
dopuszcza tylko liczby.round(1.005, 2)
zobaczyć wynik1
zamiast1.01
.round(0.995, 2) => 0.99
;round(1.006, 2) => 1.01
;round(1.005, 2) => 1
Można to zrobić na kilka sposobów. Dla ludzi takich jak ja wariant Lodasha
Stosowanie:
Jeśli twój projekt używa jQuery lub lodash, możesz również znaleźć odpowiednią
round
metodę w bibliotekach.Aktualizacja 1
Usunąłem wariant
n.toFixed(2)
, ponieważ jest nieprawidłowy. Dziękuję @ lawina 1źródło
Number.toFixed()
zwróci ciąg, ale przed nim znak plus, interpreter JS przekształci ciąg na liczbę. To jest cukier składniowy.alert((+1234).toFixed(2))
pokazuje „1234.00”.alert(+1234.toFixed(2))
rzucaSyntaxError: identifier starts immediately after numeric literal
. Trzymam się pierwszej opcji.362.42499999999995
. Oczekiwany rezultat (jak w PHPecho round(362.42499999999995, 2)
)362.43
. Rzeczywisty wynik:362.42
Jeśli używasz biblioteki lodash, możesz użyć okrągłej metody lodash w następujący sposób.
Na przykład:
źródło
Od wersji ES6 istnieje „właściwy” sposób (bez zastępowania statyki i tworzenia obejść), aby to zrobić za pomocą toPrecision
wtedy możesz po prostu
parseFloat
i zero zniknie.Nie rozwiązuje to jednak „problemu zaokrąglania 1.005” - ponieważ jest ono nieodłącznie związane z przetwarzaniem ułamków swobodnych .
Jeśli jesteś otwarty na biblioteki, możesz użyć bignumber.js
źródło
(1.005).toPrecision(3)
wciąż powraca1.00
zamiast1.01
faktycznie.toPrecision
zwraca ciąg, który zmienia pożądany typ wyjścia..toPrecision
metody, jest ona swoistością liczb zmiennoprzecinkowych (którymi są liczby w JS) - spróbuj1.005 - 0.005
, zwróci0.9999999999999999
.(1).toPrecision(3)
zwraca „1,00”, ale pytający chciał mieć1
w tym przypadku.toPrecision
robi ten format, a nie ten drugi, i nie jest odpowiedzią na pytanie PO, choć może się wydawać na pierwszy rzut oka trafny, robi się bardzo źle. Zobacz en.wikipedia.org/wiki/Sinentant_figures . Na przykładNumber(123.4).toPrecision(2)
zwroty"1.2e+2"
iNumber(12.345).toPrecision(2)
zwroty"12"
. Zgadzam się również z twierdzeniem @ adamduren, że zwraca ciąg, który nie jest pożądany (nie jest to duży problem, ale nie jest pożądany).MarkG i Lavamantis zaproponowali znacznie lepsze rozwiązanie niż to, które zostało zaakceptowane. Szkoda, że nie dostają więcej głosów pozytywnych!
Oto funkcja, której używam do rozwiązywania problemów z liczbami zmiennoprzecinkowymi również na podstawie MDN . Jest nawet bardziej ogólny (ale mniej zwięzły) niż rozwiązanie Lavamantisa:
Używaj go z:
W porównaniu z rozwiązaniem Lavamantis możemy zrobić ...
źródło
To może ci pomóc:
Aby uzyskać więcej informacji, możesz spojrzeć na ten link
Math.round (num) vs num.toFixed (0) i niespójności przeglądarki
źródło
Najłatwiejszym rozwiązaniem byłoby użycie toFixed, a następnie usunięcie końcowych zer za pomocą funkcji Number:
źródło
15.00
? Liczby w JS nie przechowują miejsc dziesiętnych, a każdy wyświetlacz automatycznie obcina nadmiar miejsc dziesiętnych (dowolne zera na końcu).toFixed(2)
tutaj 2 to liczba cyfr w górę, którą chcemy zaokrąglić tę liczbę.źródło
To może ci pomóc,
poznać różnicę między toFixed a round. Możesz spojrzeć na Math.round (num) vs num.toFixed (0) i niespójności przeglądarki .
źródło
Najprostszy sposób:
+num.toFixed(2)
Konwertuje go na ciąg, a następnie z powrotem na liczbę całkowitą / liczbę zmiennoprzecinkową.
źródło
toFixed()
na 3. Tak więc byłoby+num.toFixed(3)
.Oto prototypowa metoda:
źródło
Użyj czegoś takiego: „parseFloat (parseFloat (wartość) .toFixed (2))”
źródło
Jednym ze sposobów osiągnięcia takiego zaokrąglenia tylko w razie konieczności jest użycie Number.prototype.toLocaleString () :
Zapewni to dokładnie oczekiwany wynik, ale jako ciągi znaków. Nadal możesz przekonwertować je z powrotem na liczby, jeśli nie jest to oczekiwany typ danych.
źródło
toLocaleString
.Po przejściu przez różne iteracje wszystkich możliwych sposobów osiągnięcia prawdziwej dokładności zaokrąglania dziesiętnego, jasne jest, że najdokładniejszym i najskuteczniejszym rozwiązaniem jest użycie Number.EPSILON. To zapewnia prawdziwe matematyczne rozwiązanie problemu precyzji obliczeń zmiennoprzecinkowych. Można go łatwo wypełnić, jak pokazano tutaj: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON w celu obsługi wszystkich pozostałych użytkowników IE (wtedy znowu może powinien przestać to robić).
Zaadaptowano z rozwiązania podanego tutaj: https://stackoverflow.com/a/48850944/6910392
Proste rozwiązanie, które zapewnia dokładne zaokrąglanie dziesiętne, podłogi i sufit, z opcjonalną zmienną dokładności bez dodawania całej biblioteki.
AKTUALIZACJA: Jak zauważył Siergiej w komentarzach, istnieje ograniczenie tej (lub dowolnej) metody, na którą warto zwrócić uwagę. W przypadku liczb takich jak 0,01499999999999999999 nadal będą występować niedokładności, które są wynikiem uderzenia w absolutną krawędź ograniczeń dokładności dla przechowywania wartości zmiennoprzecinkowych. Nie ma matematyki ani innego rozwiązania, które można by zastosować do tego, ponieważ sama wartość jest natychmiast oceniana jako 0,015. Możesz to potwierdzić, po prostu wywołując tę wartość samodzielnie w konsoli. Z powodu tego ograniczenia nie byłoby nawet możliwe użycie manipulacji ciągiem w celu zmniejszenia tej wartości, ponieważ jego reprezentacja ciągu to po prostu „0,015”. Każde rozwiązanie tego wymagałoby logicznego zastosowania u źródła danych przed zaakceptowaniem wartości w skrypcie,
źródło
To najprostsze, bardziej eleganckie rozwiązanie (a ja jestem najlepszy na świecie;):
źródło
E
notacji.roundToX(362.42499999999995, 2)
. Oczekiwany rezultat (jak w PHPecho round(362.42499999999995, 2)
)362.43
. Rzeczywisty wynik:362.42