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?

Author: mk12, 2009-09-01

8 answers

Oto kilka wskazówek:

  1. Użyj BigDecimal do obliczeń, jeśli potrzebujesz precyzji, którą oferuje (wartości pieniędzy często tego potrzebują).
  2. 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ą do double, możesz użyć tej klasy.
  3. podczas korzystania z klasy NumberFormat, Użyj metoda scale() na instancji BigDecimal 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()) );
 66
Author: Vineet Reynolds,
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);
 31
Author: Brad,
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!

 7
Author: Victor Grazi,
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 BigDecimals utworzonymi z doubles.

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

 1
Author: Diego Dias,
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

 1
Author: Gregory Nozik,
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.

 0
Author: tim_wonil,
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.

 0
Author: John O,
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);
 0
Author: Heisenberg,
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