Точные числа с плавающей точкой, дроби и комплексные числа. Как с ними работать в Python?
Модуль decimal
Для использования: from decimal import *.
Если мы хотим сравнить два числа float, то нужно воспользоваться кодом ниже вместо обычного сравнения ==, так как существуют ограничения в сохранении точного значения чисел:
1
2
3
4
5
6
7
num=0.1+0.1+0.1eps=0.000000001# точность сравненияifabs(num-0.3)<eps:# число num отличается от числа 0.3 менее чем 0.000000001print('YES')else:print('NO')
Особенности и характеристики:
Тип данных Decimal - класс из модуля decimal, который тоже представляет собой число с плавающей точкой, и служит для того, чтобы выполнять операции над вещественными числами без ошибок.
С ними работают все привычные операции (сложение, умножение и т.д., но не рекомендуется оперировать между Decimal и float), их можно передавать как аргумент в математические функции (при этом вернётся float), их можно сравнивать между собой (также допускается точное равенство), можно формировать списки из Decimal и искать среди них min и max.
Тип данных Decimal является неизменяемым.
Отличие float и Decimal: В Python тип данных float реализован по стандарту IEEE-754 как число с плавающей точкой двойной точности (64 бита) с основанием экспоненты равным 2. Так как float поддерживается аппаратно, быстродействие при использовании этого типа данных сравнительно велико. Тип данных Decimal – число с плавающей точкой с основанием экспоненты 10 Он реализован по стандарту IBM: General Decimal Arithmetic Specification, в свою очередь основанному на стандартах IEEE. Тип данных Decimal реализован программно, поэтому он в разы медленнее типа данных float. Сам тип данных Decimal написан на языке С.
Характеристика / тип
float
Decimal
Реализация
аппаратная
программная
Размер
64 бит
не ограничен
Основание экспоненты
2
10
Скорость
✔️
❌
Настраиваемость
❌
✔️
Для высокоточных вычислений
❌
✔️
Для типа данных Decimal можно настроить:
точность выполнения операций в количестве десятичных знаков (по умолчанию - 28);
режимы округления;
режимы обработки исключительных ситуаций (деление на ноль, переполнение и т. д).
Создать Decimal число можно из обычного целого числа (int), из числа с плавающей точкой (float) или из строки (str):
При создании Decimal чисел из чисел с плавающей точкой (float) возникают проблемы, так как float числа округляются внутри до ближайшего возможного, а Decimal об этом ничего не знает и копирует содержимое float:
quantize() - принимает в качестве аргумента объект Decimal, указывающий на формат округления:
1
2
3
4
5
6
7
8
9
fromdecimalimport*getcontext().prec=4# устанавливаем точность числаnum=Decimal('3.1415926535')print(num.quantize(Decimal('1.000')))# округление до 3 цифр в дробной части print(num.quantize(Decimal('1.00')))# округление до 2 цифр в дробной частиprint(num.quantize(Decimal('1.0')))# округление до 1 цифр в дробной части
Опасность
При этом, если точность числа установлена в 2, а формат округления Decimal('1.00'), то возникнет ошибка.
В качестве второго параметра этот метод принимает стратегию округления: ROUND_CEILING – округление в сторону бесконечности (Infinity); ROUND_FLOOR – округляет в сторону минус бесконечности (- Infinity); ROUND_DOWN – округление в сторону нуля; ROUND_HALF_EVEN – округление до ближайшего четного числа, число 6.5 округлится не до 7, а до 6; ROUND_HALF_DOWN – округление до ближайшего нуля; ROUND_UP – округление от нуля; ROUND_05UP – округление от нуля (если последняя цифра после округления до нуля была бы 0 или 5, в противном случае к нулю).
Тип данных Fraction представляет из себя рациональное число в виде дроби.
Особенности и характеристики:
Fraction, как и Decimal, реализован программно, поэтому он в разы медленнее встроенных числовых типов данных int и float.
Тип данных Fraction неизменяемый.
Операции над данными этого типа приводят к созданию новых объектов, при этом старые не меняются. Fraction числа можно сравнивать между собой точно так же, как и любые другие числа (также можно сравнить Fraction и целые числа без явного приведения типов); с ним работает все арифметические операции (возведение в степень может вернуть float).
При создании рационального числа Fraction, автоматически происходит сокращение числителя и знаменателя дроби, а если результат - целое число, то оно и будет выведено.
Fraction числа можно передавать как аргументы функций, ожидающих float. Тогда они будут преобразованы во float. К примеру, модуль math, оперирующий float числами, может работать и с Fraction числами (возвращать функции будут float).
Создать Fraction число можно несколькими способами:
из целых чисел, передав значения числителя и знаменателя дроби;
из строки на основании десятичного представления;
из строки на основании обыкновенной дроби;
из числа с плавающей точкой (не рекомендуется, так как в Fraction попадет уже неправильно округленное число).
# Вывод:
PI = 3.141592653589793
No limit = 3141592653589793/1000000000000000
3
16/5
22/7
267/85
311/99
355/113
3126535/995207
Инфо
В Python нельзя совершать арифметические операции (+, -, *, /) между типами Decimal и Fraction.
Комплексные числа в Python
Общая форма представления комплексного числа следующая: real + imag j, где
real – вещественная часть комплексного числа;
imag – мнимая часть комплексного числа, которая завершается символом j или J.
Особенности и характеристики:
С complex числами работают все привычные операции: сложение, вычитание, умножение, деление, возведение в степень. Мы также можем совершать арифметические операции над complex и целыми числами (миксовать complex, int, float).
Для работы с комплексными числами вместо модуля math нужно использовать модуль cmath
Комплексные числа можно создать с помощью литерала, как выше, а можно с помощью функции complex(), которая принимает два аргумента: вещественную и мнимую часть числа, либо строковое представление числа:
1
2
3
4
5
6
7
z1=-3+2j# создание на основе литералаz2=complex(6,-8)# z2 = 6 - 8jz3=complex(0,2.5)# z3 = 2.5jz4=complex(5,0)# z4 = 5 + 0jz5=complex('3+4j')# создание на основе строкиprint(z1,z2,z3,z4,z5,sep='\n')
1
2
3
4
5
6
# Вывод:
(-3+2j)
(6-8j)
2.5j
(5+0j)
(3+4j)
Методы и свойства
real - для получения вещественной части, а imag - для мнимой:
1
2
3
4
z=3+4jprint('Действительная часть =',z.real)print('Мнимая часть =',z.imag)
1
2
3
# Вывод:
Действительная часть = 3.0
Мнимая часть = 4.0