Использование BigDecimal для работы с валютами

Я пытался сделать свой собственный класс для валют, используя longs, но, по-видимому, я должен использовать . Может кто-нибудь помочь мне начать? Какой был бы лучший способ использовать BigDecimals для долларовых валют, например, сделать это, по крайней мере, но не более 2 десятичных знаков для центов и т. д. API для BigDecimal огромный, и я не знаю, какие методы использовать. Кроме того,BigDecimal имеет лучшую точность, но разве это не все потеряно, если он проходит через double? если я сделаю новый BigDecimal(24.99), как это отличаться от использования double? Или я должен использовать конструктор, который использует String вместо?

8 ответов


вот несколько советов:

  1. использовать BigDecimal для вычислений, если вам нужна точность, которую он предлагает (денежные значения часто нуждаются в этом).
  2. использовать NumberFormat класс для отображения. Этот класс позаботится о проблемах локализации для сумм в разных валютах. Однако он будет принимать только примитивы; поэтому, если вы можете принять небольшое изменение точности из-за преобразования в double, вы можете использовать это класс.
  3. при использовании NumberFormat класс, используйте scale() метод BigDecimal экземпляр для установки точности и метода округления.

PS: Если вам интересно,BigDecimal всегда лучше, чем double, когда вы должны представлять денежные значения в Java.

PPS:

создания BigDecimal экземпляров

это довольно просто, так как BigDecimal обеспечивает конструкторов возьмите примитивные значения и String объекты. Вы могли бы использовать желательно принимать String объект. Например,

BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);

показывать BigDecimal экземпляров

можно использовать setMinimumFractionDigits и setMaximumFractionDigits вызовы методов для ограничения объема отображаемых данных.

NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );

Я бы рекомендовал немного исследовать денежный шаблон. Мартин Фаулер в своей книге "Analysis pattern" описал это более подробно.

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();
    }   
}

переход к использованию:

вы будете представлять все деньги, используя Money объект в отличие от BigDecimal. Представление денег как большого десятичного знака будет означать, что у вас будет форматировать деньги каждый раз, когда вы его отображаете. Только представьте, если стандарт дисплея меняется. Вам придется вносить изменения повсюду. Вместо этого используйте Money шаблон вы централизовать форматирование денег в одном месте.

Money price = Money.dollars(38.28);
System.out.println(price);

или ждать JSR-354. Java деньги и валюта API в ближайшее время!


1) Если вы ограничены double точность, одна причина использовать BigDecimals должен реализовать операции с BigDecimals создано из doubles.

2) состоит из произвольного прецизионного целочисленного ненулевого значения и неотрицательной 32-разрядной целочисленной шкалы, в то время как double обертывает значение примитивного типа double в объекте. Объект типа Double содержит одно поле, тип которого double

3) не должно быть разница

у вас не должно быть никаких трудностей с $ и точностью. Один из способов сделать это-использовать System.out.printf


примитивные числовые типы полезны для хранения одиночных значений в памяти. Но при работе с вычислениями с использованием типов double и float возникают проблемы с округлением.Это происходит потому, что представление памяти не соответствует точно значению. Например, двойное значение должно принимать 64 бита, но Java не использует все 64 бита.Он хранит только то, что он думает, важные части числа. Таким образом, вы можете прийти к неправильным значениям при добавлении значений вместе с float или двойной тип.

пожалуйста, смотрите короткий клип https://youtu.be/EXxUSz9x7BM


использовать BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP) когда вы хотите округлить до 2 знаков после запятой для центов. Однако помните об ошибке округления при выполнении вычислений. Вам нужно быть последовательным, когда вы будете делать округление денежной стоимости. Либо сделайте округление прямо в конце только один раз после выполнения всех вычислений, либо примените округление к каждому значению перед выполнением любых вычислений. Какой из них использовать будет зависеть от вашего бизнес-требования, но в целом, я думаю, что округление прямо в конце лучше объясни мне.

использовать String при создании BigDecimal за деньгами. Если вы используете double, он будет иметь значения с плавающей запятой в конце. Это связано с компьютерной архитектурой относительно того, как double/float значения представлены в двоичном формате.


существует обширный пример того, как это сделать на javapractices.com - ... См., в частности,Money класс, который предназначен для упрощения денежных расчетов, чем использование BigDecimal напрямую.

дизайн этого Money class предназначен для того, чтобы сделать выражения более естественными. Например:

if ( amount.lt(hundred) ) {
 cost = amount.times(price); 
}

инструмент WEB4J имеет аналогичный класс, называемый Decimal, который немного более отполирован, чем Money класса.


NumberFormat.getNumberInstance(java.util.Locale.US).format(num);