Представление денежных значений в Java [закрыто]

94

Я понимаю, что BigDecimal является наилучшей практикой для представления денежных величин в Java. Что ты используешь? Есть ли лучшая библиотека, которую вы бы предпочли использовать?

dshaw
источник
4
взгляните на JSR 354
yegor256
1
Вот один класс валюты, который вы можете скопировать и расширить: java-articles.info/articles/?p=254
Gilbert Le Blanc
Также смотрите эталонную реализацию JSR-354 github.com/JavaMoney/jsr354-ri
бить

Ответы:

81

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

Изменить: как Дон упоминает в своем ответе , есть проекты с открытым исходным кодом, такие как timeandmoney , и хотя я аплодирую им за попытку помешать разработчикам изобретать велосипед, у меня просто недостаточно уверенности в предварительной альфа-библиотеке для использования это в производственной среде. Кроме того, если вы покопаетесь под капотом, вы увидите, что они BigDecimalтоже используют .

девяностый
источник
4
+1. Мы решили добавить класс контейнера, который тоже потребляет валюту. Это удобно при отображении денежных значений в таблицах.
Дэниел Хиллер,
1
да, это довольно распространенный подход, и он имеет большой смысл. Одно предостережение - когда вам приходится иметь дело с японской иеной, поскольку у них нет незначительного номинала валюты, такого как центы, поэтому для этого нужны собственные правила округления.
ninesided
3
@ninesided дает отличный пример того, почему прокатить собственный - плохой ответ. «Да, кстати, это не работает для $ CURRENCY_X». Это хороший знак, что он также не работает для многих других валют.
Джеймс Мур
1
@JamesMoore Я не согласен с тем, что «использовать свой собственный» - это плохой подход, вам просто нужно знать о возможных ограничениях выбранного вами подхода, поэтому я упомянул об этом. Реализовать разные правила округления для каждой валюты - тривиально, но если ваша система должна работать только в долларах США или евро, вам не нужно чрезмерно разрабатывать вещи.
ninesided 01
1
Взгляните на stackoverflow.com/questions/5134237/… только по одной причине, почему BigDecimal является проблемой. Бухгалтерский учет на всей планете - это просто болото особых случаев, и попытка замазать их все под ковриком BigDecimal просто не работает.
Джеймс Мур
52

Людям, попадающим сюда из поисковых систем, может быть полезно узнать о JodaMoney: http://www.joda.org/joda-money/ .

Иньяки Ибаррола Атча
источник
Спасибо. Я хотел добавить дополнительную заметку о Joda Money. Вы им пользовались?
dshaw
2
+1 выглядит интересно, рад видеть, что BigDecimalпод капотом!
ninesided 01
8

Удобная библиотека, с которой я столкнулся ранее, - это библиотека Joda-Money . Одна из его реализаций действительно основана на BigDecimal. Он основан на спецификации ISO-4217 для валют и может поддерживать настраиваемый список валют (загружаемый через CVS).

В этой библиотеке есть небольшое количество файлов, которые можно быстро просмотреть, если потребуются изменения. Joda-Money издается под лицензией Apache 2.0.

Башар
источник
7

Если вы просто используете доллары и центы, я бы использовал длинный (смещение на 2 десятичных знака). Если вам нужна более подробная информация, возможно, вам подойдет большой десятичный знак.

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

Кроме того, если вы используете свой собственный класс и интерфейс, вы можете заменить реализацию по своему желанию.

Билл К
источник
2
Осторожно, даже длинный срок может оказаться слишком коротким, чтобы удерживать федеральный долг США в центах ... если не сейчас, то через несколько лет.
Ingo
3
Я согласен - любые огромные суммы долларов (или, может быть, если вы отслеживаете деньги в иенах), вам следует использовать BigDecimal, но даже в этом случае я бы серьезно подумал об использовании для этого класса контейнера. Я думаю, что большая часть сложности программирования возникает из-за того, что люди не определяют небольшие простые классы вокруг коллекций и внутренних типов.
Bill K
3

BigDecimal или другое представление с фиксированной точкой - это то, что обычно требуется для денег.

Представления и вычисления с плавающей запятой ( Double, Float) неточны, что приводит к ошибочным результатам.

Кен Джентл
источник
7
Строго говоря, BigDecimal также неточен; он просто лучше соответствует десятичному округлению, к которому мы привыкли в повседневной жизни, и позволяет указывать режимы округления.
Майкл Боргвардт
1
@Michael Borgwardt BigDecimal отличается от IEEE FP тем, что указан явный масштаб. Хотя не все операции точны, это гарантирует, что набор операций и поведения всегда будет точным, а масштаб будет постоянным, тогда как масштаб для IEEE FP уменьшается со значением.
1
При чем здесь деньги? Бухгалтерские организации по всему миру обычно предъявляют особые требования к расчетам в их валюте. Соответствует ли BigDecimal каждому из этих стандартов? Произойдет ли это в следующем году, когда эти стандарты изменятся? А BigDecimal даже близко не подходит к указанию полезных правил округления для валют.
Джеймс Мур
2

Вы должны быть очень осторожны, имея дело со временем и деньгами.

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

