Использование BigDecimal для работы с валютами
Я пытался сделать свой собственный класс для валют, используя longs, но, по-видимому, я должен использовать . Может кто-нибудь помочь мне начать? Какой был бы лучший способ использовать BigDecimal
s для долларовых валют, например, сделать это, по крайней мере, но не более 2 десятичных знаков для центов и т. д. API для BigDecimal
огромный, и я не знаю, какие методы использовать. Кроме того,BigDecimal
имеет лучшую точность, но разве это не все потеряно, если он проходит через double
? если я сделаю новый BigDecimal(24.99)
, как это отличаться от использования double
? Или я должен использовать конструктор, который использует String
вместо?
8 ответов
вот несколько советов:
- использовать
BigDecimal
для вычислений, если вам нужна точность, которую он предлагает (денежные значения часто нуждаются в этом). - использовать
NumberFormat
класс для отображения. Этот класс позаботится о проблемах локализации для сумм в разных валютах. Однако он будет принимать только примитивы; поэтому, если вы можете принять небольшое изменение точности из-за преобразования вdouble
, вы можете использовать это класс. - при использовании
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);
1) Если вы ограничены double
точность, одна причина использовать BigDecimal
s должен реализовать операции с BigDecimal
s создано из double
s.
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
класса.