Статьи

Работа с деньгами в Java

У меня новый любимый блог. Питер Лори ведет блог под названием « Ванильная Ява ». Все дело в «Понимании того, как на самом деле работает Core Java». Питер — DZone MVB, он публикует сообщения о скорости и размерах различных объектов и технологий в Java. Один конкретный пост недавно привлек мое внимание и заставил меня задуматься.

 

Рисунок:
Images_of_Money (
CC BY 2.0 ) (
TaxBrackets.org )

Это «
Удвоить ваши деньги снова », в котором Питер говорит о проблемах округления при использовании java.lang.Double, и это примитивный тип double. Это та область, о которой я немного знаю и много видел. И я подумал, что немного поделюсь своим опытом здесь.

Если вы используете double, вы сталкиваетесь с проблемами в

основном из-за того факта, что
double (Wrapper:
Double ) является 64-битной плавающей точкой IEEE 754 с двойной точностью. Он не предназначен для хранения точных десятичных значений.

double result = 0.1 + 0.2 - 0.3;
System.out.println("0.1 + 0.2 - 0.3=" + result);
=> 0.1 + 0.2 - 0.3=5.551115123125783E-17

Представление .toString () следует некоторым (не таким простым) правилам:


Если m больше или равно 10-3, но меньше 107, то оно представляется как целая часть числа m в десятичной форме без начальных нулей, за которой следует ‘.’
(‘\ u002E’), за которым следуют одна или несколько десятичных цифр, представляющих дробную часть m.

Если m меньше 10-3 или больше или равно 107, то оно представлено в так называемой «компьютеризированной научной нотации». Пусть n — единственное целое число такое, что 10n ≤ m & lt; 10n + 1; тогда пусть a будет математически точным отношением m и 10n, так что 1 ≤ a & lt; 10. Величина затем представляется как целая часть a, как одна десятичная цифра, за которой следует «.» (‘\ u002E’), за которыми следуют десятичные цифры, представляющие дробную часть a, за которыми следует буква ‘E’ (‘\ u0045’), за которой следует представление n в виде десятичного целого числа, полученного методом Integer. ToString (INT).

(Источник:
java.lang.Double )

После этого вы столкнетесь с проблемами в представлении и расчетах, с которыми вам придется справиться.

Закручивание тай-брейка
Вернуться к первоначальному посту Питера. Он предлагает использовать алгоритм округления при работе с двойными числами, чтобы предотвратить неопределенные эффекты. Маленькие методы, которые он показывает, хороши: но Java уже знает о округлении и, кроме того, она уже знает о более чем одном алгоритме округления. Теперь вам решать выбрать лучший. У нас есть пара из них под рукой (сравните статью в Википедии ). Поехали:

ROUND_CEILING
Режим округления, чтобы округлить до положительной бесконечности.
ROUND_DOWN 
Режим округления до округления до нуля.
ROUND_FLOOR 
Режим округления для округления до отрицательной бесконечности.
ROUND_HALF_DOWN
Режим округления для округления в сторону «ближайшего соседа», если оба соседа не равноудалены, в этом случае округление в меньшую  сторону.
ROUND_HALF_EVEN
Режим округления для округления до «ближайшего соседа», если оба соседа не равноудалены, в этом случае округляются  до четного соседа.
ROUND_HALF_UP
Режим округления для округления в сторону «ближайшего соседа», если оба соседа не равноудалены, в этом случае округление в большую  сторону.
ROUND_UP 
Режим округления для округления от нуля.

Чтобы обработать результаты с помощью так называемого округления по Гауссу или банкирам, все, что вам нужно сделать, это установить шкалу на вашем BigDecimal.

double result = 0.1 + 0.2 - 0.3;
BigDecimal resultRounded = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("0.1 + 0.2 - 0.3=" + resultRounded);
 => 0.1 + 0.2 - 0.3=0.00

Здесь происходит некоторое преобразование. Как видите, я конвертирую двойное в BigDecimal. Это означает, что вы не можете использовать его для дальнейших расчетов. если вам нужен двойной, вы, вероятно, можете сделать следующее:

double result = new BigDecimal(value).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 

Но, как утверждает Питер в комментариях к своему сообщению, это, вероятно, не очень хорошо.

BigDecimal! = Сумма
Даже если вы теперь знаете, как избавиться от проблемы округления, у вас все еще есть простое число, а не сумма. Это означает, что вы должны исправить презентацию. Что должно быть просто, не так. Как всегда у вас есть пара подходов для оценки.

DecimalFormat
Предпочтительным способом является использование класса DecimalFormat для форматирования десятичных чисел в строки, зависящие от локали. Этот класс позволяет управлять отображением начальных и конечных нулей, префиксов и суффиксов, группирующих (тысяч) разделителей и десятичного разделителя. Вы можете получить экземпляр с помощью NumberFormat.getCurrencyInstance (locale)

double result = 0.1 + 0.2 - 0.3;
BigDecimal result2 = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP);
NumberFormat form = NumberFormat.getCurrencyInstance(new Locale("de", "DE"));
System.out.println("Amount: " + form.format(result2));
 => Amount: 0,00 €

Валюта
Если вам нужно больше контроля, чем у вас уже есть с предпочтительным способом, вы можете подумать об использовании класса Currency. Посмотрите на java.util.Currency, поскольку она представляет валюту, обозначенную ее кодом валюты ISO 4217 . Другой вариант — получить его с локалью.

Currency curr = Currency.getInstance("EUR");
Currency currLoc = Currency.getInstance(new Locale("de", "DE"));
System.out.println("currency EUR in en_US: "+curr.getSymbol(new Locale("de", "DE")));
System.out.println("currency in de_DE for en_US: "+currLoc.getSymbol(new Locale("en", "US")));
 =>currency EUR in en_US: €
 =>currency in de_DE for en_US: EUR

Итог
Если у вас есть шанс, продолжайте встраивать функциональность. Обе проблемы округления, как и вещи i18n, могут быть решены с помощью набора доступных утилит и классов. Если у вас есть свой собственный класс Amount, использующий механизмы пользовательской валюты, помните о том, что вы должны поместить символ валюты впереди или позже, в зависимости от локали, в которой вы находитесь. Итак: внутри не так много магии, и если вы используете это правильно, вам вообще не нужно бояться работать с деньгами в Java.

 

От http://blog.eisele.net/2011/08/working-with-money-in-java.html