Найти в Дзене
Креативный дизайн

Машинный эпсилон в Python: точность в мире плавающей точки

Машинный эпсилон — это не «минимальное различимое число», как часто ошибочно полагают. Корректное определение машинного эпсилона: разница между числом 1 и наименьшим числом с плавающей точкой, которое больше 1. В Python для чисел с плавающей точкой двойной точности (тип float) это значение составляет примерно 2.22e-16. Важно понимать, что плотность представления чисел в формате с плавающей точкой неравномерна. Близко к нулю можно представить числа с гораздо меньшей разницей, чем машинный эпсилон, а для очень больших чисел разница между соседними представимыми значениями может быть значительно больше единицы. import sys
epsilon = sys.float_info.epsilon
print(f"Машинный эпсилон: {epsilon}")
# Вывод: Машинный эпсилон: 2.220446049250313e-16 В этом коде мы импортируем модуль sys и обращаемся к атрибуту float_info.epsilon, который содержит значение машинного эпсилона для типа float в Python. Мы можем вычислить машинный эпсилон самостоятельно с помощью простого алгоритма: Тот же код ниже для
Оглавление

Что такое машинный эпсилон?

Машинный эпсилон — это не «минимальное различимое число», как часто ошибочно полагают. Корректное определение машинного эпсилона: разница между числом 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

-2

В этом коде мы импортируем модуль 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

Разберем этот код пошагово:

  1. Начинаем с eps = 1.0
  2. В цикле делим eps на 2, пока выражение eps + 1 > 1 истинно
  3. Когда eps становится настолько малым, что eps + 1 == 1, мы выходим из цикла
  4. Умножаем последнее значение eps на 2, чтобы получить машинный эпсилон

Метод 3: Используя модуль math с Python 3.5+

import math

epsilon = math.ulp(1.0)
print(f"Машинный эпсилон через math.ulp: {epsilon}")
# Вывод: Машинный эпсилон через math.ulp: 2.220446049250313e-16

-4

Здесь мы используем функцию 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

-5

Вывод:

Машинный эпсилон: 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}")

В этом примере мы создаем функцию для сравнения чисел с плавающей точкой с учетом относительной погрешности.

Результат работы кода:

-7

Описание кода:

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 (машинный ноль).

Краткое резюме:
Код демонстрирует вычисление машинного эпсилона различными методами, определение функции для корректного сравнения чисел с плавающей точкой и показывает практические примеры ошибок округления и потери точности в вычислениях.

Еще раз итоговый результат работы кода:

-8

Пример 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

Этот пример показывает два важных момента:

  1. Классическая проблема 0.1 + 0.2 != 0.3 в арифметике с плавающей точкой
  2. Потеря точности при операциях с числами разных порядков величины

Неравномерность распределения чисел с плавающей точкой

Важно понимать, что плотность чисел, представимых в формате float, не постоянна. Расстояние между соседними числами с плавающей точкой зависит от их величины:

  • В окрестности нуля разница между соседними числами может быть меньше машинного эпсилона
  • Для очень больших значений разница между соседними числами может быть больше 1.

Это объясняет, почему мы можем работать с числами меньше машинного эпсилона, но точность вычислений будет зависеть от диапазона чисел.

Машинный эпсилон и машинный ноль

Машинный эпсилон и машинный ноль — это не синонимы. Машинный ноль (или минимальное положительное число с плавающей точкой) в Python можно получить через sys.float_info.min.

import sys

print(f"Машинный эпсилон: {sys.float_info.epsilon}")
print(f"Минимальное положительное число: {sys.float_info.min}")

-9

Заключение

Машинный эпсилон — фундаментальное понятие в вычислительной математике и программировании. Понимание его сути помогает:

  1. Корректно сравнивать числа с плавающей точкой
  2. Оценивать точность вычислений
  3. Избегать ошибок, связанных с ограниченной точностью представления чисел

При работе с числами с плавающей точкой в 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

---------------------------------------------------