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

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

Открыта

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
Уровень 59
26 мая, 21:17
Интересный факт: от 0 до 1 больше действительных чисел чем от 1 до ∞ натуральных
Gans Electro
Уровень 59
26 мая, 21:04
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 означает: "результат неопределён"
Валерка Ферум Software Developer
18 февраля, 21:03
Прохожу сразу два курса и старый и этот. В этом есть некоторые интересные моменты, и местами подробнее что-то описанно, и еще задачи встречаются сложнее)
Valeroy
Уровень 1
20 февраля, 02:57
Анологично)
Alex
Уровень 7
7 марта, 16:52
Задачи посложнее? Что вы имеете ввиду, создать 10 переменных, а еще вам написали как их назвать и чтобы вы не заюыли для лонг Л в конце, это абсолютно бесполезные задачи, по факту за все время не встретил ни 1 задачи которая может помогать развивать навыки программирования, зато много заданий где нужно печатать бессмысленную дичь
Валерка Ферум Software Developer
7 марта, 17:48
Найдите тогда другой ресурс, который будет соответствовать вашим ожиданиям
VictorThe main expert in Automa в Строительная компани
30 марта, 11:05
Аналогично
evoker7lug
Уровень 1
2 октября 2025, 12:50
не понимаю зачем так тщательно описывать условие задачи... по сути даете готовое решение.
Gemini
Уровень 12
27 октября 2025, 08:52
Людям не угодишь. Я со старого курса там по хардкору никаких подсказок легкое объяснение требования. Топ комментариев " почему так слабо объясняете ". Новый курс все тщательно объяснили чтоб новички понимали: " не понимаю зачем так тщательно описывать условие задачи..."
hidden #6416165
Уровень 7
11 ноября 2025, 12:22
Тут лишнее - затравки в начале задачи, которые порой больше условий и не несут никакой смысловой нагрузки. Ну и, естественно, расставленные ключевые слова, а также //комментарии в условии, которые хочется все время стереть.
8 декабря 2025, 19:12
Не переживай, потом будешь просить, чтоб подробнее описывали 😁
Darya Smagina
Уровень 7
13 марта, 17:35
Если хочется стереть – не отказывайте себе в удовольствии 😁