Decimal, Fraction и комплексные числа в Python

Точные числа с плавающей точкой, дроби и комплексные числа. Как с ними работать в Python?

Модуль decimal

Для использования: from decimal import *.

Если мы хотим сравнить два числа float, то нужно воспользоваться кодом ниже вместо обычного сравнения ==, так как существуют ограничения в сохранении точного значения чисел:

1
2
3
4
5
6
7
num = 0.1 + 0.1 + 0.1
eps = 0.000000001         # точность сравнения

if abs(num - 0.3) < eps:  # число num отличается от числа 0.3 менее чем 0.000000001
    print('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 написан на языке С.

Характеристика / типfloatDecimal
Реализацияаппаратнаяпрограммная
Размер64 битне ограничен
Основание экспоненты210
Скорость✔️
Настраиваемость✔️
Для высокоточных вычислений✔️

Для типа данных Decimal можно настроить:

  • точность выполнения операций в количестве десятичных знаков (по умолчанию - 28);
  • режимы округления;
  • режимы обработки исключительных ситуаций (деление на ноль, переполнение и т. д).

Создать Decimal число можно из обычного целого числа (int), из числа с плавающей точкой (float) или из строки (str):

1
2
3
4
5
6
7
from decimal import *

d1 = Decimal(1)
d2 = Decimal(567)
d3 = Decimal(-93)
d4 = Decimal('12345')
d5 = Decimal('52.198')

При создании Decimal чисел из чисел с плавающей точкой (float) возникают проблемы, так как float числа округляются внутри до ближайшего возможного, а Decimal об этом ничего не знает и копирует содержимое float:

1
2
3
4
5
6
7
from decimal import *

num = Decimal(0.1)

print(num)

# Вывод: 0.1000000000000000055511151231257827021181583404541015625

Методы decimal

  • У Decimal есть некоторые встроенные математические методы:
ФункцияОписание
sqrt()вычисляет квадратный корень из Decimal числа
exp()возвращает e^x для Decimal числа
ln()вычисляет натуральный логарифм Decimal числа
log10()вычисляет десятичный логарифм Decimal числа
  • as_tuple() - возвращает кортеж из 3-ёх элементов:
    • sign – знак числа (0 для положительного числа и 1 для отрицательного числа);
    • digits – цифры числа (цифра 0 не учитывается, если целая часть равна нулю);
    • exponent – значение экспоненты (количество цифр после точки, умноженное на −1),
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from decimal import *

num1 = Decimal('-1.4568769017')
num2 = Decimal('0.523')
num_tuple = num.as_tuple()

print(num1.as_tuple())
print(num2.as_tuple())
print(num1_tuple.sign)
print(num1_tuple.digits)
print(num1_tuple.exponent)
1
2
3
4
5
6
# Вывод:
DecimalTuple(sign=1, digits=(1, 4, 5, 6, 8, 7, 6, 9, 0, 1, 7), exponent=-10)
DecimalTuple(sign=0, digits=(5, 2, 3), exponent=-3)
1
(1, 4, 5, 6, 8, 7, 6, 9, 0, 1, 7)
-10
  • getcontext() - просмотр базовых параметров Decimal
1
2
3
4
5
from decimal import *

print(getcontext())

# Вывод: Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
  • quantize() - принимает в качестве аргумента объект Decimal, указывающий на формат округления:
1
2
3
4
5
6
7
8
9
from decimal import *

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, в противном случае к нулю).

1
2
3
4
5
6
from decimal import *

num = Decimal('3.456')

print(num.quantize(Decimal('1.00'), ROUND_CEILING))
print(num.quantize(Decimal('1.00'), ROUND_FLOOR))
1
2
3
# Вывод:
3.46
3.45

Модуль fractions

Для использования: from fractions import Fraction

Тип данных Fraction представляет из себя рациональное число в виде дроби.

Особенности и характеристики:

  • Fraction, как и Decimal, реализован программно, поэтому он в разы медленнее встроенных числовых типов данных int и float.
  • Тип данных Fraction неизменяемый.
  • Операции над данными этого типа приводят к созданию новых объектов, при этом старые не меняются. Fraction числа можно сравнивать между собой точно так же, как и любые другие числа (также можно сравнить Fraction и целые числа без явного приведения типов); с ним работает все арифметические операции (возведение в степень может вернуть float).
  • При создании рационального числа Fraction, автоматически происходит сокращение числителя и знаменателя дроби, а если результат - целое число, то оно и будет выведено.
  • Fraction числа можно передавать как аргументы функций, ожидающих float. Тогда они будут преобразованы во float. К примеру, модуль math, оперирующий float числами, может работать и с Fraction числами (возвращать функции будут float).

Создать Fraction число можно несколькими способами:

  • из целых чисел, передав значения числителя и знаменателя дроби;
  • из строки на основании десятичного представления;
  • из строки на основании обыкновенной дроби;
  • из числа с плавающей точкой (не рекомендуется, так как в Fraction попадет уже неправильно округленное число).
1
2
3
4
5
from fractions import Fraction

num1 = Fraction(3, 4)  # 3 – числитель, 4 – знаменатель
num2 = Fraction('0.55')
num3 = Fraction('1/9')

Методы и свойства

  • numerator - для получения числителя, denominator - для получения знаменателя:
1
2
3
4
5
6
from fractions import Fraction

num = Fraction('5/16')

print('Числитель дроби равен:', num.numerator)
print('Знаменатель дроби равен:', num.denominator)
1
2
3
# Вывод:
Числитель дроби равен: 5
Знаменатель дроби равен: 16
  • as_integer_ratio() – возвращает кортеж, состоящий из числителя и знаменателя данного Fraction числа.
  • limit_denominator() – возвращает самую близкую к данному числу рациональную дробь, чей знаменатель не превосходит переданного аргумента:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from fractions import Fraction
import math

print('PI =', math.pi)

num = Fraction(str(math.pi))

print('No limit =', num)

for d in [1, 5,  50, 90, 100, 500, 1000000]:
    limited = num.limit_denominator(d)
    print(limited)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Вывод:
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
1
2
3
4
5
6
7
z1 = 5 + 7j
z2 = 1j
z3 = -3 + 5J
z4 = 1.5 + 3.2j

print(z1, z2, z3, z4, sep='\n')
print(type(z1))
1
2
3
4
5
6
# Вывод:
(5+7j)
1j
(-3+5j)
(1.5+3.2j)
<class 'complex'>

Комплексные числа можно создать с помощью литерала, как выше, а можно с помощью функции complex(), которая принимает два аргумента: вещественную и мнимую часть числа, либо строковое представление числа:

1
2
3
4
5
6
7
z1 = -3 + 2j              # создание на основе литерала
z2 = complex(6, -8)       # z2 = 6 - 8j
z3 = complex(0, 2.5)      # z3 = 2.5j
z4 = complex(5, 0)        # z4 = 5 + 0j
z5 = 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+4j

print('Действительная часть =', z.real)
print('Мнимая часть =', z.imag)
1
2
3
# Вывод:
Действительная часть = 3.0
Мнимая часть = 4.0
  • conjugate() - возвращает сопряжённое комплексное число.
  • Для нахождения модуля комплексного числа, используется встроенная функция abs().
Поддержать автора
NoisyCake cloudtipscloudtips
0%