Какой метод округления следует использовать в Java для денег?

Предположим, у меня есть десятичное значение в Java, которое представляет деньги.

Как лучше всего округлить эти значения?

Например, если у меня есть значение, рассчитанное на основе ставки налога, и я получаю результат, скажем, 5.3999999999999995 в качестве суммы налога, должен ли я округлить его до 2 знаков после запятой, просто выполнив следующее:

double d = 5.3999999999999995
BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

для получения значения валюты:

5.40

person Tom Currency    schedule 05.05.2011    source источник
comment
Точная формула, используемая для округления значений, обычно указывается в договоре, который имеет юридическую силу. Тем не менее, вы не должны никогда использовать double для обозначения денежных сумм.   -  person Laurent Pireyn    schedule 06.05.2011
comment
Я понимаю. Почему вы не должны использовать doubles? Float лучше? Я подумал, что лучше иметь больше точности, чем меньше, и поэтому использовать doubles как можно больше. Но я рад, что меня поправили.   -  person Tom Currency    schedule 06.05.2011
comment
плавать тоже не выход   -  person Woot4Moo    schedule 06.05.2011
comment
@Woot4Moo: Какая-то конкретная причина?   -  person Tom Currency    schedule 06.05.2011
comment
@Tom float по своей сути неточен. эта статья может помочь. support.microsoft.com/kb/125056 суть в том, что компьютер не знает, как для представления доли целого числа.   -  person Woot4Moo    schedule 06.05.2011
comment
Спасибо за ссылку. Не знал этого. Хорошо, что я это вовремя узнал.   -  person Tom Currency    schedule 06.05.2011
comment
Правила округления также зависят от валюты.   -  person biziclop    schedule 06.05.2011
comment
@Tom: Нет, двоичные числа с плавающей запятой абсолютно не предназначены и не подходят для использования в качестве денег. Они были разработаны в основном для численного применения (в технике и науке). Подробнее читайте здесь: floating-point-gui.de   -  person Michael Borgwardt    schedule 06.05.2011
comment
@Woot4Moo: компьютеры могут прекрасно представлять дроби, и числа с плавающей запятой делают именно это. Проблемы возникают только тогда, когда люди ожидают поведения десятичных дробей при использовании двоичного формата. Никто не удивляется, когда есть задачи, точно представляющие 1/3.   -  person Michael Borgwardt    schedule 06.05.2011


Ответы (4)


Большинство приложений, вычисляющих деньги, не используют числа с плавающей запятой (double, float); они используют целые числа, представляющие меньшую единицу. Например, в долларах США деньги могут быть представлены в пенни, что составляет 1/100 доллара.

Для большей точности вы можете захотеть, чтобы целое число представляло 1E-03 ("миллидоллары") или 1E-06. Это зависит от таких вопросов, как расчет процентов и ваш уровень точности.

person Thomas Matthews    schedule 05.05.2011
comment
Плохо ли использовать плавающую точку для денег? Я думал, что это именно то, для чего нужна плавающая точка? Не стесняйтесь поправлять меня - я действительно не знаю. - person Tom Currency; 06.05.2011
comment
да, плохо использовать плавающую точку для денег. Нет, числа с плавающей запятой не были разработаны для представления денег. Я думаю, что научные вычисления имели к этому гораздо большее отношение. - person duffymo; 06.05.2011
comment
Плавающие точки дают вам 3,4444444444 доллара, которые трудно обналичить - person bwawok; 06.05.2011
comment
+1 за неиспользование float или double для валюты. Гораздо лучше использовать целое число и думать о долларах как о центах. - person hooknc; 06.05.2011
comment
Целые числа — это только неприятный хак, когда у вас нет чего-то вроде BigDecimal, который Том по иронии судьбы уже использует, но не полностью. - person Michael Borgwardt; 06.05.2011

Я согласен с @Laurent Pireyn, что вы должны округлять в соответствии с контрактом. Например, IRS требует округления до ближайшего доллара. Они говорят

По возвращении вы можете округлить центы до целых долларов. Если вы округляете до целых долларов, вы должны округлить все суммы. Чтобы округлить, уменьшите суммы до 50 центов и увеличьте суммы от 50 до 99 центов до следующего доллара. Например, 1,39 доллара США становятся 1 долларом, а 2,50 доллара становятся 3 долларами.

Хорошее использование RoundingMode.HALF_EVEN. Это избавляет вас от необходимости писать и тестировать функцию.

Если это действительно ставка налога для IRS, я думаю, что RoundingMode.HALF_UP будет правильным.

person rajah9    schedule 05.05.2011

Деньги обычно округляются по правилу "округления от половины до четного" (также называемому "банковским округлением"). Оно гласит, что ничьи разбиваются путем выбора ближайшей четной цифры. Можно показать, что это устраняет погрешность при округлении. как для положительных, так и для отрицательных значений (это не единственное правило округления, обладающее этим свойством, но оно полезно при работе с деньгами).

Я не думаю, что это правило доступно в стандартном API, хотя написать его несложно.

person Ted Hopp    schedule 05.05.2011
comment
Правило очень доступно в стандартном API - фактически дважды: download.oracle.com/javase/6/docs/api/java/math/ и download.oracle.com/javase/6/docs/api/java/math/ - person Michael Borgwardt; 06.05.2011
comment
Правильно ли я понимаю, что правило округления от половины до четного — это то, что я использую в приведенном выше примере, и могу ли я просто придерживаться приведенного выше кода для округления банкира? - person Tom Currency; 06.05.2011
comment
@Майкл - Спасибо. Я должен был копаться в документах немного глубже. - person Ted Hopp; 06.05.2011

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

Единственное, что вы ДОЛЖНЫ сделать, это сделать последний шаг и использовать BigDecimal для представления денежных значений в вашем приложении. Это именно то, для чего он предназначен, и он гораздо лучше подходит для этой задачи, чем double, поскольку он может точно представлять десятичные дроби, как и ожидалось.

person Michael Borgwardt    schedule 06.05.2011