4.1 Округление вещественных чисел
Вещественные (дробные) числа по-английски называются floating point number — числа с плавающей точкой: в США для разделения целой и дробной части числа используется точка. Отсюда и происходит название float.
Как мы уже разбирали, при преобразовании вещественного числа (float) в целое (int), оно всегда округляется вниз до целого — его дробная часть просто отбрасывается. А ведь легко можно представить ситуацию, когда дробное число нужно округлить просто до ближайшего целого. Что делать в такой ситуации?
Для этого в Python есть встроенная функция round(). Она была придумана еще до создания библиотеки math, поэтому не входит в нее. Функции для округления вниз и округления вверх находятся в библиотеке math.
Функция round() округляет число до ближайшего целого:
round(вещественное_число)
Данная функция вернет целое число, к которому ближе переданное в нее вещественное число. Важно отметить, что если дробная часть числа равна 0.5, функция round() использует метод округления до ближайшего четного целого числа. Это называется "банковским округлением" и позволяет уменьшить систематическую ошибку при многократном округлении. Например:
Примеры:
| Команда | Результат |
|---|---|
| x = round(4.1) | 4 |
| x = round(4.5) | 4 |
| x = round(4.9) | 5 |
| x = round(5.5) | 6 |
Функция math.ceil() округляет число до целого вверх, примеры:
| Команда | Результат |
|---|---|
| x = math.ceil(4.1) | 5 |
| x = math.ceil(4.5) | 5 |
| x = math.ceil(4.9) | 5 |
Функция math.floor() округляет число до целого вниз, примеры:
| Команда | Результат |
|---|---|
| x = math.floor(4.1) | 4 |
| x = math.floor(4.5) | 4 |
| x = math.floor(4.9) | 4 |
Хотя для округления числа до целого вниз проще использовать функцию преобразования типов int():
| Команда | Результат |
|---|---|
| x = int(4.9) | 4 |
Если вам сложно запомнить эти команды, поможет небольшой урок английского:
math— математикаround— круг/округлятьceiling— потолокfloor— пол
4.2 Устройство чисел с плавающей точкой
Тип float в Python может хранить значения в диапазоне -1.7*10308 до +1.7*10308. Такой гигантский диапазон значений объясняется тем, что тип float устроен совсем иначе по сравнению с целыми типами. Каждая переменная типа float содержит два числа: первое называется мантисса, а второе — степень.
Допустим, у нас есть число 123456789, и мы сохранили его в переменную типа float. Тогда число будет преобразовано к виду 1.23456789*108, и внутри типа float будут хранится два числа — 23456789 и 8. Красным выделена «значащая часть числа» (манти́сса), зелёным — степень.
Такой подход позволяет хранить как очень большие числа, так и очень маленькие. Но поскольку размер числа ограничен 8 байтами (64 бита) и часть бит используется под хранение степени (а также знака числа и знака степени), максимальная длина мантиссы ограничена 15 цифрами.
Это очень упрощенное описание устройства вещественных чисел, более полное можно загуглить.
4.3 Потеря точности при работе с вещественными числами
При работе с вещественными числами всегда нужно иметь в виду, что вещественные числа — неточные. Всегда будут ошибки округления, ошибки преобразования из десятичной системы в двоичную и, наконец, самое частое — потеря точности при сложении/вычитании чисел слишком разных размерностей.
Последнее — самая неожиданная ситуация для новичков в программировании.
Если из числа 109 вычесть 1/109, мы получим опять 109.
| Вычитание чисел слишком разных размерностей | Объяснение |
|---|---|
|
Второе число слишком маленькое, и его значащая часть игнорируется (выделено серым). Красным выделены 15 значащих цифр. |
Что тут сказать, программирование — это не математика.
4.4 Опасность сравнения вещественных чисел
Еще одна опасность подстерегает программистов при сравнении вещественных чисел. Поскольку при работе с этими числами могут накапливаться ошибки округления, то возможны ситуации, когда вещественные числа должны быть равны, но они не равны. И наоборот: числа должны быть не равны, но они равны.
Пример:
| Команда | Пояснение |
|---|---|
| a = 1000000000.0 b = 0.000000001 c = a – b | В переменной a будет значение 1000000000.0 В переменной c будет значение 1000000000.0 (число в переменной b слишком маленькое) |
В приведенном выше примере a и c не должны быть равны, но они равны.
Или возьмем другой пример:
| Команда | Пояснение |
|---|---|
| a = 1.00000000000000001 b = 1.00000000000000002 | В переменной a будет значение 1.0 В переменной b будет значение 1.0 |
На практике вещественные числа сравнивают так:
Берется какое-то очень маленькое число. Если разница чисел (по модулю) меньше, чем это маленькое число, то они считаются равными. Пример:
a = 0.00000000012
b = 0.000000000011
if abs(a - b) < 0.00001:
print("равны")
else:
print("не равны")
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