Представлення грошових цінностей на Java [закрито]


94

Я розумію, що BigDecimal рекомендується найкращою практикою для представлення грошових цінностей на Java. Що ви використовуєте? Чи є краща бібліотека, яку ви віддаєте перевагу використовувати натомість?


4
погляньте на JSR 354
yegor256

1
Ось один клас валют, який ви можете скопіювати та продовжити: java-articles.info/articles/?p=254
Гілберт Ле Блан

Також див. Довідкову реалізацію JSR-354 github.com/JavaMoney/jsr354-ri
битий

Відповіді:


81

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

Редагувати: Як згадує Дон у своїй відповіді , існують проекти з відкритим джерелом, такі як timeandmoney , і хоча я аплодую їм за те, що вони намагаються перешкодити розробникам заново винаходити колесо, я просто не маю достатньої впевненості в бібліотеці пре-альфа для використання це у виробничому середовищі. Крім того, якщо ви покопаєтесь під капотом, ви побачите, що вони BigDecimalтеж використовують .


4
+1. Ми вирішили також додати клас контейнера, який споживає валюту. Це стає в нагоді при відображенні грошових значень у таблицях.
Деніел Хіллер,

1
так, це досить поширений підхід, і це має багато сенсу. Одне застереження - це коли вам доводиться мати справу з японською ієною, оскільки у них немає незначної номіналу валюти, як центи, тому для цього потрібні власні правила округлення.
нулісидом

3
@ninesided наводить чудовий приклад того, чому прокат власного - погана відповідь. "О, і, до речі, це не працює за $ CURRENCY_X." Це хороший знак, що це також не працює для багатьох інших валют.
James Moore

1
@JamesMoore Я не згоден з тим, що "прокручувати своє" - це поганий підхід, вам просто потрібно знати про можливі обмеження обраного вами підходу, отже, причина того, що я про це згадую. Тривіально впроваджувати різні правила округлення для кожної валюти, але якщо вашій системі потрібно обмінюватися лише в доларах США або євро, то вам не потрібно надто інженерно розробляти речі.
дезінсекцією

1
Погляньте на stackoverflow.com/questions/5134237/… лише з однієї причини, чому BigDecimal є проблемою. Бухгалтерський облік на всій планеті - це просто болото особливих випадків, і спроба підмітати їх усіх під килимом BigDecimal просто не працює.
Джеймс Мур,

52

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


Дякую. Я мав намір додати наступну нотатку про Joda Money. Чи використовували ви це?
dshaw

2
+1 виглядає цікаво, радий бачити, що він BigDecimalпід капотом!
нулісидом


8

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

Ця бібліотека має невелику кількість файлів, які можна швидко переглянути, якщо потрібні зміни. Joda-Money видається під ліцензією Apache 2.0.


7

Якщо ви просто використовуєте долари та центи, я б використовував лонг (компенсований на 2 знаки після коми). Якщо вам потрібні докладніші відомості, може знадобитися великий десятковий знак.

У будь-якому випадку, я б, напевно, розширив клас, щоб мати .toString (), який використовує правильний формат, і як місце, де можна розмістити інші методи, які можуть виникнути (Довгий час множення і ділення підуть не так, якщо десяткове значення isn не відрегульовано)

Крім того, якщо ви використовуєте визначення власного класу та інтерфейсу, ви можете замінити реалізацію за бажанням.


2
Слідкуйте, навіть довгий час може бути занадто коротким, щоб утримувати федеральний борг США в центрах ... якщо не зараз, то через кілька років.
Інго

3
Я згоден - будь-яка величезна кількість доларів (або, можливо, якщо ви відстежуєте гроші в ієнах), ви повинні використовувати BigDecimal - але навіть тоді я серйозно задумався б використовувати для цього клас контейнера. Я думаю, що найбільша складність програмування походить від людей, які не визначають малі, прості класи навколо колекцій та внутрішніх типів.
Bill K

3

BigDecimal або інше подання з фіксованою точкою - це те, що зазвичай потрібно для грошей.

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


7
Власне кажучи, BigDecimal також є неточним; він просто краще відповідає десятковому округленню, до якого ми звикли в повсякденному житті, і дозволяє задати режими округлення.
Michael Borgwardt

1
@Michael Borgwardt BigDecimal відрізняється від IEEE FP тим, що вказано явний масштаб. Хоча не всі операції є точними, це гарантує, що набір операцій і поведінки завжди є точним, а масштаб є постійним, тоді як шкала для IEEE FP зменшується зі значенням.

