Что такое машинный эпсилон?
Машинный эпсилон — это не «минимальное различимое число», как часто ошибочно полагают. Корректное определение машинного эпсилона: разница между числом 1 и наименьшим числом с плавающей точкой, которое больше 1. В Python для чисел с плавающей точкой двойной точности (тип float) это значение составляет примерно 2.22e-16.
Важно понимать, что плотность представления чисел в формате с плавающей точкой неравномерна. Близко к нулю можно представить числа с гораздо меньшей разницей, чем машинный эпсилон, а для очень больших чисел разница между соседними представимыми значениями может быть значительно больше единицы.
Как получить значение машинного эпсилона в Python?
Метод 1: Используя sys.float_info
import sys
epsilon = sys.float_info.epsilon
print(f"Машинный эпсилон: {epsilon}")
# Вывод: Машинный эпсилон: 2.220446049250313e-16
В этом коде мы импортируем модуль sys и обращаемся к атрибуту float_info.epsilon, который содержит значение машинного эпсилона для типа float в Python.
Метод 2: Вычисление машинного эпсилона вручную
Мы можем вычислить машинный эпсилон самостоятельно с помощью простого алгоритма:
Тот же код ниже для копирования и вставки в программу. Не забывайте про необходимый отступ пробелами в определённых местах в начале строки, так как код на сервере блога может отображаться некорректно.
def calculate_epsilon():
eps = 1.0
while eps + 1 > 1:
eps /= 2
eps *= 2
return eps
epsilon = calculate_epsilon()
print(f"Рассчитанный машинный эпсилон: {epsilon}")
# Вывод: Рассчитанный машинный эпсилон: 2.220446049250313e-16
Разберем этот код пошагово:
- Начинаем с eps = 1.0
- В цикле делим eps на 2, пока выражение eps + 1 > 1 истинно
- Когда eps становится настолько малым, что eps + 1 == 1, мы выходим из цикла
- Умножаем последнее значение eps на 2, чтобы получить машинный эпсилон
Метод 3: Используя модуль math с Python 3.5+
import math
epsilon = math.ulp(1.0)
print(f"Машинный эпсилон через math.ulp: {epsilon}")
# Вывод: Машинный эпсилон через math.ulp: 2.220446049250313e-16
Здесь мы используем функцию math.ulp() (Unit in the Last Place), которая возвращает значение одной единицы в последнем разряде для заданного числа.
Практическое применение машинного эпсилона
Пример 1: Демонстрация ограничений точности
import sys
epsilon = sys.float_info.epsilon
print(f"Машинный эпсилон: {epsilon}")
print(f"1.0 + epsilon = {1.0 + epsilon}") # Больше 1 print(f"1.0 + epsilon/2 = {1.0 + epsilon/2}") # Равно 1
Вывод:
Машинный эпсилон: 2.220446049250313e-16
1.0 + epsilon = 1.0000000000000002
1.0 + epsilon/2 = 1.0
В этом примере мы видим классическую демонстрацию машинного эпсилона:
- Когда мы добавляем epsilon к 1.0, результат отличается от 1.0. Мы видим на конце числа 2 (1.0000000000000002).
- Когда мы добавляем epsilon/2 к 1.0, разница настолько мала, что результат округляется до 1.0
Пример 2: Сравнение чисел с плавающей точкой
Тот же код ниже для копирования и вставки в программу. Не забывайте про необходимый отступ пробелами в определённых местах в начале строки, так как код на сервере блога может отображаться некорректно.
import sys
def calculate_epsilon():
eps = 1.0
while eps + 1 > 1:
eps /= 2
eps *= 2
return eps
def is_close(a, b, rel_tol=sys.float_info.epsilon * 10):
#Сравнивает два числа с плавающей точкой с относительной погрешностью.
abs_diff = abs(a - b)
max_magnitude = max(abs(a), abs(b))
if max_magnitude < sys.float_info.epsilon:
# Для очень маленьких чисел используем абсолютную погрешность
return abs_diff < sys.float_info.epsilon
return abs_diff / max_magnitude < rel_tol
# Получение машинного эпсилона тремя способами
epsilon_sys = sys.float_info.epsilon
epsilon_calculated = calculate_epsilon()
import math
epsilon_math = math.ulp(1.0)
print(f"Машинный эпсилон (sys): {epsilon_sys}")
print(f"Рассчитанный машинный эпсилон: {epsilon_calculated}")
print(f"Машинный эпсилон через math.ulp: {epsilon_math}")
# Демонстрация
print(f"1.0 + epsilon = {1.0 + epsilon_sys}") # Больше 1
print(f"1.0 + epsilon/2 = {1.0 + epsilon_sys/2}") # Равно 1
# Тестирование функции is_close
print("Сравнение 0.1 + 0.2 и 0.3:", is_close(0.1 + 0.2, 0.3))
print("Сравнение 1.0 и 1.0 + epsilon/2:", is_close(1.0, 1.0 + epsilon_sys/2))
print("Сравнение 1.0 и 1.0 + epsilon*5:", is_close(1.0, 1.0 + epsilon_sys*5))
# Демонстрация ошибок округления
print("0.1 + 0.2 =", 0.1 + 0.2)
print("Разница между (0.1 + 0.2) и 0.3 меньше epsilon?", abs((0.1 + 0.2) - 0.3) < epsilon_sys)
print("Разница между (0.1 + 0.2) и 0.3 меньше 1e-15?", abs((0.1 + 0.2) - 0.3) < 1e-15)
# Демонстрация потери точности при операциях с большими числами
large_num = 1e16
small_increment = 1.0
print(f"{large_num} + {small_increment} - {large_num} =", large_num + small_increment - large_num)
# Машинный эпсилон и машинный ноль
print(f"Машинный эпсилон: {sys.float_info.epsilon}")
print(f"Минимальное положительное число: {sys.float_info.min}")
В этом примере мы создаем функцию для сравнения чисел с плавающей точкой с учетом относительной погрешности.
Результат работы кода:
Описание кода:
import sys
• Импорт модуля sys для доступа к атрибутам, связанным с типом float (например, sys.float_info).
def calculate_epsilon():
• Объявление функции для вычисления машинного эпсилона вручную.
eps = 1.0
• Инициализация переменной eps значением 1.0.
while eps + 1 > 1:
• Запуск цикла, который продолжает делить eps, пока прибавление его к 1 дает значение, отличное от 1.
eps /= 2
• Деление eps на 2 для уменьшения шага, пока eps не станет слишком малым для изменения суммы с 1.
eps *= 2
• После цикла возвращаемся к последнему значению eps, которое проделывало изменение суммы 1 + eps, корректно умножив его на 2.
return eps
• Возвращаем полученное значение машинного эпсилона.
def is_close(a, b, rel_tol=sys.float_info.epsilon * 10):
• Объявление функции, сравнивающей два числа с плавающей точкой с учетом относительной погрешности.
• Аргумент rel_tol по умолчанию установлен как 10-кратное значение sys.float_info.epsilon.
abs_diff = abs(a - b)
• Вычисление абсолютной разницы между a и b.
max_magnitude = max(abs(a), abs(b))
• Определение максимальной абсолютной величины из a и b для расчета относительной разницы.
if max_magnitude < sys.float_info.epsilon:
• Проверка: если оба числа слишком малы (почти равны нулю), используем абсолютное сравнение.
return abs_diff < sys.float_info.epsilon
• Возвращаем True, если абсолютная разница меньше машинного эпсилона (для очень маленьких чисел).
return abs_diff / max_magnitude < rel_tol
• Для остальных чисел возвращаем результат проверки: относительная разница меньше заданного порога rel_tol.
# Получение машинного эпсилона тремя способами
• Комментарий, поясняющий цель следующего кода – получение машинного эпсилона разными методами.
epsilon_sys = sys.float_info.epsilon
• Прямое получение машинного эпсилона через атрибут модуля sys.
epsilon_calculated = calculate_epsilon()
• Вычисление машинного эпсилона с использованием ранее определенной функции calculate_epsilon().
import math
• Импорт модуля math для использования дополнительных математических функций.
epsilon_math = math.ulp(1.0)
• Получение машинного эпсилона через функцию math.ulp(), которая возвращает наименьшую разницу между 1.0 и следующим представимым числом.
print(f"Машинный эпсилон (sys): {epsilon_sys}")
• Вывод значения машинного эпсилона, полученного через sys.float_info.epsilon.
print(f"Рассчитанный машинный эпсилон: {epsilon_calculated}")
• Вывод вычисленного с помощью функции calculate_epsilon машинного эпсилона.
print(f"Машинный эпсилон через math.ulp: {epsilon_math}")
• Вывод машинного эпсилона, полученного функцией math.ulp.
# Демонстрация
• Комментарий, помогающий понять, что ниже идет демонстрация работы с машинным эпсилоном.
print(f"1.0 + epsilon = {1.0 + epsilon_sys}")
• Вывод результата суммы 1.0 и машинного эпсилона; результат будет чуть больше 1.
print(f"1.0 + epsilon/2 = {1.0 + epsilon_sys/2}")
• Вывод результата суммы 1.0 и половины машинного эпсилона; результат останется равным 1 из-за ограничений точности.
# Тестирование функции is_close
• Комментарий для раздела тестирования функции сравнения чисел с плавающей точкой.
print("Сравнение 0.1 + 0.2 и 0.3:", is_close(0.1 + 0.2, 0.3))
• Тестирование функции is_close, сравнивая арифметическую сумму 0.1 + 0.2 с 0.3 (известное явление округления).
print("Сравнение 1.0 и 1.0 + epsilon/2:", is_close(1.0, 1.0 + epsilon_sys/2))
• Тест функции is_close для проверки незначительного изменения числа (1.0 и 1.0 + половина машинного эпсилона).
print("Сравнение 1.0 и 1.0 + epsilon*5:", is_close(1.0, 1.0 + epsilon_sys*5))
• Тест функции is_close для проверки чисел с более заметной разницей (1.0 и 1.0 + 5-кратный машинный эпсилон).
# Демонстрация ошибок округления
• Комментарий, вводящий демонстрацию типичных ошибок округления в арифметике с плавающей точкой.
print("0.1 + 0.2 =", 0.1 + 0.2)
• Вывод результата суммы 0.1 и 0.2, демонстрирующий проблему точности (неполное равенство 0.3).
print("Разница между (0.1 + 0.2) и 0.3 меньше epsilon?", abs((0.1 + 0.2) - 0.3) < epsilon_sys)
• Вывод проверки, меньше ли разница между (0.1 + 0.2) и 0.3, чем машинный эпсилон.
print("Разница между (0.1 + 0.2) и 0.3 меньше 1e-15?", abs((0.1 + 0.2) - 0.3) < 1e-15)
• Вывод альтернативной проверки разницы с фиксированным порогом 1e-15 для сравнения.
# Демонстрация потери точности при операциях с большими числами
• Комментарий, поясняющий следующий блок кода, демонстрирующий потерю точности при операциях с разными порядками чисел.
large_num = 1e16
• Инициализация переменной large_num значением 1e16 (очень большое число).
small_increment = 1.0
• Инициализация переменной small_increment значением 1.0 (маленькое приращение).
print(f"{large_num} + {small_increment} - {large_num} =", large_num + small_increment - large_num)
• Вычисление и вывод разницы между (large_num + small_increment) и large_num, что демонстрирует потерю точности (ожидаемо должно получаться 1, но выводится 0).
# Машинный эпсилон и машинный ноль
• Комментарий, поясняющий, что далее сравниваются два критичных значения: машинный эпсилон и минимальное положительное число.
print(f"Машинный эпсилон: {sys.float_info.epsilon}")
• Вывод значения машинного эпсилона, полученного через sys.float_info.
print(f"Минимальное положительное число: {sys.float_info.min}")
• Вывод минимального положительного числа, которое может быть представлено в Python (машинный ноль).
Краткое резюме:
Код демонстрирует вычисление машинного эпсилона различными методами, определение функции для корректного сравнения чисел с плавающей точкой и показывает практические примеры ошибок округления и потери точности в вычислениях.
Еще раз итоговый результат работы кода:
Пример 3: Ошибки округления и машинный эпсилон
# Демонстрация ошибок округления print(0.1 + 0.2) # Результат не точно 0.3 print(abs((0.1 + 0.2) - 0.3) < sys.float_info.epsilon) # False print(abs((0.1 + 0.2) - 0.3) < 1e-15) # True
# Демонстрация потери точности при операциях с большими числами large_num = 1e16 small_increment = 1.0 print(large_num + small_increment - large_num) # 0.0 вместо 1.0
Этот пример показывает два важных момента:
- Классическая проблема 0.1 + 0.2 != 0.3 в арифметике с плавающей точкой
- Потеря точности при операциях с числами разных порядков величины
Неравномерность распределения чисел с плавающей точкой
Важно понимать, что плотность чисел, представимых в формате float, не постоянна. Расстояние между соседними числами с плавающей точкой зависит от их величины:
- В окрестности нуля разница между соседними числами может быть меньше машинного эпсилона
- Для очень больших значений разница между соседними числами может быть больше 1.
Это объясняет, почему мы можем работать с числами меньше машинного эпсилона, но точность вычислений будет зависеть от диапазона чисел.
Машинный эпсилон и машинный ноль
Машинный эпсилон и машинный ноль — это не синонимы. Машинный ноль (или минимальное положительное число с плавающей точкой) в Python можно получить через sys.float_info.min.
import sys
print(f"Машинный эпсилон: {sys.float_info.epsilon}")
print(f"Минимальное положительное число: {sys.float_info.min}")
Заключение
Машинный эпсилон — фундаментальное понятие в вычислительной математике и программировании. Понимание его сути помогает:
- Корректно сравнивать числа с плавающей точкой
- Оценивать точность вычислений
- Избегать ошибок, связанных с ограниченной точностью представления чисел
При работе с числами с плавающей точкой в Python всегда помните о их неточной природе и используйте подходящие методы сравнения с учетом допустимой погрешности.
Для большинства практических задач рекомендуется использовать встроенную функцию math.isclose() или модуль numpy.isclose() для сравнения чисел с плавающей точкой, а не проверять непосредственное равенство с помощью оператора ==.
ПОЛЕЗНЫЕ РЕСУРСЫ:
---------------------------------------------------
Сообщество дизайнеров в VK
https://vk.com/grafantonkozlov
Телеграмм канал сообщества
https://t.me/grafantonkozlov
Архив эксклюзивного контента
https://boosty.to/antonkzv
Канал на Дзен
https://dzen.ru/grafantonkozlov
---------------------------------------------------
Бесплатный Хостинг и доменное имя
https://tilda.cc/?r=4159746
Мощная и надежная нейронная сеть Gerwin AI
https://t.me/GerwinPromoBot?start=referrer_3CKSERJX
GPTs — плагины и ассистенты для ChatGPT на русском языке
https://gptunnel.ru/?ref=Anton
---------------------------------------------------