Почему не стоит использовать double для денег
Проблемы точности чисел с плавающей запятой
«Почему не стоит использовать double для денег» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Опасное значение по умолчанию
Кажется естественным хранить цены в типе double. К сожалению, double не может точно представить большинство десятичных дробей, из-за чего возникают небольшие ошибки, способные испортить финансовые расчёты.
Классическая неожиданность
Если сложить 0.1 и 0.2 с помощью double, результатом не будет 0.3. Вы получите значение с небольшой ошибкой округления.
public class Main {
public static void main(String[] args) {
double result = 0.1 + 0.2;
System.out.println(result);
}
}Почему это происходит
Компьютеры хранят значения double в двоичной системе. Подобно тому как одну треть нельзя точно записать в десятичной системе, многие десятичные дроби, например 0.1, нельзя точно записать в двоичной системе.
В памяти сохраняется ближайшее двоичное приближение, и ошибка накапливается.
Ошибки накапливаются
Одна небольшая ошибка может показаться безобидной, но в цикле по тысячам операций разница накапливается и превращается в заметные неточности.
public class Main {
public static void main(String[] args) {
double total = 0.0;
for (int i = 0; i < 10; i++) {
total += 0.1;
}
System.out.println(total);
}
}Сравнение на равенство даёт сбой
Из-за ошибок округления сравнение вычисленных значений типа double с помощью == часто даёт неверный результат, когда Вы ожидаете равенства.
public class Main {
public static void main(String[] args) {
double a = 0.1 + 0.2;
double b = 0.3;
System.out.println(a == b);
}
}Деньгам нужна точность
В финансовых расчётах ошибка даже в долю копейки недопустима. Аудиторы ожидают, что итоговые суммы будут совпадать до последней копейки.
Поэтому деньги никогда не следует хранить в типе с плавающей запятой.
Решение: BigDecimal
Java предоставляет BigDecimal — класс, который хранит десятичные числа точно. Он отдельно отслеживает немасштабированное значение и параметр scale, поэтому 0.1 действительно означает 0.1.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));
}
}Всегда используйте конструктор со строкой
Создавайте BigDecimal из строки, а не из значения double. Передача значения double переносит двоичную ошибку в BigDecimal.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal fromDouble = new BigDecimal(0.1);
BigDecimal fromString = new BigDecimal("0.1");
System.out.println(fromDouble);
System.out.println(fromString);
}
}Или используйте valueOf
BigDecimal.valueOf — это безопасное сокращение. Оно преобразует double через его каноническое строковое представление и устраняет исходную двоичную ошибку.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal price = BigDecimal.valueOf(19.99);
System.out.println(price);
}
}Хранение денег в копейках
Для простых случаев можно хранить деньги как целое количество копеек, используя long. Тогда 19.99 доллара превращаются в 1999 копеек, а вся математика выполняется точно как целочисленная арифметика.
public class Main {
public static void main(String[] args) {
long cents = 1999;
long total = cents * 3;
System.out.println(total + " cents");
}
}Главный вывод
Используйте float и double в научных и графических задачах, где допустимы небольшие ошибки. Для денег и любых значений, требующих точности, используйте BigDecimal или целое количество копеек.
Быстрая проверка
Проверьте, насколько хорошо Вы понимаете точность чисел с плавающей запятой.
Повторение
Вы узнали, почему числа с плавающей запятой не подходят для денег:
- Двоичная арифметика с плавающей запятой не может точно хранить многие десятичные дроби
- Ошибки, например в выражении
0.1 + 0.2, из-за которых результат не равен 0.3, накапливаются - Используйте
BigDecimalдля точной десятичной арифметики - Создавайте его из строки или с помощью
valueOf, но никогда не передавайте необработанное значение double - Хранение целого количества копеек — ещё один точный вариант
Часто задаваемые вопросы
Урок «Почему не стоит использовать double для денег» бесплатный?
Да — полный текст урока «Почему не стоит использовать double для денег» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Почему не стоит использовать double для денег»?
Проблемы точности чисел с плавающей запятой Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Почему не стоит использовать double для денег»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Почему не стоит использовать double для денег
- Работа с BigDecimal
- Округление и масштаб
- BigInteger для огромных чисел