Вещественные числа — float

Главным отличием вещественных чисел от целых является наличие символа "." которая отделяет целую часть от дробной:

2.78    2.0    2.    0.78    .78

Причем обратите внимание на числа 2. и .78, т.е. если Python видит, что слева или справа от точки ничего нет, то он понимает, что либо целая либо дробная часть равна \(0\).

Другой отличительной чертой является необязательное наличие символа "e" – экспоненты:

2.78e-1    2e10    2e+10    2e-10    0e0

Если на длину целых чисел не накладывается абсолютно никаких ограничений по величине, то с вещественными числами это не так. Что бы узнать информацию о числах типа float для компьютера на котором выполняется программа, можно воспользоваться командой sys.float_info:

>>> import sys
>>> sys.float_info    #  вывод я отформатировал вручную
sys.float_info(max=1.7976931348623157e+308,
               max_exp=1024,
               max_10_exp=308,
               min=2.2250738585072014e-308,
               min_exp=-1021,
               min_10_exp=-307,
               dig=15,
               mant_dig=53,
               epsilon=2.220446049250313e-16,
               radix=2,
               rounds=1)
  • max - максимальное представимое число;
  • max_exp - максимальня степень \(2\);
  • max_10_exp - максимальное число e, такое что 10**e находится в [min , max];
  • min - минимальное представимое число;
  • min_exp - минимальная степень \(2\);
  • min_10_exp - минимальное число e, такое что 10**e находится в [min , max];
  • dig - максимальное число цифр, которыми можно точно отобразить число;
  • mant_dig - максимальное число цифр в radix системе счисления, которыми можно точно отобразить число;
  • epsilon - разность между \(1\) и наименьшим числом большим \(1\) которую можно представить как число с плавающей точкой;
  • radix - основание используемой системы счисления;
  • rounds - целочисленная константа определяющая режим округления.

В данном выводе для нас представляют особый интерес значения: max, min, max_10_exp и min_10_exp, которые определяют пределы диапазонов для мантисы и степени, например:

>>> 10.0**308    #  не приводит к ошибке
1e+308
>>> 10.0**309    #  а это уже недопустимо
Traceback (most recent call last):
  File "<stdin>", line 1, in 
OverflowError: (34, 'Numerical result out of range')

Значения -inf, inf и nan

Тем не менее вместо ошибок OverflowError, когда число выходит за указанные пределы, иногда появляются значения -inf и inf:

>>> 1.7*10e307
1.7e+308
>>> 
>>> 1.7*10e307*1.7*10e307
inf
>>> 
>>> -10e9999
-inf

Возможно, к появляению ошибки OverflowError приводит только операция возведения в степень (скорее всего, хотя может я и не прав, точные причины возникновения данной ошибки кроются где-то в недрах языка C, на котором реализован интерпретатор CPython). Все что нужно знать о значениях -inf и inf это то что это числа обозначающие плюс минус бесконечность. Да, это на самом деле числа числа с плавающей запятой, которые могут учавствовать в выражениях:

>>> inf = 10e777
>>> inf**3000
inf
>>> inf - 3000
inf

Однако, что бы вы не делали с бесконечностью, она так и останется бесконечностью. Единственное что можно попробовать, так это вычесть из одной бесконечности другую бесконечность:

>>> inf - inf
nan

И мы получили nan - значение, которое означает неопределяемое число (или "не число"). Но оно ведет себя как число, и в каком бы выражении оно не появилось, результат всегда будет nan (кроме nan/0 т.к. деление на \(0\) всегда приводит к появлению ошибки):

>>> 2*nan + 1
nan
>>> 
>>> -inf*inf*nan
nan

В реальной жизни, вы скорее всего никогда не встретитесь с значениями -inf, inf и nan, если только не собираетесь заниматься какими-нибудь весьма специфичными вычислениями. Но, как правило, тех, кто встречается с этими значениями впервые, почему-то смущает, то что они не понимают откуда берутся и как действуют эти значения.


Арифметические операции

Целые числа поддерживают следующие математические операции, которые отсортированы по убыванию приоритета:

