Привет! Если ты уже пробовал работать с дробными числами в Python, то мог заметить странную вещь:
print(0.1 + 0.2) # 0.30000000000000004print(0.1 + 0.2 == 0.3) # FalseЭто не баг Python. Это особенность хранения чисел с плавающей точкой во всех языках программирования.
В этой статье разберём:
- Почему 0.1 + 0.2 ≠ 0.3
- Как компьютер хранит дробные числа
- Как правильно сравнивать float
- Как избежать проблем с точностью
- Что нужно знать перед началом
- Основная часть
- Что происходит?
- Примеры
- Почему это важно?
- Как правильно сравнивать float
- Модуль decimal
- Модуль fractions (рациональные числа)
- Округление
- Задачи для закрепления
- Нюансы и подводные камни
- round() не всегда работает как ожидается
- float не для денег
- Ошибка накапливается
- Частые ошибки и как их избежать
- Ошибка 1: Сравнение float через ==
- Ошибка 2: Использование float для денег
- Ошибка 3: Игнорирование погрешности в циклах
- Шпаргалка
- Заключение / Выводы
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный Python
- Базовое понимание чисел с плавающей точкой (float)
Совет: Эта тема важна для финансов, научных расчётов и любого кода, где точность критична.
Основная часть
Что происходит?
В большинстве языков программирования числа с плавающей точкой хранятся в двоичном формате IEEE 754.
Человек думает в десятичной системе. Компьютер — в двоичной (0 и 1).
Некоторые десятичные дроби невозможно точно представить в двоичной системе, как 1/3 в десятичной.
# В десятичной системе
1/3 = 0.3333333333... (бесконечно)
# В двоичной системе
0.1 = 0.0001100110011001100110011... (бесконечно)Компьютер обрезает эту бесконечную дробь, получая небольшое приближение.
Примеры
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.7) # 0.7999999999999999
print(0.3 - 0.2) # 0.09999999999999998Совет: Это касается не только Python, но и JavaScript, Java, C++, C# и других языков.
Почему это важно?
Если ты работаешь с:
- Финансовыми расчётами (деньги)
- Научными данными
- Геокоординатами
- Графикой и физикой
То ошибка в 0.00000000000000004 может накапливаться и приводить к серьёзным последствиям.
Как правильно сравнивать float
Неправильно:
if 0.1 + 0.2 == 0.3:
print("Равно")
else:
print("Не равно") # Всегда будет здесь!Правильно — с допуском (epsilon):
a = 0.1 + 0.2
b = 0.3
if abs(a - b) < 1e-10:
print("Примерно равно")
else:
print("Не равно")Совет: В Python 3.5+ есть встроенная функция
math.isclose(), которая делает это за вас.
import math
print(math.isclose(0.1 + 0.2, 0.3)) # TrueМодуль decimal
Для финансовых расчётов используй модуль decimal.
from decimal import Decimal
a = Decimal('0.1')
b = Decimal('0.2')
c = Decimal('0.3')
print(a + b) # 0.3
print(a + b == c) # TrueВажно: Передавай числа в виде строк, а не float.
# Неправильно
a = Decimal(0.1) # Уже с погрешностью
# Правильно
a = Decimal('0.1') # Без погрешностиСовет:
decimalмедленнее, чемfloat, но для денег — это лучший выбор.
Модуль fractions (рациональные числа)
Если работаешь с дробями, используй fractions.
from fractions import Fraction
a = Fraction(1, 10) # 1/10
b = Fraction(1, 5) # 1/5
c = Fraction(3, 10) # 3/10
print(a + b) # 3/10
print(a + b == c) # TrueСовет:
fractionsточны, но медленнееfloat.
Округление
Иногда достаточно просто округлить результат.
result = 0.1 + 0.2
print(round(result, 10)) # 0.3Важно:
round()не решает проблему полностью, но помогает в простых случаях.
Задачи для закрепления
Попробуй решить задачи самостоятельно.
Задача 1. Что выведет код?
print(0.1 + 0.2)
print(0.3 - 0.2)Задача 2. Напиши функцию, которая сравнивает два float с допуском 1e-9.
Задача 3. Используй модуль decimal, чтобы вычислить 0.1 + 0.2 без ошибок.
Задача 4. Используй модуль fractions, чтобы вычислить 1/3 + 1/3 + 1/3.
Задача 5. Почему round(2.675, 2) даёт 2.67, а не 2.68?
Ответы:
Задача 1
# 0.30000000000000004
# 0.09999999999999998Задача 2
def compare_float(a, b, epsilon=1e-9):
return abs(a - b) < epsilon
print(compare_float(0.1 + 0.2, 0.3)) # TrueЗадача 3
from decimal import Decimal
a = Decimal('0.1')
b = Decimal('0.2')
print(a + b) # 0.3Задача 4
from fractions import Fraction
a = Fraction(1, 3)
b = Fraction(1, 3)
c = Fraction(1, 3)
print(a + b + c) # 1Задача 5
# 2.675 не может быть точно представлен в двоичной системе.
# round(2.675, 2) даёт 2.67 из-за погрешности.Нюансы и подводные камни
round() не всегда работает как ожидается
Из-за той же проблемы float, round() может давать неожиданные результаты
print(round(2.675, 2)) # 2.67 (а не 2.68)Совет: Для точного округления используй
decimal.
float не для денег
Никогда не используй float для денег.
# Плохо
price = 0.1
total = price * 3 # 0.30000000000000004
# Хорошо
from decimal import Decimal
price = Decimal('0.1')
total = price * 3 # 0.3Ошибка накапливается
Чем больше операций, тем больше погрешность.
total = 0.0
for i in range(1000):
total += 0.1
print(total) # 100.00000000000159 (вместо 100.0)Частые ошибки и как их избежать
Ошибка 1: Сравнение float через ==
Неправильно:
if 0.1 + 0.2 == 0.3:
print("Равно")Правильно:
if abs(0.1 + 0.2 - 0.3) < 1e-10:
print("Равно")Ошибка 2: Использование float для денег
Неправильно:
price = 0.1
total = price * 3 # 0.30000000000000004Правильно:
from decimal import Decimal
price = Decimal('0.1')
total = price * 3 # 0.3Ошибка 3: Игнорирование погрешности в циклах
Неправильно:
total = 0.0
for i in range(100):
total += 0.01
# total не будет ровно 1.0Правильно:
total = 0
for i in range(100):
total += 1
print(total / 100) # Работаем с целымиШпаргалка
| Что нужно | Как пишется |
|---|---|
| Проверить float с допуском | abs(a - b) < 1e-9 |
| Точные десятичные числа | Decimal('0.1') |
| Точные дроби | Fraction(1, 10) |
| Округлить float | round(x, 2) |
| Проверить с isclose() | math.isclose(a, b) |
Совет: В Python 3.5+ есть функция
math.isclose().
import math
print(math.isclose(0.1 + 0.2, 0.3)) # TrueЗаключение / Выводы
Сегодня мы узнали:
- 0.1 + 0.2 ≠ 0.3 из-за двоичного представления float
- Для сравнения float используй допуск (
abs(a - b) < epsilon) илиmath.isclose() - Для денег используй
decimal - Для дробей используй
fractions round()не решает проблему полностью








