Работа с числовыми данными
Целочисленная арифметика
Операторы +, -, *, /, % работают с целыми типами. Умножение, деление и остаток выполняются раньше сложения и вычитания; скобки явно фиксируют намерение.
int pages = 10 / 3; // 3
int remainder = 10 % 3; // 1
double exact = 10 / 3.0; // 3.333...Если оба операнда целые, деление остаётся целочисленным. Приведение результата после деления уже не вернёт потерянную дробную часть:
double wrong = (double) (10 / 3); // 3.0
double right = (double) 10 / 3; // 3.333...Остаток в Java сохраняет знак делимого: -5 % 3 == -2. Для математического неотрицательного остатка при положительном модуле можно использовать Math.floorMod.
Переполнение
Обычная целочисленная арифметика переполняется по модулю разрядности и не выбрасывает исключение:
int max = Integer.MAX_VALUE;
int overflow = max + 1; // Integer.MIN_VALUEКогда переполнение нарушает бизнес-инвариант, применяют Math.addExact, subtractExact, multiplyExact, incrementExact, toIntExact.
int total = Math.addExact(left, right);Для больших целых без фиксированного диапазона есть BigInteger.
float и double
float и double используют двоичный формат IEEE 754. Многие десятичные дроби в нём непредставимы точно:
double value = 0.1 + 0.2;
System.out.println(value); // 0.30000000000000004Для измерений сравнивают с допустимой погрешностью:
boolean close = Math.abs(actual - expected) < 1e-9;Особые значения:
NaNне равен даже самому себе; проверка —Double.isNaN;- положительная и отрицательная бесконечность;
0.0и-0.0, которые ведут себя одинаково не во всех операциях.
Нельзя проверять x == Double.NaN.
BigDecimal для десятичной точности
Деньги и другие точные десятичные величины обычно представляют BigDecimal.
BigDecimal price = new BigDecimal("19.90");
BigDecimal quantity = BigDecimal.valueOf(3);
BigDecimal total = price.multiply(quantity);Создавать BigDecimal из строкового литерала или через valueOf(double) безопаснее, чем new BigDecimal(0.1), который зафиксирует двоичную погрешность.
Операции возвращают новый объект:
BigDecimal amount = new BigDecimal("10.00");
amount = amount.add(new BigDecimal("2.50"));Деление может потребовать режим округления:
BigDecimal third = BigDecimal.ONE.divide(
BigDecimal.valueOf(3),
2,
RoundingMode.HALF_UP
); // 0.33equals сравнивает и значение, и scale: 10.0 не равно 10.00. compareTo сравнивает числовое значение и вернёт 0.
Распределение суммы
Если общую сумму нужно разделить на части и сохранить точное равенство, нельзя независимо округлять каждую долю: сумма округлённых частей может отличиться. Один рабочий подход — рассчитывать первые n - 1 частей по принятому правилу, а последнюю определить как остаток от исходной суммы.
Math и сравнение
Math содержит функции abs, min, max, sqrt, pow, округление и точные целочисленные операции. Для сравнения целых в компараторе используйте Integer.compare/Long.compare, а не вычитание, которое может переполниться.
Comparator<Item> byCount =
(a, b) -> Integer.compare(a.count(), b.count());Ловушки
- Целочисленное деление до приведения к
double. - Тихое переполнение
intиlong. - Денежные вычисления через
double. new BigDecimal(double)с неточным двоичным значением.- Деление
BigDecimalбез явной точности и округления. - Использование
BigDecimal.equalsтам, где нужна числовая эквивалентность.
Вопросы на собеседовании
- Почему
10 / 3и10 / 3.0дают разные результаты? - Что происходит при переполнении
intи как его обнаружить? - Почему
0.1 + 0.2не равно точно0.3? - Как корректно создать
BigDecimalдля денежного значения? - Чем
BigDecimal.equalsотличается отcompareTo? - Как разделить сумму на части, сохранив точный итог?
Связанные заметки
Переменные и типы данных Java · Преобразование типов Java · Классы обертки Java