JavaRush /Курсы /JAVA 25 SELF /Проблемы точности и специальные значения

Проблемы точности и специальные значения

JAVA 25 SELF
6 уровень , 3 лекция
Открыта

1. Введение

Кажется, что если компьютер "умный", то 0.1 + 0.2 должно быть просто 0.3. Но это не совсем так. Давайте разберёмся на простом примере.


double x = 0.1;
double y = 0.2;
double sum = x + y;
System.out.println(sum); // Что выведет программа?
Сложение вещественных чисел: ожидаем 0.3, а что на самом деле?

А теперь попробуйте сравнить с 0.3:

System.out.println(sum == 0.3); // А тут будет true или false?

Если вы увидите false, не удивляйтесь!

Причина кроется в представлении чисел в памяти

Компьютеры работают с числами в двоичной системе. Но далеко не все десятичные дроби можно представить конечной двоичной дробью, так же как 1/3 нельзя точно записать десятичной дробью (0.333...). Например, 0.1 в двоичной системе — это бесконечная дробь, и её приходится "округлять" для хранения.

Если перевести это на бытовой язык, то double иногда "делает вид", что хранит ваше число точно, но на самом деле хранит лишь очень близкое приближённое значение.

2. Какие "глюки" бывают при арифметике с double?

Давайте рассмотрим практические примеры.

Пример 1. Классическая "магия" 0.1 + 0.2

double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum);            // 0.30000000000000004
System.out.println(sum == 0.3);     // false

Компьютер вывел не 0.3, а 0.30000000000000004. Разница маленькая, но если вы занимаетесь, например, финансами — это уже критично.

Пример 2. Итеративное сложение

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
System.out.println(result); // 0.9999999999999999

Хотели 1.0 — получили чуть меньше. Снова, причина в округлении внутри double.

Почему это важно в реальных задачах

Многие думают: "Какая разница, ну небольшая ошибка, подумаешь!" Рассмотрим пример из мира платежей.

Допустим, ваш интернет-банк складывает 100 транзакций по 0.1 евро. Если ваша программа "теряет" по одной стотысячной евро на каждую итерацию, то на масштабе банка вы уже "потеряете" реальные деньги. Вот тут к вам сразу придёт бухгалтер и спросит: "Где наши деньги?!"

3. Как правильно сравнивать вещественные числа

Поскольку double часто не может хранить именно то значение, которое вы ожидаете, прямое сравнение через == может подвести. Вместо этого принято сравнивать модуль разности чисел с некоторым очень маленьким числом (epsilon).

Пример сравнения с допуском

double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;

if (Math.abs(a - b) < epsilon)
{
    System.out.println("Почти равно!"); // Так сравнивать безопаснее
}

Здесь мы говорим: "Если разница между числами меньше, чем одна миллионная, считаем, что числа равны".
Примечание: функция Math.abs(value) возвращает абсолютное значение (модуль) переданного в неё числа.

4. Специальные значения double: Infinity, NaN, -Infinity

Тип double хранит не только числа, но и особые значения. Они возникают в ситуациях, которые математик обычному студенту строго-настрого запрещает.

Бесконечность (Infinity)

Что будет, если поделить 1 на 0?

double result = 1.0 / 0.0;
System.out.println(result); // Infinity

В Java (и многих языках) деление на 0 для double не вызывает исключения! Вместо этого результат становится специальным значением "положительная бесконечность".

Минус бесконечность (-Infinity)

Если поделить отрицательное число на 0, то вы получите отрицательную бесконечность:

double result = -1.0 / 0.0;
System.out.println(result); // -Infinity

"Не число" (NaN — Not a Number)

Если сделать что-то совсем странное, например, попытаться вычислить корень из отрицательного числа:

double result = Math.sqrt(-1);
System.out.println(result); // NaN

Или результат деления 0.0 / 0.0:

double result = 0.0 / 0.0;
System.out.println(result); // NaN

NaN — это "всё, что не являлось бы числом в реальной жизни".

Проверка на специальные значения

В Java есть функции для проверки специальных значений:

System.out.println(Double.isInfinite(result));    // true, если бесконечность
System.out.println(Double.isNaN(result));         // true, если NaN

Таблица: Как double реагирует на необычные операции

Операция Результат Что хранится в double
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN Не число
Math.sqrt(-1)
NaN Не число

5. Типичные ошибки при работе с вещественными числами

Ошибка №1: Сравнение дробных чисел через ==
Самая распространённая ловушка: попытка проверить, равны ли два вычисленных числа с плавающей точкой через обычное сравнение. Из-за накопления ошибок округления вы почти всегда получите неожиданный результат. Всегда используйте сравнение с допуском (epsilon).

