Czy istnieje jedna reprezentacja danych, która działa dla wszystkich walut (nawet tych innych niż dolary, euro i funty)?

12

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?

Kat
źródło
2
Nie wszyscy programiści pracują w branżach, które manipulują kwotami walutowymi.
whatsisname
4
Och, oczywiście. Ale można to powiedzieć o prawie wszystkim. Myślę, że możemy założyć, że ci, którzy uznają to pytanie za przydatne, będą tymi, którzy pracują z walutą.
Kat
5
Wykonałem wiele prac rozwojowych dla banków i innych firm świadczących usługi finansowe w Nowym Jorku. I mogę w zasadzie zagwarantować, że nie ma odpowiedzi „zamkniętej formy” na twoje pytanie. Równie dobrze możesz zapytać: „ile matematyki muszę wiedzieć?” Na przykład w latach 90. ceny akcji wzrosły z ósmej i 256. do dziesiętnej, co wymagało wielu przeprogramowań. Kto mógł zgadnąć, że tak się stanie? Musisz tylko zrozumieć działalność obsługiwaną przez twoje aplikacje i jak najlepiej reprezentować dane (w tym przypadku walutę), aby zaspokoić te potrzeby biznesowe.
John Forkosh,
4
Moje 0,02: Cena to jedno. Waluta jest inna.
Machado,
3
Pomyślałem: Och, musi istnieć wspólny mianownik dla wszystkich walut. Ale może nie. Grosz szterling obecnie wynosi 1⁄100 funta, ale wynosił 1⁄240 funta. Więc nie tylko masz podział 1/240, ale masz datę zamiany i być może kurs wymiany starych pensów jeszcze się nie zmienił. en.wikipedia.org/wiki/Penny_sterling Nawet mnie nie zaczynaj na muszelkach! en.wikipedia.org/wiki/Shell_money lub fakt, że pieniądze wymagają wiary, aby stać się prawdziwym: en.wikipedia.org/wiki/Money Świetne pytanie, nawet jeśli nie pasuje do StackExchange!
GlenPeterson

Odpowiedzi:

24

np. z dolarem nigdy nie masz precyzji mniejszej niż 0,01 $

Naprawdę? wprowadź opis zdjęcia tutaj

odwieczny problem, dlaczego nie należy przechowywać waluty jako liczby zmiennoprzecinkowej IEEE 754.

wprowadź opis zdjęcia tutaj

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 1siedzeniem po prawej stronie. To dziwne liczby, które musiały zostać użyte, aby je zdobyć. Zamiast używać najpopularniejszego przykładu, 0.1musimy 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.1samo, 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:

wprowadź opis zdjęcia tutaj

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.

wprowadź opis zdjęcia tutaj

candied_orange
źródło
6
Tak naprawdę nie szukałem innego wyjaśnienia, dlaczego IEEE 754 nie działa na pieniądze. Wspomniałem w PO, że zostało to dobrze wyjaśnione w innym miejscu. Podobnie istnieje powód, dla którego użyłem terminu „użytek fizyczny”. Mianowicie dlatego, że chociaż ceny gazu, wartości giełdowe itp. Mogą mieć dowolną precyzję, pieniądze fizyczne (i ogromna liczba transakcji użytkowników) nie przekraczają setek miejsc (a różne programy chcą reprezentować tylko to, za co można zapłacić pieniądze fizyczne). Zastanawiałem się tylko, czy istnieją waluty, które szłyby w bardziej dziesiętne miejsca.
Kat
3
„Dziwactwo” zmiennoprzecinkowego nie ogranicza się tylko do połowy i do 1/10 tego. Aspekt „zmiennoprzecinkowy” w nazwie powoduje również czasem nieoczekiwane rezultaty.
whatsisname
6
Przed decymalizacją walutą brytyjską były funty, szylingi i pensy (£ sd), z 1 £ == 20s, 1s = 12d, więc 1 £ = 240d, a więc 1d = 0,0046666666666 .. więc nie można go nawet przedstawić w postaci dziesiętnej. Przed przystąpieniem do euro lira włoska osiągnęła 2346,4 (w 2001 r.) W stosunku do dolara amerykańskiego, więc rzeczy takie jak wynagrodzenia i ceny domów czasami osiągały górną granicę pojedynczych ruchów precyzyjnych, a dane ekonomiczne osiągały górne poziomy podwójnych.
Steve Barnes
2
Chciałbym tylko argumentować / wskazać, że Waluta to jedno, a Cena to coś innego. Ceny mogą mieć dokładność większą niż 0,01 USD, ale nie można skutecznie płacić z tą precyzją.
Machado,
1
Również zdjęcia rodzinne . Pft. :)
Machado,
10

