Mogę znaleźć wiele pytań na temat bibliotek używanych do reprezentowania kwot w niektórych walutach. I o odwiecznym pytaniu, dlaczego nie należy przechowywać waluty jako liczby zmiennoprzecinkowej IEEE 754. Ale nie mogę nic więcej znaleźć. Na pewno jest o wiele więcej informacji na temat waluty w świecie rzeczywistym. Szczególnie interesuje mnie to, co powinieneś wiedzieć, aby przedstawić to w fizycznym użyciu (np. W przypadku dolara nigdy nie masz precyzji mniejszej niż 0,01 USD, co pozwala na reprezentację w postaci całkowitej liczby centów).
Ale trudno jest założyć, jak wszechstronny jest twój program, gdy jedynymi znanymi walutami są popularne waluty zachodnie (takie jak dolar, euro i funt). Czym jeszcze jest odpowiednia wiedza w czysto programowej perspektywie? Nie martwię się tematem konwersji.
W szczególności, co musimy wiedzieć, aby móc przechowywać wartości w niektórych walutach i drukować je?
Odpowiedzi:
Naprawdę?
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 :
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?
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.
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.
źródło
„Biblioteki” są niepotrzebne, chyba że w standardowej bibliotece twojego języka brakuje określonych typów danych, jak wyjaśnię.
Po prostu potrzebujesz stałej liczby dziesiętnej, a nie zmiennoprzecinkowej . Na przykład klasa BigDecimal Java może być używana do przechowywania kwoty waluty. Inne współczesne języki mają wbudowane podobne typy, w tym C # i Python . Implementacje są różne, ale zwykle przechowują liczbę jako liczbę całkowitą, a lokalizacja dziesiętna jako osobny element danych. Daje to dokładną dokładność, nawet podczas wykonywania arytmetyki, która dałaby nieparzyste reszty (np. 0,0000001) z liczbami zmiennoprzecinkowymi IEEE.
Jest kilka ważnych punktów.
Użyj rzeczywistego typu dziesiętnego zamiast zmiennoprzecinkowego.
Zrozum, że kwota w walucie składa się z dwóch składników: wartości (5.63) oraz kodu lub typu waluty (USD, CAD, GBP, EUR i in.). Czasami możesz zignorować kod waluty, innym razem jest to niezbędne. Co zrobić, jeśli pracujesz nad systemem finansowym lub detalicznym / e-commerce, który pozwala na wiele walut? Co się stanie, jeśli próbujesz pobrać pieniądze od klienta w CAD, ale ten chce zapłacić MXN? Potrzebny jest typ „pieniężny” z kodem waluty i kwotą waluty, aby mieszać te wartości (także kurs wymiany, ale nie chcę przesadzać ze styczną). Jednocześnie moje oprogramowanie do finansów osobistych nigdy nie musi się tym martwić, ponieważ wszystko jest w USD ( może mieszać waluty, ale nigdy nie muszę).
Podczas gdy waluta może mieć w praktyce najmniejszą jednostkę fizyczną (CAD i USD mają centy, JPY to po prostu ... Jen), można ją zmniejszyć. Odpowiedź CandiedOrange wskazuje ceny paliw w dziesiątych części centa. Moje podatki od nieruchomości są oceniane jako młyny za dolara lub dziesiąte części centa (1/1000 dolara USD). Nie ograniczaj się do 0,01 USD. Chociaż możesz wyświetlać te wartości przez większość czasu, twoje typy powinny dopuszczać mniejsze (typy dziesiętne wymienione powyżej).
Obliczenia pośrednie z pewnością muszą pozwolić na większą precyzję niż jeden cent. Pracowałem nad systemami handlu detalicznego / e-commerce, w których wartości wewnętrzne zostały zaokrąglone wewnętrznie do 0,00000001 USD. Nieskończona precyzja zazwyczaj nie jest obsługiwana przez typy dziesiętne (lub SQL), więc musi istnieć pewien limit. Na przykład podzielenie 1/3 za pomocą BigDecimal Java spowoduje zgłoszenie wyjątku bez określonego RoundingMode lub MathContext, ponieważ wartości nie można dokładnie przedstawić.
W każdym razie jest to krytyczne. Załóżmy, że masz użytkownika z sześcioma przedmiotami w koszyku i idzie do kasy. Twój system musi obliczyć podatek, i robi to według pozycji, ponieważ przedmioty mogą być opodatkowane inaczej. Jeśli zaokrąglisz podatki przy każdym produkcie, możesz dostać błędy zaokrąglania groszów na poziomie transakcji / koszyka. Jednym ze sposobów rozwiązania tego problemu może być przechowywanie podatków do większej liczby miejsc po przecinku na sztukę, uzyskanie sumy dla całej transakcji i cofanie się i zaokrąglanie każdej pozycji, aby całkowity podatek był poprawny (może jedna pozycja zaokrągla w górę, a druga w dół).
Z tego wszystkiego należy zdać sobie sprawę, że coś tak ważnego jak zaokrąglanie pensów może być bardzo ważne dla właściwych ludzi (np. Niektórzy z moich poprzednich klientów, którzy musieli płacić podatki od sprzedaży w imieniu swoich klientów). Są to jednak rozwiązane problemy. Miej na uwadze powyższe punkty i sam wykonaj eksperymenty, a nauczysz się poprzez działanie.
źródło
Jednym z miejsc, w którym wielu programistów jest skonfrontowanych z pojedynczą reprezentacją danych dla dowolnej waluty, są zakupy w aplikacjach na iOS. Twój klient może być połączony ze sklepem w prawie każdym kraju na świecie. W takiej sytuacji otrzymasz cenę zakupu składającą się z podwójnej liczby precyzyjnej i kodu waluty.
Musisz mieć świadomość, że liczby mogą być duże. Istnieją waluty, w których równowartość na przykład dziesięciu dolarów wynosi ponad 100 000 jednostek. I mamy szczęście, że w tej chwili nie ma takich walut jak dolar Zimbabwe, w których można by mieć sto bilionów banknotów!
Aby wyświetlać waluty, potrzebujesz biblioteki - nie masz szans, aby wszystko naprawić. Sposób wyświetlania zależy od dwóch rzeczy: kodu waluty i ustawień regionalnych użytkownika. Zastanów się, jak dolary amerykańskie i dolary kanadyjskie byłyby wyświetlane w przypadku lokalizacji w USA i lokalizacji w Kanadzie: W USA masz $ vs CAN $, aw Kanadzie masz $ US vs. $. Mam nadzieję, że jest wbudowany w system operacyjny lub masz dobrą bibliotekę.
W przypadku obliczeń wszelkie obliczenia zakończą się krokiem zaokrąglania. Musisz dowiedzieć się, jak legalnie wykonać to zaokrąglenie . To nie jest problem z programowaniem, to jest problem prawny. Na przykład, jeśli obliczasz podatek VAT w Wielkiej Brytanii, musisz obliczyć podatek na sztukę lub na linię towaru i zaokrąglić go w dół do centów. To, co zaokrąglisz, zależy od waluty. Ale zasady oczywiście zależą od kraju. Nie można oczekiwać, że obliczenia, które są prawnie poprawne w Wielkiej Brytanii, byłyby prawnie poprawne w Japonii i odwrotnie.
źródło
Niektóre przykładowe problemy związane z ustawieniami regionalnymi:
Innym potencjalnym problemem jest nawet to, czy poprawnie przechowujesz walutę w stałym numerze punktu, może być konieczna konwersja na liczbę zmiennoprzecinkową, ponieważ biblioteka używana do drukowania obsługuje tylko zmiennoprzecinkowe.
źródło