Как стать автором
Обновить

Комментарии 42

Отличная работа. Я тоже думал реализовать это именно таким же способом, но в итоге никак не дошли руки. Добавил в избранное, буду смотреть код и пул реквестить. Спасибо за реально полезную тулу. А то в энтерпрайз языке отсутствие нормальной реализации чисел с фикс точкой это ад. Хотя я помню, когда занимался бух задачами, находил какую-то библиотеку.
Спасибо. Улучшения, а особенно багфиксы всячески приветствуются
в смысле «нет нормальной реализации»? а упомянутый Biginteger куда делся? а вот ад (точнее трэш) это велосипед, который работает с финансами, при этом ради 1,5 кратного выигрыша
Вы точно прочитали статью и конкретное место, где я написал почему я не мог использовать BigDecimal? С BigInteger та же проблема.
Это реально вредная тулза. Аргументы против BigDecimal в высшей степени спорны. А подобная кастомщина в проекта начинает резко задирать Total cost of ownership. Потому что поведение библиотеки очевидно только самому автору и то пока не забыл. Приходит новый человек на проект (автор разумеется уже уволился) и видит этот треш. Вы реально думаете что словив багу он будет исправлять либу? Причем по уровню исполнения, эта библиотека написана в лучшем случае мидлом.
Причем по уровню исполнения, эта библиотека написана в лучшем случае мидлом.

А какие косяки в исполнении вы видите?

public static void main в Decimal? Locale dependent исполнение? Отсутствие форматтера/парсера? NaN, причем только отрицательный, про то что NaN может быть как положительный так и отрицательный не слышали. Константный scale? Open-Close principle violation? Подобный трешачок return a == NaN || b == NaN || (result < 0) != (a < 0) && (result < 0) != (b < 0)? NaN: result;

И самое главное — плохое API (можете сказать что делают product и quotient не глядя в исходники?) и мутабельность. От мутабельности уходят не просто так, а потому что это error-prone подход.
Кажется, автор уже несколько раз сказал что именно ради мутабельности все и делалось…
От того что глупость повторять много раз она не становится мудростью.

P.S. Искренне желаю вам использовать эту библиотеку в своих проектах.
Вы NaN с бесконечностями не попутали? Те бывают положительные и отрицательные, а NaN — это not a number.
Посмотрите исходники и как этот NaN используется. С моей точки зрения его вообще быть не должно, вместо этого следует кидать ArithmeticException.

Мне очень жаль, что вас огорчило нарушение Open-closed principle в библиотеке из 2 классов без планов расширения.


Ваши замечания насчёт getScale и ArithmeticException противоречат требованиям, из-за которых проект был создан. У меня сложилось впечатление, что вы эти требования не понимаете.


Желаю вам и дальше пребывать в уверенности, что все Java программы должны писаться по одному шаблону и для похожих целей. А все что не укладывается в привычные рамки — трэш и говнокод.


Вашу оценку моей квалификации оставлю без комментариев.

НЛО прилетело и опубликовало эту надпись здесь
Это не перевод. Я понимаю, что «запятая» по-русски правильнее. Но во большинстве языков программирования мы используем точку. И я решил, что если буду использовать в одной статье «floating point», «плавающая запятая», «1.23» (в коде) и «1,23» (не в коде), то я запутаюсь сам и запутаю остальных. Я думаю, что ИТ-сообщество вполне привыкло к англицизмам в русских технических текстах, в том числе и к «точке».

А почему бы не считать все в копейках? Или есть проблемы у такого решения?

По-сути так и делается для N=2. Но иногда требуется делить копейки на какие-нибудь центы (чтобы получить курс обмена) и результат должен содержать больше 2 знаков после запятой. Класс просто позволяет работать с разными точностями.

Интересная вещь. Для себя решил остаться на BigDecimal в виду неудачного гугляжа альтернатив в свое время. Использую там, где точность до 12 знаков (крипта), радует в нем то, что можно и более высокую точность использовать.


Конечно,


В целом, умножение получается в 4 раза быстрее BigDecimal, деление — в 1.5

Тоже прирост. В другой стороны в BD много фишек по округлению в любую сторону, что очень удобно, когда нужно не всегда классческое округление.

