Korzystanie z BigDecimal do pracy z walutami
Próbowałem stworzyć własną klasę dla walut używając longów, ale najwyraźniej powinienem użyć BigDecimal
zamiast tego. Czy ktoś mógłby mi pomóc zacząć? Jaki byłby najlepszy sposób użycia BigDecimal
s dla walut dolarowych, np. co najmniej, ale nie więcej niż 2 miejsca po przecinku dla centów itp. API dla BigDecimal
jest ogromne i nie wiem jakich metod użyć. Poza tym, BigDecimal
ma lepszą precyzję, ale czy to wszystko nie jest stracone, jeśli przechodzi przez double
? jeśli zrobię Nowy BigDecimal(24.99)
, Jak będzie inaczej niż używając double
? Czy powinienem użyć konstruktora, który zamiast tego używa String
?
8 answers
Oto kilka wskazówek:
- Użyj
BigDecimal
do obliczeń, jeśli potrzebujesz precyzji, którą oferuje (wartości pieniędzy często tego potrzebują). - użyj
NumberFormat
Klasa do wyświetlania. Ta klasa zajmie się problemami z lokalizacją dla kwot w różnych walutach. Jednak zajmie to tylko prymitywy; dlatego, jeśli możesz zaakceptować małą zmianę dokładności spowodowaną transformacją dodouble
, możesz użyć tej klasy. - podczas korzystania z klasy
NumberFormat
, Użyj metodascale()
na instancjiBigDecimal
do ustawiania dokładności i metody zaokrąglania.
PS: gdybyś się zastanawiał, BigDecimal
jest zawsze lepsze niż double
, gdy musisz reprezentować wartości pieniądza w Javie .
PPS:
Tworzenie instancji BigDecimal
Jest to dość proste, ponieważ BigDecimal
dostarcza konstruktorów do przyjmowania prymitywnych wartości i String
obiektów. Możesz użyć tych, najlepiej tych, które biorą String
obiekt . Na przykład,
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
Wyświetlanie BigDecimal
instancji
Można użyć wywołań metod setMinimumFractionDigits
i setMaximumFractionDigits
, aby ograniczyć ilość wyświetlanych danych.
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:01
Polecam trochę badań na temat wzorca pieniędzy. Martin Fowler w swojej książce Analysis pattern opisał to bardziej szczegółowo.
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
@Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
Przychodząc do użycia:
Reprezentowałbyś wszystkie pieniądze używając Money
obiektu w przeciwieństwie do BigDecimal
. Reprezentowanie pieniędzy jako dużych dziesiętnych będzie oznaczać, że będziesz miał sformatować pieniądze w każdym miejscu, w którym je wyświetlasz. Wyobraź sobie, że standard wyświetlania się zmieni. Będziesz musiał dokonać zmian w każdym miejscu. Zamiast używać Money
wzorzec scentralizuj formatowanie pieniędzy w jednym miejscu.
Money price = Money.dollars(38.28);
System.out.println(price);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-02 12:33:34
Lub czekać na JSR-354 . Java Money and Currency API już wkrótce!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-31 17:56:55
1) jeśli jesteś ograniczony do precyzji double
, jednym z powodów, aby użyć BigDecimal
s, jest realizacja operacji z BigDecimal
s utworzonymi z double
s.
2) BigDecimal
składa się z dowolnie precyzyjnej wartości całkowitej bezskalowanej i nieujemnej 32-bitowej skali całkowitej, podczas gdy double owija w obiekt wartość prymitywnego typu double
. Obiekt typu Double
zawiera pojedyncze pole, którego typem jest double
3) It should make no difference
Nie powinieneś mieć trudności z $ i precyzją. Jednym ze sposobów jest użycie System.out.printf
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-14 21:37:37
Prymitywne typy liczbowe są przydatne do przechowywania pojedynczych wartości w pamięci. Ale przy obliczaniu za pomocą typów double I float występują problemy z rounding.It dzieje się tak, ponieważ reprezentacja pamięci nie odwzorowuje dokładnie wartości. Na przykład, Podwójna wartość powinna przyjmować 64 bity, ale Java nie używa wszystkich 64 bits.It przechowuje tylko to, co myśli, ważne części numeru. Możesz więc dojść do niewłaściwych wartości, gdy dodajesz wartości razem z float lub podwójny typ.
Zobacz krótki klip https://youtu.be/EXxUSz9x7BM
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-01 16:16:20
Użyj BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP)
, gdy chcesz zaokrąglić do 2 punktów po przecinku za centy. Należy jednak pamiętać o zaokrągleniu błędu podczas wykonywania obliczeń. Musisz być konsekwentny, gdy będziesz robić zaokrąglanie wartości pieniędzy. Albo po wykonaniu wszystkich obliczeń wykonaj zaokrąglenie bezpośrednio na końcu, albo zastosuj zaokrąglenie do każdej wartości przed wykonaniem jakichkolwiek obliczeń. To, którego użyć, zależy od wymagań biznesowych, ale ogólnie myślę, że robienie zaokrąglenia na końcu wydaje się dla mnie lepszy rozsądek.
Użyj String
, gdy konstruujesz BigDecimal
dla wartości pieniądza. Jeśli użyjesz double
, będzie ona miała końcową wartość zmiennoprzecinkową na końcu. Wynika to z architektury komputera, w jaki sposób double
/float
wartości są reprezentowane w formacie binarnym.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-14 21:38:32
Jest obszerny przykład jak to zrobić na javapractices.com. Zobacz w szczególności Money
klasa, która ma na celu uproszczenie obliczeń pieniężnych niż bezpośrednie użycie BigDecimal
.
Konstrukcja tej klasy Money
ma na celu uczynienie wyrażeń bardziej naturalnymi. Na przykład:
if ( amount.lt(hundred) ) {
cost = amount.times(price);
}
Narzędzie WEB4J ma podobną klasę, zwaną Decimal
, która jest nieco bardziej dopracowana niż klasa Money
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-14 21:39:12
NumberFormat.getNumberInstance(java.util.Locale.US).format(num);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-01 23:32:35