Почему 0.1 + 0.2 не равно 0.3? Проблема точности float

Почему 0.1 + 0.2 не равно 0.3? Проблема точности float Переменные и типы данных

Привет! Если ты уже пробовал работать с дробными числами в Python, то мог заметить странную вещь:

print(0.1 + 0.2)   # 0.30000000000000004
print(0.1 + 0.2 == 0.3)   # False

Это не баг Python. Это особенность хранения чисел с плавающей точкой во всех языках программирования.

В этой статье разберём:

  • Почему 0.1 + 0.2 ≠ 0.3
  • Как компьютер хранит дробные числа
  • Как правильно сравнивать float
  • Как избежать проблем с точностью

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный 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)
Округлить floatround(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() не решает проблему полностью

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×