2008-12-09 2 views
12

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

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

Существенные требования: (a) бережливый след (2 класса плюс исключение OverflowException) и (b) полная поддержка всех основных операций плюс вся математика, которая имеет смысл.

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

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

EDIT: Это отличается от BigDecimal, поскольку оно должно быть (a) чертовски более эффективным, чтобы не иметь дело с массивом ints и (b) оно не будет обертывать BigInteger, поэтому оно будет более компактным и (c) он будет иметь изменяемый вариант, так что он будет быстрее и там. В итоге - меньше накладных расходов для простых случаев использования, таких как «Я хочу сохранить баланс банка без накладных расходов BigDecimal и неточности двойного».

EDIT: Я намерен делать всю математику, используя Int или долго, чтобы избежать классической проблемы: 1586.60-708.75 = 877,8499999999999 вместо 877,85

+0

"Или это уже было сделано?" Чем отличаются предлагаемые классы и BigDecimal? – 2008-12-09 20:12:05

+0

Когда вы говорите «с произвольным числом десятичных знаков», вы имеете в виду, что у вас есть фиксированное число десятичных знаков или вы хотите иметь возможность поддерживать любое количество десятичных знаков? – DJClayworth 2008-12-10 14:25:01

+0

Скорее всего, было бы быстрее написать его, а иметь дело с людьми, которые не понимают ваш вопрос здесь. – jmucchiello 2008-12-11 03:42:05

ответ

0

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

Также интересует библиотека constructive reals. Однако он не легкий.

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

+0

Реальный выглядит хорошо, но ... это GPL'd, поэтому он не может использовать его в коммерческих целях, и, разве это еще не математика с плавающей запятой, поэтому он страдает от проблем с десятичными дробями, которые не могут быть представлены в двоичном формате? – 2008-12-09 21:47:10

+0

Я не понимал, что у вас есть требование к лицензии. – WolfmanDragon 2008-12-11 01:53:27

-1

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

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

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

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

0

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

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

12

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

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

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

1

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

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

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

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

печатает

877.85 
Смежные вопросы