np. z dolarem nigdy nie masz precyzji mniejszej niż 0,01 $
Naprawdę?
odwieczny problem, dlaczego nie należy przechowywać waluty jako liczby zmiennoprzecinkowej IEEE 754.
Przechowuj cale w liczbach zmiennoprzecinkowych IEEE 754 . Przechowują dokładnie tak, jak można się spodziewać.
Przechowuj dowolne kwoty w liczbach zmiennoprzecinkowych IEEE 754 , które możesz przechowywać za pomocą podziałek dzielących linijkę na ułamki cala.
Dlaczego? Ponieważ kiedy używasz IEEE 754 , właśnie tak go przechowujesz.
Chodzi o to, że cale są podzielone na połówki. Rzecz w większości rodzajów walut jest podzielona na dziesiąte (niektóre rodzaje nie są, ale skupmy się).
Ta różnica nie byłaby aż tak myląca, z wyjątkiem tego, że dla większości języków programowania dane wejściowe i wyjściowe z liczb zmiennoprzecinkowych IEEE 754 są wyrażane w postaci dziesiętnej! Co jest bardzo dziwne, ponieważ nie są przechowywane w postaci dziesiętnej.
Z tego powodu nigdy nie zobaczysz, jak bity robią dziwne rzeczy, gdy poprosisz komputer o przechowywanie 0.1
. Dziwność pojawia się tylko wtedy, gdy się jej przeciwdziała, i ma dziwne błędy.
Z efektywnej javy Josha Blocha :
System.out.println(1.03 - .42);
Produkuje 0.6100000000000001
To, co najbardziej o tym mówi, nie jest 1
siedzeniem po prawej stronie. To dziwne liczby, które musiały zostać użyte, aby je zdobyć. Zamiast używać najpopularniejszego przykładu, 0.1
musimy użyć przykładu, który pokazuje problem i pozwala uniknąć zaokrąglenia, które by go ukryło.
Na przykład dlaczego to działa?
System.out.println(.01 - .02);
Produkuje -0.01
Ponieważ mieliśmy szczęście.
Nienawidzę problemów, które trudno zdiagnozować, ponieważ czasami mam szczęście.
IEEE 754 po prostu nie może dokładnie przechowywać 0.1. Ale jeśli poprosisz o zapisanie 0.1, a następnie o wydruk, pokaże 0.1, a pomyślisz, że wszystko jest w porządku. Nie jest w porządku, ale tego nie widać, ponieważ zaokrągla się, aby wrócić do 0,1.
Niektórzy ludzie mylą, do cholery, nazywając te rozbieżności błędami zaokrąglania. Nie, to nie są błędy zaokrąglania. Zaokrąglanie robi to, co powinno, i zamienia to, co nie jest dziesiętne, na dziesiętne, aby można było drukować na ekranie.
Ale to ukrywa niedopasowanie między sposobem wyświetlania liczby a sposobem jej przechowywania. Błąd nie wystąpił podczas zaokrąglania. Stało się tak, gdy zdecydowałeś się wprowadzić liczbę do systemu, który nie może dokładnie przechowywać, i zakładałeś, że jest przechowywany dokładnie wtedy, gdy nie był.
Nikt nie spodziewa się, że π zachowa się dokładnie w kalkulatorze i poradzi sobie z nim dobrze. Problem nie dotyczy nawet precyzji. Chodzi o oczekiwaną precyzję. Komputery wyświetlają jedną dziesiątą tyle 0.1
samo, co nasze kalkulatory, więc oczekujemy, że będą przechowywać jedną dziesiątą idealnie, tak jak robią to nasze kalkulatory. Oni nie. Co jest zaskakujące, ponieważ komputery są droższe.
Pokażę ci niedopasowanie:
Zauważ, że 1/2 i 0,5 idealnie pasują do siebie. Ale 0,1 po prostu się nie zgadza. Jasne, że możesz podejść bliżej, jeśli dzielisz przez 2, ale nigdy nie trafisz dokładnie. I potrzebujemy coraz więcej bitów za każdym razem, gdy dzielimy przez 2. Zatem reprezentowanie 0,1 w każdym systemie, który dzieli przez 2, potrzebuje nieskończonej liczby bitów. Mój dysk twardy po prostu nie jest taki duży.
Więc IEEE 754 przestaje próbować, gdy zabraknie bitów. Co jest miłe, ponieważ potrzebuję miejsca na dysku twardym na ... rodzinne zdjęcia. Nie naprawdę. Rodzinne fotografie. : P
W każdym razie to, co piszesz i co widzisz, są ułamki dziesiętne (po prawej), ale to, co przechowujesz, to dwójki (po lewej). Czasami są one dokładnie takie same. Czasami nie są. Czasami WYGLĄDA, jakby byli tacy sami, kiedy po prostu nie są. To zaokrąglenie.
W szczególności, co musimy wiedzieć, aby móc przechowywać wartości w niektórych walutach i drukować je?
Jeśli korzystasz z moich pieniędzy po przecinku, nie używaj liczb zmiennoprzecinkowych lub podwójnych.
Jeśli masz pewność, że takie rzeczy jak dziesiąte grosze nie będą zaangażowane, po prostu przechowuj je. Jeśli nie, to dowiedz się, jaka będzie najmniejsza jednostka tej waluty i skorzystaj z niej. Jeśli nie możesz, użyj czegoś takiego jak BigDecimal .
Moja wartość netto prawdopodobnie zawsze będzie pasować do 64-bitowej liczby całkowitej, ale rzeczy takie jak BigInteger działają dobrze w przypadku większych projektów. Są po prostu wolniejsze niż typy rodzime.
Zastanawianie się, jak go przechowywać, to tylko połowa problemu. Pamiętaj, że musisz także móc go wyświetlić. Dobry projekt rozdzieli te dwie rzeczy. Prawdziwym problemem związanym z używaniem pływaków jest to, że te dwie rzeczy są ze sobą powiązane.