Особенности работы с java.math.BigDecimal

Если использовать типы double или float в Java, то можно заметить, что некоторые десятичные дроби не могут быть записаны в них без погрешности. Это связано с особенностью хранения данных в формате с плавающей точкой. Для того, чтобы увидеть эту погрешность, посмотрите вот этот пример:

В консоли будет написано далеко не 0.3.

В некоторых случаях такая погрешность может стать критической. Например, в случае когда мы считаем сумму всех денежных поступлений или списаний.

В Java существует специальный тип для точного хранения десятичных дробей с произвольным количеством знаков после запятой. Это java.math.BigDecimal. Предыдущий пример с использованием BigDecimal будет выглядеть так:

Теперь мы увидим ровно 0.3, как ожидалось.

Всегда используйте для хранения и обработки денежных сумм java.math.BigDecimal!

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

В java.math.BigDecimal есть другие полезные методы, кроме сложения:

BigDecimal     abs() — возвращает абсолютное значение (убирает минус, если он есть).

BigDecimal     add(BigDecimal augend) — сложение, которое использовалось в примере.

int     compareTo(BigDecimal val) — сравнение. Возвращает 1, если текущее значение больше val, 0 если текущее значение равно val, -1 если текущее значение меньше val.

BigDecimal     divide(BigDecimal divisor, int roundingMode) — деление. Количество знаков после запятой у возвращаемого значения this.scale().
BigDecimal     divide(BigDecimal divisor, int scale, Rounding roundingMode) — деление. Возвращает BigDecimal, значение которого равно this/divisor, количество знаков после запятой равно scale.

BigDecimal     multiply(BigDecimal multiplicand) — умножение. Возвращает результат умножения.

Это далеко не полный список методов. Все методы можно прочитать в официальной документации по ссылке в конце поста.

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

Также обратите внимание, что не стоит сравнивать BigDecimal с помощью equals. Используйте для этого метод compareTo, так как он корректно обрабатывает разную точность представления BigDecimal. Метод equals вернёт true только в том случае, если равны их значения и их точность. То есть для equals числа 2.0 и 2.00 не равны.

Ссылки:

https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html API java.math.BigDecimal.

Особенности работы с java.math.BigDecimal: 2 комментария

  1. Сразу же выполнил первый пример и увидел совсем не то, что ожидал: 0.3000000000000000166533453693773481063544750213623046875
    Потом присмотрелся… оу, оказывается
    System.out.println(new BigDecimal(0.1));
    и
    System.out.println(new BigDecimal(«0.1»));
    дают разный результат.
    Последний момент про equals тоже интересный.

    Спасибо за статью

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *