Тихие переполнения чисел в Java


Что содержится в статье ⌄
  • Переполнение Java
  • AddExact и обычное сложение в Java
  • Целочисленное переполнение в Java
  • Безопасные арифметические операции в Java

Рассмотрим этот пример:

int result = Integer.MAX_VALUE + 1;
// Приводит к Integer.MIN_VALUE из-за переполнения

В обычных арифметических операциях при переполнении Java выполняет так называемый “циклический переход”, то есть при значении > Integer.MAX_VALUE, “дальнейший счет” пойдет с Integer.MIN_VALUE.


Методы Java Math.exact

Безопасное сложение

Math.addExact() выполняет сложение и кидает ArithmeticException, если результат приведет к переполнению. Есть как int, так и long версии:

try {
    int result = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
    System.out.println("Переполнение");
}

Безопасное умножение

Аналогично addExact, multiplyExact безопасно делает умножение:

try {
    int result = Math.multiplyExact(100000, 100000);
} catch (ArithmeticException e) {
    System.out.println("Переполнение");
}

Также есть версия для long.

Безопасное вычитание

subtractExact делает безопасное вычитание:

try {
    int result = Math.subtractExact(Integer.MIN_VALUE, 1);
} catch (ArithmeticException e) {
    System.out.println("Переполнение");
}

Другие арифметические методы Math.exact

Java также предоставляет несколько других точных арифметических методов:

incrementExact – увеличивает на единицу, кидает ArithmeticException:

int result = Math.incrementExact(Integer.MAX_VALUE);

decrementExact – уменьшает на единицу, кидает ArithmeticException:

int result = Math.decrementExact(Integer.MIN_VALUE);

negateExact – возвращает отрицание аргумента, кидает ArithmeticException:

int result = Math.negateExact(Integer.MIN_VALUE);

toIntExact – возвращает значение аргумента long, кидает ArithmeticException:

int result = Math.toIntExact(Long.MAX_VALUE);

Применение

Эти методы особенно полезны в:

  1. Финансовых расчетах, где точность критически важна
  2. Научных вычислениях, которые не могут допускать переполнение
  3. Критически важных для безопасности приложениях

Не забудем BigInteger или BigDecimal для больших чисел.


Пример

Вот практический пример, объединяющий несколько exact методов:

public class SafeCalculator {
    public static int calculateTotal(int quantity, int price) {
        try {
            // Сначала умножаем количество на цену
            int subtotal = Math.multiplyExact(quantity, price);
            // Добавляем налог (предположим 10%)
            int tax = Math.multiplyExact(subtotal, 10);
            tax = Math.divideExact(tax, 100);
            // Возвращаем итог с налогом
            return Math.addExact(subtotal, tax);
        } catch (ArithmeticException e) {
            throw new IllegalArgumentException("Переполнение", e);
        }
    }
}