Mogę znaleźć wiele pytań na temat bibliotek używanych do reprezentowania kwot w niektórych walutach.

„Biblioteki” są niepotrzebne, chyba że w standardowej bibliotece twojego języka brakuje określonych typów danych, jak wyjaśnię.

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

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.

W szczególności, co musimy wiedzieć, aby móc przechowywać wartości w niektórych walutach i drukować je?

Jest kilka ważnych punktów.

  1. Użyj rzeczywistego typu dziesiętnego zamiast zmiennoprzecinkowego.

  2. 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ę).

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

  4. 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
W przypadku 2 uważam, że kurs wymiany wystarczy, aby uzyskać przynajmniej swój własny numer. Musisz wziąć pod uwagę, że stawki zmieniają się w czasie, co ma kilka różnych sposobów postępowania.
Izkata,
2
+1. Ponadto waluty zmieniają się z czasem. W Brazylii wielokrotnie zmienialiśmy waluty w latach 80. i 90., wycinając zera i zmieniając nazwę waluty w razie potrzeby z powodu niekontrolowanej inflacji. Oznacza to, że każdy aspekt waluty może się zmieniać z czasem.
Machado,
@Izkata z pewnością pracowałem nad systemami, które zmieniają waluty. Chciałem poruszyć ten temat, ponieważ jest on istotny. Nie jest to jednak sedno pytania i prawdopodobnie mógłbym wpisać inną odpowiedź, o ile ta dotyczyłaby wymiany walut.
„„ Biblioteki ”są niepotrzebne, chyba że w standardowej bibliotece twojego języka brakuje określonych typów danych, jak wyjaśnię.” Waluty mogą mieć połówki, ćwiartki, dziesiąte, ósme itd., Co oznacza, że ​​potrzebujemy przynajmniej reprezentacji dziesiętnej. Ale czy jesteśmy całkowicie pewni, że nie ma walut z 1/3 całości?
Daniel McLaury
4

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.

gnasher729
źródło
0

W szczególności, co musimy wiedzieć, aby móc przechowywać wartości w niektórych walutach i drukować je?

  • „do przechowywania wartości” : typ danych liczb stałych i typ waluty. Myślę, że są to dość oczywiste. Zapisywanie do numeru o stałym punkcie może wymagać zaokrąglenia i zmiany wartości. Inną kwestią byłoby obliczanie waluty.
  • „wydrukować je” : lokalizacja użytkownika. Jeśli masz szczęście, otrzymujesz standardową bibliotekę do wykonywania wszystkiego, np. Symbol waluty, separator tysięcy, separator dziesiętny, precyzja itp.

Niektóre przykładowe problemy związane z ustawieniami regionalnymi:

  • 100 USD w ustawieniach regionalnych w USA powinno wynosić 100,00 USD, w innych ustawieniach regionalnych z kropką jako separatorem dziesiętnym byłoby 100,00 USD.
  • Niektóre kraje używają niezliczonej liczby zamiast tysięcy do grupowania numerów, np. Zamiast 10.000.00 byłoby to tylko 1.0000.00
  • Ze względów praktycznych ludzie nie drukują wszystkich liczb dla małych walut, np. Zamiast 1 000 000 000 000, chcą tylko, abyś wydrukował 1 miliard dolarów.

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.

imel96
źródło