Reprezentowanie wartości pieniężnych w Javie [zamknięte]

94

Rozumiem, że BigDecimal jest zalecaną najlepszą praktyką przedstawiania wartości pieniężnych w Javie. Czego używasz? Czy jest lepsza biblioteka, z której wolisz korzystać?

dshaw
źródło
4
spójrz na JSR 354
yegor256
1
Oto jedna klasa walutowa, którą możesz skopiować i rozszerzyć: java-articles.info/articles/?p=254
Gilbert Le Blanc
Zobacz także referencyjną implementację JSR-354 github.com/JavaMoney/jsr354-ri
pokonaj

Odpowiedzi:

81

BigDecimaldo samego końca. Słyszałem, że niektórzy ludzie tworzą własne Cashlub Moneyklasy, które zawierają wartość pieniężną w walucie, ale pod skórą nadal jest to BigDecimal, prawdopodobnie z BigDecimal.ROUND_HALF_EVENzaokrągleniem.

Edycja: Jak wspomina Don w swojej odpowiedzi , istnieją projekty open source, takie jak czas i pieniądze , i chociaż brawo im za to, że próbowali uniemożliwić programistom wymyślanie koła na nowo, po prostu nie mam wystarczającego zaufania do biblioteki pre-alpha, aby z niej korzystać to w środowisku produkcyjnym. Poza tym, jeśli poszperasz pod maską, zobaczysz, że oni BigDecimalteż używają .

dziewięciostronny
źródło
4
+1. Zdecydowaliśmy się dodać klasę kontenera, która również zużywa walutę. Jest to przydatne podczas renderowania wartości pieniężnych w tabelach.
Daniel Hiller
1
tak, to dość powszechne podejście i ma dużo sensu. Jedynym zastrzeżeniem jest sytuacja, gdy masz do czynienia z jenami japońskimi, ponieważ nie mają one mniejszego nominału walutowego, takiego jak centy, więc wymaga własnych zasad zaokrąglania.
dziewiątka
3
@ninesided to świetny przykład tego, dlaczego skręcenie własnej jest złą odpowiedzią. „A tak przy okazji, to nie działa w przypadku CURRENCY_X $”. To dobry znak, że nie działa również w przypadku wielu innych walut.
James Moore
1
@JamesMoore Nie zgadzam się z tym, że „toczenie własnym” to złe podejście, wystarczy, że zdajesz sobie sprawę z możliwych ograniczeń podejścia, które wybierzesz, stąd powód, dla którego o tym wspominam. Wdrożenie różnych zasad zaokrąglania w zależności od waluty jest trywialne, ale jeśli Twój system potrzebuje tylko transakcji w USD lub EUR, nie musisz zbytnio projektować.
dziewięcioosobowy
1
Spójrz na stackoverflow.com/questions/5134237/ ... tylko z jednego powodu, dla którego BigDecimal jest problemem. Rachunkowość na całej planecie to po prostu bagno specjalnych przypadków, a próba zamiatania ich wszystkich pod dywan BigDecimal po prostu nie działa.
James Moore
52

Informacja o JodaMoney może być przydatna dla osób przybywających tutaj przez wyszukiwarki internetowe: http://www.joda.org/joda-money/ .

Iñaki Ibarrola Atxa
źródło
Dzięki. Chciałem dodać następną notatkę o Joda Money. Używałeś tego?
dshaw
2
+1 wygląda ciekawie, cieszę się, że jest BigDecimalpod maską!
dziewięcioosobowy
8

Wygodną biblioteką, na którą natknąłem się wcześniej, jest biblioteka Joda-Money . Jedna z jego implementacji jest rzeczywiście oparta na BigDecimal. Opiera się na specyfikacji ISO-4217 dla walut i może obsługiwać dostosowaną listę walut (ładowaną przez CVS).

Ta biblioteka zawiera niewielką liczbę plików, przez które można szybko przejść, jeśli potrzebne są modyfikacje. Joda-Money jest publikowany na licencji Apache 2.0.

Bashar
źródło
7

Jeśli używasz tylko dolarów i centów, użyłbym długiego (przesuniętego o 2 miejsca po przecinku). Jeśli potrzebujesz więcej szczegółów, dobrym rozwiązaniem może być duży ułamek dziesiętny.

