Легкий Java десятичный класс - PullRequest
12 голосов
/ 09 декабря 2008

Я рассматриваю возможность написания двух альтернатив с ограниченной точностью для BigDecimal, а именно DecimalInt и DecimalLong. Они могли бы иметь дело с числами в пределах реальных границ int и long с произвольным числом десятичных знаков, создаваемых как в изменяемой, так и в неизменяемой форме. Мой план состоит в том, чтобы сделать поддержку DecimalInt от +/- 999,999,999 до +/- 0,9999999999 и DecimalLong одинаковыми, но с до 18 цифрами.

Это можно сделать, поддерживая значение счетчика десятичных цифр от 0 до 9 для DecimalInt и от 0 до 18 для DecimalLong вдоль фактического значения, сохраненного как масштабированное int или long. Обычно используется для небольшого числа десятичных знаков, таких как деньги и цены на акции, обычно 2-4 знака после запятой.

К основным требованиям относятся: (а) экономная площадь (2 класса плюс OverflowException) и (б) полная поддержка всех базовых операций и всей математики, которая имеет смысл.

Поиск в результатах поиска не дал никаких очевидных результатов - все они, похоже, имели отношение к произвольным десятичным знакам.

Мои вопросы: это уже сделано? Есть ли в этом скрытые тонкости, поэтому это еще не сделано? Кто-нибудь слышал слухи о том, что Java поддерживает десятичный тип, такой как DotNet.

РЕДАКТИРОВАТЬ: Это отличается от BigDecimal, потому что это должно быть (а) намного более эффективным, чтобы не иметь дело с массивом целых чисел, и (б) он не обернет BigInteger, так что он будет меньше памяти также, и (c) у него будет изменяемая опция, поэтому он будет быстрее там. В итоге - меньше накладных расходов для простых случаев использования, таких как «Я хочу сохранить баланс в банке без накладных расходов на BigDecimal и неточность двойного».

РЕДАКТИРОВАТЬ: Я собираюсь выполнить всю математику, используя int или long, чтобы избежать классической проблемы: 1586.60-708.75 = 877.8499999999999 вместо 877.85

Ответы [ 5 ]

12 голосов
/ 11 декабря 2008

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

В качестве примера: для любого финансового приложения сохранение нескольких десятков байт не является проблемой и ограничивает точность сделки (в США цены на акции обычно составляют 2-4 цифры, но если вы хотите иметь дело на развивающихся рынках вы столкнетесь с валютами с безудержной инфляцией, где сумма в 15 цифр приносит вам половину буханки хлеба.

По сути, это звучит как очередной случай преждевременной оптимизации.

1 голос
/ 24 ноября 2010

Большинство людей, которые особенно обеспокоены ошибками округления, используют BigDecimal и BigInteger, которые достаточно хорошо работают в большинстве ситуаций.

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

В подавляющем большинстве случаев двойники с округлением - это все, что вам нужно.

System.out.printf("%.2f%n", 1586.60-708.75);

печать

877.85
0 голосов
/ 10 декабря 2008

Мне кажется, что если вам нужна произвольная точность, вам понадобится неопределенное количество бит для представления мантиссы. Это означает, что для мантиссы будет необходима какая-то стратегия размещения массивов. Вы можете создать свой собственный здесь, но BigInteger делает это довольно эффективно, и это работает

Вам необходимо указать, какое наименьшее (ненулевое) значение вам нужно представить. Это будет 10 ^ - (2 ^ n), где n + 1 - это количество битов, которые вы выделяете для экспоненты. С BigDecimal это 10 ^ - (2 ^ 31). Вы можете использовать показатель произвольного размера, но этого диапазона должно быть достаточно для всех.

Итак, вам нужна неограниченная целочисленная мантисса, чтобы дать вам произвольную точность, и показатель фиксированного размера, в зависимости от того, каким вы хотите, чтобы ваше минимальное представимое значение было. По сути это BigDecimal; единственное изменение - вы будете использовать какой-то объект меньшего размера, а не int, используемый BigDecimal. Я бы сомневался, стоит ли экономия места. Я бы подумал, что BigDecimal сделает то, что вам нужно, с чуть большим использованием памяти, чем любое решение, которое вы создадите сами.

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

0 голосов
/ 10 декабря 2008

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

Если вы имеете дело с деньгами, вам нужно быть осторожным с тем, как вы справляетесь с округлением. Если ваши расчеты будут проверяться, есть правила, как это делается. Также я предполагаю, что вы знаете, что некоторые операции не могут быть выполнены точно (деление является очевидным примером).

0 голосов
/ 09 декабря 2008

Если вы ориентированы на портативные устройства, посмотрите на Real . Real позволяет установить точность номера от 0 до 16. Он предназначен для сотовых телефонов MIDP.

Также интересно взглянуть на библиотеку construals reals . Это не легкий, хотя.

Ссылаясь на комментарий ниже, нельзя ли использовать Apache Commons Math Library для работы с дробями? Есть ли какая-то причина, которая не будет работать?

...