Ошибка №2: Неожиданные NaN и Infinity в вычислениях
Если не контролировать деление на ноль или корни из отрицательных чисел, в программе могут появиться NaN или Infinity и "заразить" все последующие расчёты. Не забывайте проверять подозрительные значения с помощью Double.isNaN() и Double.isInfinite() — это поможет избежать неприятных сюрпризов.

Ошибка №3: Использование NaN в качестве "маркера"
Некоторые новички используют NaN как "специальное" значение для проверки, например, "если не найдено — вернём NaN". Но помните: NaN коварен, и сравнение через == не сработает! Проверяйте только через специальные методы.

Ошибка №4: Ожидание исключения при делении на 0.0
В отличие от деления целых чисел, деление на 0.0 для double не вызывает ошибку, а возвращает Infinity или NaN. Это может привести к тихим и трудноуловимым багам — результат есть, но он не такой, как вы ожидали.

1
Задача
JAVA 25 SELF, 6 уровень, 3 лекция
Недоступна
Проверка точности химического раствора 🧪
Проверка точности химического раствора 🧪
1
Задача
JAVA 25 SELF, 6 уровень, 3 лекция
Недоступна
Изучение границ вселенной вычислений 🌌
Изучение границ вселенной вычислений 🌌
1
Задача
JAVA 25 SELF, 6 уровень, 3 лекция
Недоступна
Попытка вычислить Неизвестное ❓
Попытка вычислить Неизвестное ❓
1
Задача
JAVA 25 SELF, 6 уровень, 3 лекция
Недоступна
Калибровка высокоточного лазера 🔬
Калибровка высокоточного лазера 🔬
Комментарии (12)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Gans Electro Уровень 31
26 мая 2026
Интересный факт: от 0 до 1 больше действительных чисел чем от 1 до ∞ натуральных
Gans Electro Уровень 31
26 мая 2026
double занимает 64 бита: 1 бит — знак 11 бит — экспонента 52 бита — мантисса Формат:

| sign | exponent (11) | fraction/mantissa (52) |
Битовое представление +Infinity

0 11111111111 0000000000000000000000000000000000000000000000000000
Для -Infinity:

1 11111111111 0000000000000000000000000000000000000000000000000000
Битовое представление NaN sign: любой exponent: все 1 mantissa: НЕ нули Например:

0 11111111111 1000000000000000000000000000000000000000000000000000 или
0 11111111111 1000000000000000000000000000000000000000000000000001 или
1 11111111111 1000000000000000000000000000000000000000000000000010 или
0 11111111111 1000000000000000000000000000000000000000000000000100 ...
Главное отличие от Infinity: Infinity → mantissa = 0 NaN → mantissa ≠ 0 Но у NaN существует множество разных битовых вариантов. Интересный момент NaN не равен даже самому себе: System.out.println(Double.NaN == Double.NaN); // false Потому что NaN означает: "результат неопределён"
Валерка Ферум Уровень 32
18 февраля 2026
Прохожу сразу два курса и старый и этот. В этом есть некоторые интересные моменты, и местами подробнее что-то описанно, и еще задачи встречаются сложнее)
Valeroy Уровень 7
20 февраля 2026
Анологично)
Alex Уровень 7
7 марта 2026
Задачи посложнее? Что вы имеете ввиду, создать 10 переменных, а еще вам написали как их назвать и чтобы вы не заюыли для лонг Л в конце, это абсолютно бесполезные задачи, по факту за все время не встретил ни 1 задачи которая может помогать развивать навыки программирования, зато много заданий где нужно печатать бессмысленную дичь
Валерка Ферум Уровень 32
7 марта 2026
Найдите тогда другой ресурс, который будет соответствовать вашим ожиданиям
Victor Уровень 23
30 марта 2026
Аналогично
evoker7lug Уровень 1
2 октября 2025
не понимаю зачем так тщательно описывать условие задачи... по сути даете готовое решение.
Gemini Уровень 12
27 октября 2025
Людям не угодишь. Я со старого курса там по хардкору никаких подсказок легкое объяснение требования. Топ комментариев " почему так слабо объясняете ". Новый курс все тщательно объяснили чтоб новички понимали: " не понимаю зачем так тщательно описывать условие задачи..."
hidden #6416165 Уровень 7
11 ноября 2025
Тут лишнее - затравки в начале задачи, которые порой больше условий и не несут никакой смысловой нагрузки. Ну и, естественно, расставленные ключевые слова, а также //комментарии в условии, которые хочется все время стереть.
8 декабря 2025
Не переживай, потом будешь просить, чтоб подробнее описывали 😁
Darya Smagina Уровень 7
13 марта 2026
Если хочется стереть – не отказывайте себе в удовольствии 😁