Tak czy inaczej, prawdopodobnie rozszerzyłbym klasę tak, aby miała .toString (), która używa poprawnego formatu i jako miejsce do umieszczania innych metod, które mogą się pojawić (przez długi czas mnożenie i dzielenie pójdzie nie tak, jeśli dziesiętna jest n 't dostosowane)

Ponadto, jeśli używasz zdefiniowania własnej klasy i interfejsu, możesz dowolnie zastępować implementację.

Bill K.
źródło
2
Uważaj, nawet długi czas może być zbyt krótki, aby utrzymać amerykański dług federalny w centach ... jeśli nie teraz, to za kilka lat.
Ingo
3
Zgadzam się - wszelkie ogromne kwoty dolarów (a może jeśli śledzisz pieniądze w jenach), powinieneś używać BigDecimal - ale nawet wtedy poważnie rozważałbym użycie do tego klasy kontenera. Myślę, że większość złożoności programowania wynika z tego, że ludzie nie definiują małych, prostych klas wokół kolekcji i typów wewnętrznych.
Bill K,
3

BigDecimal lub inna stała reprezentacja jest tym, co jest ogólnie potrzebne do pieniędzy.

Reprezentacje i obliczenia zmiennoprzecinkowe ( Double, Float) są niedokładne, co prowadzi do błędnych wyników.

Ken Gentle
źródło
7
Ściśle mówiąc, BigDecimal jest również niedokładne; po prostu lepiej koresponduje z zaokrąglaniem dziesiętnym, do którego jesteśmy przyzwyczajeni w życiu codziennym, i pozwala określić tryby zaokrąglania.
Michael Borgwardt
1
@Michael Borgwardt BigDecimal różni się od IEEE FP tym, że określono jawną skalę. Chociaż nie wszystkie operacje są dokładne, zapewnia to, że zestaw operacji i zachowań jest zawsze dokładny, a skala jest stała, podczas gdy skala dla IEEE FP maleje wraz z wartością.
1
Co to ma wspólnego z pieniędzmi? Organizacje księgowe na całym świecie mają zwykle bardzo specyficzne wymagania dotyczące sposobu obliczania matematyki w ich walucie. Czy BigDecimal dokładnie pasuje do każdego z tych standardów? Czy zrobi to w przyszłym roku, kiedy te standardy się zmienią? A BigDecimal nawet nie zbliża się do określenia przydatnych reguł zaokrąglania dla walut.
James Moore
2

Musisz być bardzo ostrożny, gdy masz do czynienia z czasem i pieniędzmi.

Mam nadzieję, że kiedy pracujesz z pieniędzmi, każdy powinien wiedzieć, jak nigdy nie używać pływaka ani podwójnej.

Ale nie jestem pewien co do BigDecimal.

W większości przypadków będzie dobrze, jeśli po prostu będziesz śledzić centy w ciągu int lub long. W ten sposób nigdy nie masz do czynienia z miejscem dziesiętnym.

Wyświetlasz dolary tylko podczas drukowania. Zawsze używaj centów wewnętrznych, używając liczb całkowitych. Może to być trudne, jeśli trzeba podzielić lub użyć Math.abs ().

Jednak może Cię to obchodzić pół centa, a nawet setną część. Nie wiem, jaki jest dobry sposób, aby to zrobić. Być może będziesz musiał poradzić sobie z tysięcznymi centami i użyć długiego. A może będziesz zmuszony użyć BigDecimal

Czytałbym o wiele więcej na ten temat, ale zignorowałbym każdego, kto zaczyna mówić o używaniu float lub double do reprezentowania pieniędzy. Po prostu proszą o kłopoty.

Czuję, że moja rada nie jest kompletna, więc proszę, włóż w nią więcej. Masz do czynienia z niebezpiecznymi typami!

Pirolistyczne
źródło
2
Dlaczego miałbyś być „zmuszany” do używania BigDecimal? Czego nie jesteś pewien? Jest zdecydowanie lepszy od pracy z centami, ponieważ umożliwia jawne określenie trybów zaokrąglania.
Michael Borgwardt
1
@MichaelBorgwardt: tak, pozwala określić niewielki podzbiór trybów zaokrąglania, których potrzebujesz dla walut. Więc? (Podpowiedź: o zaokrąglaniu walut decydują zazwyczaj krajowe organizacje księgowe. Z przyjemnością rzucają w dziwnych, specjalnych przypadkach. Zobacz stackoverflow.com/questions/5134237/ ... tylko jeden z wielu zabawnych powodów, dla których zaokrąglanie BigDecimal jest całkowicie bezużyteczne tutaj.)
James Moore
@James: Jak dokładnie jest to „bezużyteczne”? Jak wdrożenie tych specjalnych przypadków byłoby trudniejsze z BigDecimal niż z czymś innym?
Michael Borgwardt
1
OK, zupełnie bezużyteczne jest zbyt mocne. W skomplikowanej klasie, która wyodrębnia walutę, reguły zaokrąglania BigDecimal są prawdopodobnie przydatne w niektórych konkretnych przypadkach do zbudowania podzbioru sposobów zaokrąglania waluty. Ale ogólny przypadek jest taki, że zasady zaokrąglania dla walut wymagają mechanizmów, które podlegają zmianom w czasie (ponieważ agencje księgowe tworzą zasady i mogą je zmieniać). Pytanie nie dotyczy euro (lub czegokolwiek, co zastąpi euro w przyszłym miesiącu ...) ani dolarów w 2011 r., Ale dotyczy waluty, więc trzeba sobie radzić z wieloma paskudnymi problemami.
James Moore
2

Tworzenie klasy Money jest drogą do zrobienia. Używanie BigDecimal (lub nawet int) pod spodem. Następnie użycie klasy Currency do zdefiniowania konwencji zaokrąglania.

Niestety bez przeciążania operatorów Java sprawia, że ​​tworzenie takich podstawowych typów jest dość nieprzyjemne.

Kozyarchuk
źródło
2

Jest lepsza biblioteka, czas i pieniądze . IMO, znacznie przewyższa biblioteki dostarczane przez JDK do reprezentowania tych dwóch koncepcji.

Dónal
źródło
3
Ta odpowiedź została opublikowana trzy lata temu. Obecnie, zgodnie z tym linkiem, projekt timeandmoney jest nadal pre-alpha.
James Moore
1
@JamesMoore Dobre połączenie. Odpowiedź ma już 7 lat, a projekt nadal nie jest stabilny.
Navin,
1

Zdecydowanie nie BigDecimal. Jest tak wiele specjalnych zasad dotyczących zaokrąglania i prezentacji, o które musisz się martwić.

Martin Fowler zaleca implementację dedykowanej klasy Money do reprezentowania kwot walut, a także implementuje zasady przeliczania walut.

lindelof
źródło
6
i bazowy typ danych jego klasy Money? BigDecimal.
dziewięcioosobowy
1
To nieprawda. Możesz użyć liczby całkowitej w klasie pieniężnej, co robi Martin. Robiłem to wiele razy.
egervari
Zalecenie jest jednak poprawne; obliczenia związane z pieniędzmi to olbrzymie bagno specjalnych przypadków, które zmieniają się w czasie. BigDecimal może być przydatna jako niewielka część rozwiązania, ale z pewnością nie jest to ogólne.
James Moore
1

Hej, oto bardzo interesujący artykuł na temat BigDecimal i ilustrujący przykład, dlaczego czasami jest używany zamiast podwójnych. Samouczek BigDecimal .

arg20
źródło
0

Możesz użyć klasy DecimalFormat podczas ostatecznego wyświetlania wartości waluty. Zapewnia obsługę lokalizacji i jest dość rozszerzalny.


źródło
0

Zamknąłbym BigDecimal w klasie Money, która również ma walutę, tak jak ktoś wspomniany powyżej. Ważną rzeczą jest to, że wykonujesz ekstremalną liczbę testów jednostkowych, zwłaszcza jeśli pracujesz z różnymi walutami. Dobrym pomysłem jest również dodanie wygodnego konstruktora, który pobiera ciąg znaków lub metodę fabryczną, która robi to samo, dzięki czemu można napisać swoje testy mniej więcej tak:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
Per Arneng
źródło
0

W grę wchodzą zawsze ograniczenia i szczegóły. Każdy, kto nie ma wystarczającego doświadczenia, aby docenić subtelne kwestie przedstawione w poniższym artykule, powinien poważnie rozważyć ponowne rozważenie przed zapoznaniem się z rzeczywistymi danymi finansowymi:

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal nie jest jedyną poprawną reprezentacją ani jedynym elementem układanki. W pewnych warunkach użycie klasy Money zabezpieczonej centami przechowywanymi jako liczba całkowita może być wystarczające i byłoby znacznie szybsze niż BigDecimal. Tak, to implikuje użycie dolarów jako waluty i ogranicza kwoty, ale takie ograniczenia są całkowicie akceptowalne w wielu przypadkach użycia, a wszystkie waluty i tak mają specjalne przypadki zaokrąglania i denominacji, więc nie ma „uniwersalnego” rozwiązania.

Craig
źródło
1
Wygląda na to, że jest to komentarz do innego posta zamiast rzeczywistej odpowiedzi. Jest też zbyt jadowita. Proszę, staraj się być bardziej uprzejmym w przyszłości.
Slater Victoroff