1
Яке відношення це має до грошей? Бухгалтерські організації у всьому світі зазвичай мають дуже конкретні вимоги до того, як ви робите математику в їх валюті. Чи точно відповідає BigDecimal кожному з цих стандартів? Чи буде це робити наступного року, коли ці стандарти зміняться? А BigDecimal навіть не наближається до визначення корисних правил округлення валют.
Джеймс Мур,

2

Ви повинні бути настільки обережними, маючи справу з часом і грошима.

Коли ви працюєте з грошима, я сподіваюся, що всі повинні знати, що ніколи не використовувати поплавок або дубль.

Але я не впевнений щодо BigDecimal.

У більшості випадків вам буде добре, якщо ви просто будете відстежувати центи в int або long. Таким чином ви ніколи не маєте справу з десятковою комою.

Ви відображаєте долари лише тоді, коли їх друкуєте. Завжди працюйте з центрами, використовуючи цілі числа. Це може бути складно, якщо потрібно розділити або використовувати Math.abs ().

Тим не менш, ти можеш піклуватися про піввідсотка, а то й про соту відсоток. Я не знаю, який хороший спосіб це зробити. Можливо, вам просто доведеться мати справу з тисячною копійкою і використовувати довгу. Або, можливо, вас змусять використовувати BigDecimal

Я б набагато більше читав про це, але ігнорував усіх, хто починає говорити про використання поплавка або дубля для представлення грошей. Вони просто просять неприємностей.

Я вважаю, що моя порада не є повною, тому, будь ласка, вкладіть більше. Ви маєте справу з небезпечними типами!


2
Чому вам потрібно "змусити" використовувати BigDecimal? У чому ви не впевнені? Це явно перевершує роботу з центами, оскільки дозволяє чітко вказати режими округлення.
Michael Borgwardt

1
@MichaelBorgwardt: так, це дозволяє вказати крихітну підмножину режимів округлення, необхідних для валют. Так? (Підказка: округлення валют вирішують, як правило, національні бухгалтерські організації. Вони цілком раді кидати в дивних особливих випадках. Див. Stackoverflow.com/questions/5134237/… лише з однієї з багатьох цікавих причин, чому округлення BigDecimal повністю тут марно.)
Джеймс Мур,

@James: Як саме це "марно"? Як реалізація спеціальних випадків буде важче з BigDecimal, ніж з чимось іншим?
Michael Borgwardt

1
Добре, абсолютно марний занадто сильний. У складному класі, що абстрагує валюту, правила округлення BigDecimal, мабуть, корисні в деяких конкретних випадках для побудови підмножини способів округлення валют. Але загальний випадок полягає в тому, що правила округлення для валют вимагають механізмів, які можуть змінюватися з часом (оскільки бухгалтерські агенції складають правила і можуть вільно їх змінювати). Питання не в євро (або що-небудь, що замінить євро в наступному місяці ...), чи доларах у 2011 році, а в валюті, тому вам доведеться мати справу з безліччю неприємної складності.
James Moore

2

Створення класу Гроші - це шлях. Використання BigDecimal (або навіть int) знизу. Потім за допомогою класу Currency визначити конвенцію округлення.

На жаль, без перевантаження оператора Java стає досить неприємним створення таких базових типів.


2

Є краща бібліотека, час і гроші . IMO значно перевершує бібліотеки, надані JDK для представлення цих 2 концепцій.


3
Ця відповідь була опублікована три роки тому. Сьогодні проект timeandmoney все ще є попередньо альфа-версією відповідно до цього посилання.
James Moore

1
@JamesMoore Гарний дзвінок. Відповіді зараз 7 років, і проект все ще не стабільний.
Navin

1

Однозначно не BigDecimal. Існує стільки спеціальних правил для округлення та подання, що вам доведеться турбуватися.

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


6
і основний тип даних його класу Money? BigDecimal.
нулісидом

1
Це не правда. Ви можете використовувати Integer у грошовому класі, що і робить Мартін. Я робив це багато разів.
egervari

Однак рекомендація є правильною; розрахунки, що стосуються грошей, - це величезне болото особливих випадків, які з часом змінюються. BigDecimal може бути корисним як крихітна частина рішення, але він, звичайно, не є загальним.
James Moore

1

Гей, ось дуже цікава стаття про BigDecimal та наочний приклад того, чому іноді її використовують замість подвійних. Підручник з BigDecimal .


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
Здається, це коментар до іншого повідомлення замість фактичної відповіді. Це також надмірно купорос. Будь ласка, намагайтеся бути більш цивільними в майбутньому.
Слейтер Вікторов,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.