Но насчет BigDecimal я не уверен.

В большинстве случаев все будет в порядке, если вы просто будете отслеживать центы в int или long. Таким образом, вы никогда не будете иметь дело с десятичным знаком.

Вы показываете только доллары при печати. Всегда работайте с внутренними центами, используя целые числа. Это может быть сложно, если нужно разделить или использовать Math.abs ().

Тем не менее, вы можете рассчитывать на полцента или даже сотую долю цента. Я не знаю, как это сделать. Возможно, вам просто нужно иметь дело с тысячными центами и использовать длинную. Или, может быть, вам придется использовать BigDecimal

Я бы прочитал об этом гораздо больше, но игнорирую всех, кто начинает говорить об использовании числа с плавающей запятой или удвоения для представления денег. Они просто напрашиваются на неприятности.

Я считаю, что мой совет неполный, поэтому, пожалуйста, вдавите в него больше. Вы имеете дело с опасными типами!

Пиролитический
источник
2
Зачем вас «заставляют» использовать BigDecimal? В чем вы не уверены? Он явно превосходит работу с центами, поскольку позволяет явно указывать режимы округления.
Майкл Боргвардт
1
@MichaelBorgwardt: да, он позволяет указать небольшое подмножество режимов округления, которые вам нужны для валют. Так? (Подсказка: решение об округлении валют, как правило, принимают национальные бухгалтерские организации. Они с радостью подбрасывают странные особые случаи. См. Stackoverflow.com/questions/5134237/... только одну из многих забавных причин, по которым округление BigDecimal полностью здесь бесполезно.)
Джеймс Мур
@James: Как именно это «бесполезно»? Насколько сложнее реализовать эти особые случаи с BigDecimal, чем с чем-то еще?
Майкл Боргвардт
1
Хорошо, совершенно бесполезно - это слишком сильно. В сложном классе, который абстрагирует валюту, правила округления BigDecimal, вероятно, полезны в некоторых конкретных случаях для создания подмножества способов округления валют. Но в общем случае для правил округления валют требуются механизмы, которые могут меняться с течением времени (поскольку бухгалтерские агентства составляют правила и могут их изменять). Речь идет не о евро (или о том, что заменит евро в следующем месяце ...) или долларах в 2011 году, а о валюте, поэтому вам придется иметь дело с множеством неприятных сложностей.
Джеймс Мур
2

Создание класса Money - это лучший способ. Используя BigDecimal (или даже int) внизу. Затем с помощью класса Currency для определения соглашения об округлении.

К сожалению, без перегрузки операторов Java делает довольно неприятным создание таких базовых типов.

Козярчук
источник
2

Есть лучшая библиотека, время и деньги . IMO, он намного превосходит библиотеки, предоставляемые JDK для представления этих двух концепций.

Dónal
источник
3
Этот ответ был опубликован три года назад. Сегодня, судя по этой ссылке, проект timeandmoney находится на стадии предварительной альфа-версии.
Джеймс Мур
1
@JamesMoore Хороший звонок. Ответу сейчас 7 лет, а проект все еще нестабилен.
Navin
1

Определенно не BigDecimal. Существует так много особых правил округления и представления, о которых вам нужно беспокоиться.

Мартин Фаулер рекомендует реализовать специальный класс Money для представления денежных сумм, а также реализует правила конвертации валюты.

Линделоф
источник
6
и базовый тип данных его класса Money? BigDecimal.
ninesided
1
Это не правда. Вы можете использовать Integer в классе денег, что и делает Мартин. Я делал это много раз.
egervari
Однако рекомендация верна; денежные расчеты - это огромное количество особых случаев, которые со временем меняются. BigDecimal может быть полезен как крошечная часть решения, но, конечно, не в общем.
Джеймс Мур
1

Эй, вот очень интересная статья о BigDecimal и наглядный пример того, почему иногда его используют вместо двойников. BigDecimal Учебник .

arg20
источник
0

Вы можете использовать класс DecimalFormat при окончательном отображении значения валюты. Он обеспечивает поддержку локализации и довольно расширяемый.


источник
0

Я бы инкапсулировал BigDecimal в классе Money, который также имеет валюту, как упоминалось выше. Важно то, что вы делаете очень много юнит-тестов, особенно при работе с разными валютами. Также будет хорошей идеей, если вы добавите удобный конструктор, который принимает строку или фабричный метод, который делает то же самое, чтобы вы могли писать свои тесты примерно так:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));
Пер Арненг
источник
0

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

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal вряд ли является единственно правильным представлением или единственным фрагментом головоломки. При определенных условиях использования класса Money, поддерживаемого центами, хранящимися в виде целого числа, может быть достаточно, и это будет намного быстрее, чем BigDecimal. Да, это подразумевает использование долларов в качестве валюты и ограничивает суммы, но такие ограничения вполне приемлемы для многих случаев использования, и все валюты в любом случае имеют особые случаи для округления и суб-деноминации, поэтому «универсального» решения не существует.

Крейг
источник
1
Кажется, это комментарий к другому сообщению, а не фактический ответ. К тому же он излишне едкий. Пожалуйста, постарайтесь быть более вежливым в будущем.
Slater Victoroff