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

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

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

1. Введение

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

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

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

Console.WriteLine(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;
Console.WriteLine(sum);            // 0.30000000000000004
Console.WriteLine(sum == 0.3);     // False

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

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

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
Console.WriteLine(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)
{
    Console.WriteLine("Почти равно!"); // Так сравнивать безопаснее
}

Здесь мы говорим: "Если разница между числами меньше, чем одна миллионная, считаем, что числа равны".

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

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

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

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

double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity

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

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

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

double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity

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

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

double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN

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

double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN

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

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

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

Console.WriteLine(double.IsInfinity(result));    // true если бесконечность
Console.WriteLine(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 Не число
2
Задача
C# SELF, 6 уровень, 3 лекция
Недоступна
Разница в точности арифметики
Разница в точности арифметики
2
Задача
C# SELF, 6 уровень, 3 лекция
Недоступна
Сравнение с использованием epsilon
Сравнение с использованием epsilon
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Александр Уровень 39
21 сентября 2025
забавно, но у меня 0.1 * 10 всегда выдаёт 1... а вот 0.1 + 0.2 как и написано в задаче
Yurii N Уровень 66
25 декабря 2025
Аналогично )