У меня поддерживаются все режимы округления из BigDecimal. Но я бы не сказал, что главная фича это прирост производительности. Цель была — мутабельный и неаллоцирующий класс. Если этого не требуется, то вполне разумно использовать стандартные средства
Есть мнение, что такие библиотеки вовсе так просты, как кажется и нужно понимать, что подобные велосипеды имеют очень ограниченное применение со множеством побочных эффектов. Не дай Бог расслабиться и забыть об этом (раз проект сделал, два сделал — работе же!). А потом через год окажется, что программа тупо неправильно считает деньги во многих случаях.
Вы уверены, что ваша библиотека корректно будет работать во всех случаях умножения (и в каким именно случаях)?
У вас же можно умножать только числа с порядком не больше половины максимального, да ещё и с учётом множителя дробной части.
Т.е. 1'000'000'000.0000*1'000'000'000.0000 приведёт к катастрофе. И, что хуже, при таком подходе к перемножению чисел разной точности будут неочевидные побочные эффекты. Например, 10'000'000'000.0000*10.000000 (второе число с большим числом знаков после запятой) тоже приведёт к катастрофе. Вы можете тупо единицу на единицу при большом количество занком после запятой умножить и получить неконтролируемое переполнение. По-моему, так делать нельзя. Нужно, как минимум, ловить потенциальные перполнения по точности и значению аргументов и обрабатывать пограничные случаи отдельно.
А ещё есть, как минимум, операцииквадратного корня и возведения в степень, которые даже в бухгалтерии используются — они у вас при таком подходе вообще неправильно вычисляться будут.
И про опитимизацию деления и взятия остатка — вы точно в этом уверены (что будет сгенерирована одна операция деления)?
умножать только числа с порядком не больше половины максимального

Нет, умножать/делить/etc можно любые числа при условии что результат вместе с дробной частью влезает в long. Если не влезает, возвращается NaN, который легко проверить. Напомню, Java в случае переполнения типа Long.MAX_VALUE * 10 вообще возвращает мусор.


Ваш первый пример — переполнение (18 нулей до запятой и 4 после):


System.out.println(new Decimal4().parse("1000000000.0000")
    .mulRD(new Decimal4().parse("1000000000.0000"))); // "NaN"

Ваш второй пример работает нормально:


System.out.println(new Decimal4().parse("10000000000.0000")
    .mulRD(new Decimal6().parse("10.000000"))); // 100000000000.0000

Насчет "вы уверены?". 100% гарантию отсутствия багов я дать не могу, как и большиство разработчиков. Но класс достаточно хорошо оттестирован, в том числе и на граничных случаях. Если вы нашли конкретную проблему, нет ничего проще написать падающий юнит-тест и прислать мне, я буду очень признателен. У библиотеки нет зависимостей, можно просто скопировать 2 класса из Гитхаба в любой пакет.


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

NaN возвращается, это уже хорошо, но пользователю от этого не легче. Он множит два «нормальных» с его точки зрения числа, результат влазит в представление с большим запасом, а на выходе получает NaN. Если не знать о внутренней реализации, то можно гарантировать баги при использовании.
Под уверенностью я имел в виду умножение отрицательных чисел с переполнением — там NaN правильно обрабатывается?
0.1 * 0.1 * 0.1 = 0.30000000000000004 в Java
0.1 * 0.1 * 0.1 = 0.30000000000000004
У вас очень странная Java.
У вас очень странная Java.

11 OpenJDK
Скрытый текст

Вы или сложение вместо умножения поставьте или результат измените.

Мдауж. Sorry, так я еще не фейлился…
При работе с BigDecimal очень напрягает, что нельзя написать формулу с помощью стандартных операторов (+, * и т.д.). И это очень сильно сказывается на читаемости, с BigDecimal даже самые простые вычисления выглядят монструозно. К сожалению, это особенности Java (не хватает перегрузки операторов) и тут сложно что-то придумать.
Вам в Kotlin. Там сделать нормальные унарные и бинарные операторы для BigDecimal дело пяти минут

Или сразу использовать scala.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Можно все-таки пруф насчет «считает неправильно»?
Один маленький падающий юнит-тест, вместо долгих рассуждений на тему «это не заработает»
НЛО прилетело и опубликовало эту надпись здесь

Реализация JavaMoney на Гитхабе (jsr354-ri) использует BigDecimal внутри. Навскидку — в методе divide. Почему мне не подходит BigDecimal я уже писал. Если вы знаете другую реализацию — поделитесь.

НЛО прилетело и опубликовало эту надпись здесь

Далеко не сразу нашёл лицензию проекта — она указана только в паре файлов. Стоит её добавить в readme и корень проекта (чтобы github её в заголовке указывал), pom.xml и во все исходники.

Ок, добавлю лиценцию (MIT). В главные классы проекта (их ровно 2) я добавил сразу, а вот про тесты, примеры и прочее забыл
Обратил внимание, что до примерно 2014 года на хабре можно было получить спасибо и плюсы за такую статью и за многие другие. После — обольют помоями. Здесь просто многие комментирующие самоутверждаются
Чувствуется вдохновление моей статьей, плюсанул :)

Вам стоило сделать стресс-тест со случайными числами и сверять результат с классом BigDecimal. Или же взять юнит-тесты со свободных либ на других языках, коли уж вы связаны с финансами. Тогда всем «критикам» можно было бы легко парировать.
Случайные тесты vs BigDecimal у меня бежали где-то неделю на 2 ядрах (домашняя машина). С юнит-тестами хорошая идея, попробую найти в OpenJDK и позаимствовать.
Есть такая штука docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/MathContext.html
и есть процессоры, например IBM Power, которые поддерживают арифметику с десятичной запятой. Процессоры intel тоже поддерживают, но не уверен что она используется в Java. Думаю, всё это не учитывается в вашей библиотеке.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации