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

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

Погрешность представления чисел в формате с плавающей точкойJava

System.out.println(0.1+0.2);

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

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

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

System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));

Теперь мы увидим ровно 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 не равны.