Операция Результат Замечание
1 x ** y возводит x в степень y (I)
2 pow(x, y[, z]) возводит x в степень y по модулю z, где z – необязательный аргумент (I)
3 divmod(x, y) возвращает кортеж с парой чисел (x // y, x % y) (II)
4 x.conjugate() возвращает \(\bar{x}\) - число, которое комплексно сопряжено с \(x\)
5 complex(re, im) преобразует re в комплексное число (по умолчанию im = 0) (V)
6 float(x) преобразует x в вещественное число (число с плавающей точкой) (V)
7 int(x) переобразует x в целое число, представленное в десятичной системе счисления (V)
8 abs(x) абсолютное значение (модуль) числа x
9 +x делает число x положительным
10 -x делает число x отрицательным
11 x % y остаток от деления x на y (II)
12 x // y результат целочисленного деления x на y (III) (II)
13 x / y результат "истинного" деления x на y
14 x * y произведение x и y
15 x - y разность x и y
16 x + y сумма x и y

Важно: приоритет математических операций выше побитовых логических операций и операций сравнения.

Замечания:

I. возведение \(0\) в степень \(0\) возвращает \(1\):

>>> 0**0, pow(0, 0)
(1, 1)

Извлечение корней четной степени из отрицательных чисел не вызывает ошибки, а возвращает комплексное число:

>>> (-0.1)**0.5
(1.9363366072701937e-17+0.31622776601683794j)
>>> 
>>> pow(-3.14, 1/4)
(0.9412769291411894+0.9412769291411892j)

Использование в выражении x ** y слишком больших значений x и y типа float может привести к ошибке OverflowError.

II. функция divmod() и операции %, // не работают для комплексных чисел. Для вас это может быть и очевидно, но не пользователя для которого вы пишите программу.

III. Данная операция всегда возвращает целое число, т.е. если число x можно представить в виде d*y + r, то x//y = d (r – остаток от деления). Так же следует иметь ввиду, что результат данной операции всегда округляется в сторону минус бесконечности:

>>> 0.1//0.2, (-0.1)//0.2, 0.1//(-0.2), (-0.1)//(-0.2)
(0.0, -1.0, -1.0, 0.0)

Это немного сбивает с толку, но проверив результат по формуле x = d*y + r, вы убедитесь что все верно.

IV. встроенная функция int() отбрасывает дробную часть вещественных чисел:

>>> int(3.14), int(-3.14)
(3, -3)

V. строго говоря эти функции не являются математическими, но они могут учавствовать в математических выражениях Python и поэтому должны обладать приоритетом.


Операции сравнения

Для сравнения чисел, доступно \(8\) операций сравнения, причем все они имеют одинаковый приоритет:

Операция Результат Замечание
1 x < y True если x меньше y, иначе False
2 x <= y True если x меньше или равно y, иначе False
3 x > n True если x больше y, иначе False
4 x >= n True если x больше или равно y, иначе False
5 x == y True если x равно y, иначе False
6 x != y True если x не равно y, иначе False
7 x is y True если x и y это один и тот же объект, иначе False
8 x is not y True если x и y это не один и тот же объект, иначе False

Важно: приоритет операций сравнения ниже математических и побитовых операций.

Важно: числа типа float не являются десятичными дробями и используют двоичную арифметику компьютера, поэтому многие, даже самые простые выражения могут вычисляться с ничтожно малыми погрешностями. Однако, из-за этих погрешностей, вполне очевидные операции сравнения работают не так как ожидается:

>>> 0.7 - 0.4 - 0.3 == 0  #  должно быть True, но получаем:
False
>>> 
>>> 0.7-0.4-0.3    #  потому что результат выражения не равен 0
-5.551115123125783e-17

В Python сравнение x > y and y > z является эквивалентным x > y > z т.е. сравнения связаные оператором and в произвольные цепочки могут быть записаны в более компактной форме. Выполнение таких выражений начинается слева направо и останавливается как только будет получено первое значение False. Это означает, что если в выражении x > y > z сравнение x > y вернет False то сравнение y > z выполняться не будет.


Методы вещественных чисел

Вещественные числа – это объекты, которые обладают следующими методами:

float.as_integer_ratio()
возвращает пару целых чисел (кортеж), первое из которых равно числителю а второе всегда положительному знаменателю обыкновенной дроби, значение которой точно совпадает с указанным исходным числом типа float:
>>> (0.3).as_integer_ratio()
(5404319552844595, 18014398509481984)

Появление таких больших чисел связано с тем, что числа типа float на самом деле не являются десятичными дробями, и хранятся в памяти с небольшой погрешностью:

>>> 5404319552844595/18014398509481984
0.3
>>> from decimal import *
>>> 
>>> Decimal(5404319552844595)/Decimal(18014398509481984)
Decimal('0.2999999999999999888977697537')
float.is_integer()
возвращает True если дробная часть числа равна \(0\) и False если нет:
>>> (3.0).is_integer()
True
>>> (3.14).is_integer()
False

Python позволяет преобразовывать вещественные числа из десятичной системы счисления в шестнадцатеричную и обратно. Создание вещественных чисел из их шестнадцатеричного представления, позволяет избежать погрешности которая неминуемо возникает при переводе десятичных чисел в двоичное представление. Шестнадцатеричные вещественные числа в Python задаются строкой вида:

[sign] ['0x'] integer ['.' fraction] ['p' exponent]

где sign – это необязательный знак, который может быть как + или -; integer – и fraction – целая и дробная части которые должны обязательно содержать хотя бы по одной цифре, '0x' – уже знакомый нам префикс, обозначающий шестнадцатеричные числа и 'p' exponent – экспонента в виде десятичного целого числа со знаком или без.

Показатель степени 'p' exponent является степенью двойки, например, перевод числа '0x2.f1ap+3' из шестнадцатеричной системы счисления в десятичную будет выглядеть следующим образом:

>>> (2 + 15./16 + 1./16**2 + 10./16**3)*2**3
23.55078125

Применение обратного преобразования дает другую шестнадцатеричную строку, которая, однако, представляет тоже самое число:

>>> (_).hex()
'0x1.78d0000000000p+4'
>>> 
>>> (1 + 7./16 + 8./16**2 + 13./16**3)*2**4
23.55078125
float.hex()
возвращает представление числа в шестнадцатеричной системе счисления:
>>> (3.141592).hex()
'0x1.921fafc8b007ap+1'
>>> 
>>> (10.01).hex()
'0x1.4051eb851eb85p+3'
classmethod float.fromhex(s)
метод класса для преобразования шестнадцатеричной строки s в число типа float:
>>> float.fromhex('0x1.4051eb851eb85p+3')
10.01
>>> 
>>> # строка может иметь начальные и конечные пробельные символы:
... float.fromhex('   0x1.921fafc8b007ap+1    ')
3.141592
>>> 
>>> float.fromhex('0x0.1p+3')
0.5
>>> float.fromhex('0x0.31p+1')
0.3828125