Разбираемся в числах с плавающей точкой (часть 0)
Здравствуйте, хабровчане. Я давно увлекаюсь темой регистров с плавающей точкой. Меня всегда волновало то, как происходит вывод на экран и т.д. Помню, давным-давно в универе реализовывал свой класс чисел с плавающей точкой, состоящих из 512 бит. Единственное, что я не мог никак реализовать — это вывод на экран.
Как только у меня появилось свободное время, я взялся за старое. Завел себе тетрадку и пошло-поехало. Хотелось додуматься до всего самому, лишь иногда заглядывая в стандарт IEEE 754.
И вот что из всего этого вышло. Интересующихся прошу под кат.
Чтобы освоить эту статью, надо знать следующее: что такое бит, двоичная система, арифметика на уровне знания отрицательных степеней. В статье не будут затронуты инженерные подробности реализации на уровне процессора а также нормализованные и денормализованные числа. Больший упор сделан на перевод числа в двоичную форму и наоборот, а также объяснение того, как вообще хранятся числа с плавающей точкой в виде битов.
Числа с плавающей точкой — очень мощный инструмент, которым надо уметь правильно пользоваться. Они не столь банальны, как целочисленные регистры, но и не столь сложны, если в них грамотно и потихоньку вникнуть.
В сегодняшней статье для примера я буду использовать 32-битные регистры. Числа с двойной точностью (64-битные) работают абсолютно по той же логике.
Сначала поговорим о том, как хранятся числа с плавающей точкой. Старший 31 бит у нас знаковый. Единичка значит, что число отрицательное, а ноль, соответственно наоборот. Далее идут 8 бит экспоненты. Эти 8 битов представляют из себя обычное беззнаковое число. И в самом конце идут 23 бита мантиссы. Для удобства будем обозначать знак как S, экспоненту как E, а мантиссу, как ни странно, M.
Получаем общую формулу
У мантиссы принято считать один неявный единичный бит. То есть мантисса будет представлять из себя 24 бита, но так как старший 23-й бит всегда единица, то его можно не записывать. Таким образом это «ограничение» будет давать нам единственность представления любого числа.
Мантисса из себя представляет обычное двоичное число, но в отличие от целых чисел, старший бит это 2^0 степени и далее по убыванию степеней. Вот тут и пригождается экспонента. В зависимости от ее значения, степень двойки старшего бита увеличивается либо уменьшается. Вот и вся гениальность этой задумки.
Давайте попробуем показать это на наглядном примере:
Представим число 3.625 в двоичном виде. Поначалу разобьем это число на степени двойки.
Степень старшей двойки равна единице. E – 127 = 1. E = 128.
0 1000000 11010000000000000000000
Вот и все наше число.
Попробуем также и в обратную сторону. Пусть у нас есть 32 бита, произвольные 32 бита.
0 10000100 (1)11011100101000000000000
В скобочках указан тот самый неявный старший бит.
Сначала вычислим экспоненту. E = 132. Соответственно степень старшей двойки будет равна 5. Итого имеем следующее число:
Нетрудно догадаться, что мы можем хранить всего лишь диапазон из 24-х степеней двойки. Соответственно, если два числа отличаются в экспоненте на больше чем 24, то при сложении число остается равным большему среди них.
Для удобной конвертации я накидал небольшую программу на языке C.
Шагом сетки является минимальная разницу между двумя соседними числами с плавающей точкой. Если представить последовательность битов такого числа как обычное целое число, то соседнее число с плавающей точкой будет отличаться в битах как целое число на единицу.
Можно выразиться иначе. Два соседних числа с плавающей точкой будут отличаться на 2 ^ (E — 127 — 23). То есть на разницу, равную значению младшего бита.
В качестве доказательства можете поменять main в коде и скомпилировать еще раз.
Думаю на сегодня можно закругляться, а то получается слишком длинно. В следующий раз буду писать о сложении чисел с плавающей точкой и потере точности при округлении.
[ Сборник задач ]
Тема 2.Числа с плавающей
точкой
Операции над числами с плавающей точкой могут вызывать ошибки и давать неожиданные результаты. Работа с типом float требует повышенного внимания.
Также отображение таких чисел конечно, несмотря на то что в математике имеются иррациональные числа (например, Пи, корень квадратный из двух).
При изучении темы следует обратить внимание на:
- Структуру числа с плавающей точкой (какие его части сохраняются в памяти и сколько бит занимает каждая); типа float ;
- Способы преобразования в целые числа ( trunc , floor , ceiling , round );
- Правила округления.
Читайте также
Сначала вычислим выражение, а потом определим его тип при помощи функции type().
Решение – Интерактивный режим
>>> 3 + 3.0 + 4
10.0
>>> type(10.0)
<class ‘float’>
Как видим, получается тип float, т.е. число с плавающей точкой.
2. Из каких трех элементов состоит тип данных float?
Числа типа float состоят из:
– знака (+ или -);
– экспоненты, обозначающей степень десятки (например, е+5);
– значимых чисел (до и после запятой).
-11.03е+5 – типичная запись вещественного числа. Она аналогична варианту -11.03 * 10 ** 5 или -1103000.0.
В результате всегда получим целое число. Знак сохраняется, целочисленная часть остается, а вся дробная часть отрезается.
4. Какой самый простой способ получения нуля с плавающей точкой (т.е. числа 0.0)? Использовать можно только функцию float().
Первое, что приходит на ум: передать 0 в качестве аргумента внутрь функции float().
Решение – Интерактивный режим
>>> float(0)
0.0
Однако, все еще проще: если не передавать аргумент, то по умолчанию ставится 0.
Решение – Интерактивный режим
>>> float()
0.0
5. Каким методом можно проверить, является ли заданное вещественное число целым?
С нашей точки зрения 4.0 и 4 – равные числа, и оба являются целыми, так как 0 после точки не несет никакой смысловой нагрузки. В Python эти числа также равны, но первое относится к типу float, а второе – к int.
Для ответа на вопрос можно использовать 2 варианта:
A. Преобразовать число в тип int и сравнить его с самим собой.
Решение – Интерактивный режим
>>> int(4.0) == 4.0
True
Б. Так как в вопросе использовано слово «метод», то логичнее воспользоваться соответствующим методом.
Решение – Интерактивный режим
>>> (4.0).is_integer()
True
6. Какие типы данных можно преобразовать в числа с плавающей точкой?
К вещественным числам можно преобразовать строки и числа:
Решение – Интерактивный режим
>>> float(8)
8.0
Решение – Интерактивный режим
>>> float(8.0)
8.0
– булевы значения (т.к. они относятся, в том числе, к типу int)
Решение – Интерактивный режим
>>> float(False)
0.0
Решение – Интерактивный режим
>>> float(‘4.6’)
4.6
– экземпляры классов Decimal и Fraction
Решение – IDE
from decimal import Decimal
from fractions import Fraction
print(float(Decimal(10.4)))
print(float(Fraction(5, 2)))
Результаты:
10.4
2.5
7*. В чем суть банковского округления в Python (стандарт IEEE 754)?
В языке Python используется так называемое банковское округление (на основании стандарта IEEE 754). Его суть в следующем: округление числа производится в сторону ближайшего значения, а если последняя цифра 5, то в сторону ближайшего четного числа.
Что функция int(), что метод trunc() из модуля math дают одинаковые результаты. По факту, это аналоги, реализующие следующий механизм: отсекается дробная часть и возвращается целочисленное значение с сохранением знака.
Какие бы аргументы мы сюда не передавали, итог вычисления всегда будет одинаковым.
Решение – IDE
from math import trunc
print(int(5.2))
print(trunc(5.2))
print(int(5.2) == trunc(5.2))
Результаты:
5
5
True
Что выведет float 21 2
0.000003190524921592 (FN)
0.07001511752605438 (MW)
0.1500047892332077 (FT)
0.38000398874282837 (WW)
0.45001640915870667 (BS)
0.999925971031189 (BS)
Числа в Python
Когда мы хотим представить информацию о количестве деревьев в саду, остатке на банковской карте или же провести какие-либо математические расчеты, мы используем числа. В Python числа представлены сразу несколькими родственными категориями типов. Сюда относятся целые ( int ), вещественные ( float ) и комплексные ( complex ) числа. Если возможностей базовых типов не хватает, всегда можно воспользоваться числовыми расширениями в виде модулей decimal или fractions. Все перечисленные числовые типы довольно легко преобразуются между собой, а математические операции с ними интуитивно понятны и производятся по общепринятым правилам математики.
Целые числа
По умолчанию для записи литералов целых чисел в Python используется десятичная система счисления, однако разрешается записывать их в двоичной, восьмеричной и шестнадцатеричной системах счисления. При этом следует помнить, что вне зависимости от используемого формата представления, результат все равно будет отображаться в десятичном виде (см. пример №1 ).
Пример №1. Литералы целых чисел в Python.
Как видно из примера №1 , синтаксис записи целых чисел в разрешенных системах счисления должен иметь следующий вид.
- В десятичной системе счисления целые числа записываются в виде привычной нам последовательности цифр от нуля до десяти, например, 0 , -33 , +127 .
- В двоичной системе счисления запись целого числа должна начинаться с нуля и латинской буквы B в верхнем или нижнем регистре (т.е. 0b или 0B ), после чего должна идти последовательность нулей и единиц. Например, 0b01101 , 0B1011 , -0b1001 . Если будут использованы другие цифры, интерпретатор сгенерирует сообщение об ошибке.
- В восьмеричной системе счисления целое число должно начинаться с нуля и латинской буквы O в верхнем или нижнем регистре (т.е. 0o или 0O ), после чего должна идти последовательность цифр от нуля до семи ( 01234567 ). Например, 0o77 , 0O35 , -0O174 . Если будут использованы цифры не входящие в данный диапазон, интерпретатор сгенерирует сообщение об ошибке.
- В шестнадцатеричной системе счисления запись целого числа должна начинаться с нуля и латинской буквы X в верхнем или нижнем регистре (т.е. 0x или 0X ), после чего должны идти символы последовательности 0123456789ABCDEF , представляющих данную систему счисления. При этом буквы также разрешается использовать в любом регистре. Например, 0x7F , 0XDD5A , -0xfF9 . Если будут использованы символы не входящие в данный диапазон, интерпретатор сгенерирует сообщение об ошибке.
Кроме того, во всех случаях разрешается использовать перед числом знаки плюса ( + ) и минуса ( — ). Также можно переводить целые числа из одной системы счисления в другую. Для этого следует использовать следующие встроенные функции.
- bin(int_num) – преобразует целое число в двоичную систему счисления, возвращая строку с представлением литерала числа в этой системе.
- oct(int_num) – преобразует целое число в восьмеричную систему счисления, возвращая строку с представлением литерала числа в этой системе.
- hex(int_num) – преобразует целое число в шестнадцатеричную систему счисления, возвращая строку с представлением литерала числа в этой системе.
- int(obj) – конструктор типа int может использоваться как с одним, так и с двумя аргументами. Когда указан только один аргумент, функция пытается преобразовать объект в целое число в десятичной системе счисления, а в случае невозможности такого преобразования возбуждает соответствующее исключение. При этом, если объект obj является вещественным числом, оно преобразуется в целое путем усечения дробной части, например, int(21.302) вернет 21 .
- int(num_str, base=10) – если указано два аргумента, то первый аргумент должен быть строковым представлением целого числа в системе счисления с указанным основанием base . Разрешается использовать системы счисления с основаниями от 2 до 36 . При этом префикс 0b , 0o или 0x у строкового представления числа можно опускать. Если же точно известно, что в строковом представлении числа присутствует соответствующий префикс, второму аргументу разрешается передавать ноль, т.к. интерпретатор сможет определить систему счисления переводимого числа по указанному префиксу. В случае невозможности преобразования строки в целое число функция возбуждает соответствующее исключение (см. пример №2 ).
Пример №2. Перевод целых чисел между СС.
Итак, функции bin(int_num) , oct(int_num) и hex(int_num) принимают целые числа и возвращают строковые представления этих чисел в соответствующих системах счисления. Если же требуется обратное преобразование в десятичную систему, то следует передавать числа функции int(num_str, base=10) , предварительно приведя число к строке ( num_str = str(num) ) и указав основание системы счисления переводимого числа.
Подробнее о переводе чисел из одной системы счисления в другую можно почитать на нашей страничке здесь. Там же представлен калькулятор для перевода чисел из одной системы счисления в другую, а также ряд задач на закрепление теории.
Далее. Целые числа в Питоне отличаются тем, что их размер в принципе ограничивается лишь доступной памятью. Т.е. они поддерживают длинную арифметику и могут быть сколь угодно большими (см. пример №3 ). При этом следует помнить, что при обработке очень больших чисел приходится жертвовать скоростью их обработки.
Пример №3. Пример очень большого целого числа.
Также следует отметить, что в отличие от других типов чисел над целыми числами в Python можно производить битовые операции, перечень которых представлен для ознакомления в порядке возрастания их приоритета в таблице №4 . Подробнее данный вопрос мы рассмотрим позже, когда будем рассматривать битовые операции.
Таблица №4. Список битовых операций, применимых к целым числам.
Также у типа int имеется несколько дополнительных методов:
- int.bit_length() – метод возвращает количество бит, которое необходимо для представления числа в памяти в двоичном виде, без учета знака и незначащих (лидирующих) нулей;
- int.to_bytes(length, byteorder, *, signed=False) – метод возвращает массив байтов, который соответствует данному числу;
- int.from_bytes(bytes, byteorder, *, signed=False) – метод класса, который возвращает целое число, соответствующее указанному массиву байтов.
Как видим, все дополнительные методы тоже касаются битов и байтов, поэтому мы их сейчас рассматривать не будем.
Вещественные числа
Что касается записи литералов вещественных чисел (по-другому чисел с плавающей точкой), то в Python для них используется привычный нам математический синтаксис: сперва записывается знак числа (плюс можно не указывать), затем последовательность цифр целой части, разделительная точка и последовательность цифр дробной части. Например, 0.33 , .33 , 0.0 , .0 , -123.45 , -.123 . Также допустима запись вещественных чисел в экспоненциальной форме. При этом для представления степени 10 n используется латинская буква E в верхнем или нижнем регистре (т.е. e или E ), следующий за ней необязательный знак плюс или обязательный знак минус, а также сам показатель степени в виде положительного целого числа или нуля (см. пример №5 ).
Пример №5. Литералы вещественных чисел в Python.
В отличие от целых чисел вещественные не поддерживают длинную арифметику. В обычных условиях их точность ограничивается максимум 17 значащими цифрами. Поэтому для очень маленьких и очень больших вещественных чисел получаются весьма неточные результаты (см. пример №6 ). Если же в расчетах необходима высокая точность представления вещественных чисел, следует использовать, например, модуль decimal , позволяющий задавать требуемую точность результата (опять же, за счет снижения скорости обработки таких чисел).
Пример №6. Точность представления вещественных чисел в Python.
Неточное представление вещественных чисел в Python , впрочем как и в других языках программирования, может иногда приводить к неожиданным результатам (см. пример №7 ). Это связано с тем, что мы в основном привыкли вести расчеты в десятичной системе счисления, а компьютер перед вычислениями преобразует все числа в двоичную систему. Но не все вещественные числа могут быть точно преобразованы из десятичной системы счисления в двоичную, что и приводит к некоторой потере точности при хранении десятичных чисел в памяти компьютера.
Пример №7. Побочные эффекты сложения вещественных чисел в Python.
Как видим, на первый взгляд результат действительно получился неожиданным (не забываем про кнопку «Результат» ). Однако все верно. Ведь в данном случае при переводе десятичных чисел в двоичную систему компьютер не смог перевести их точно, округлив результат так, чтобы получившееся двоичное число поместилось в пределах выделяемого для хранения вещественного числа объема памяти. В результате у нас получилось число несколько меньшее, чем 0.8 . Но, повторимся, это не проблема Python , это проблема математики и невозможности во всех случаях осуществить точный перевод чисел из одной системы счисления в другую.
Не стоит сравнивать вещественные числа, доверяя их точности до последнего знака.
В заключение данного пункта перечислим дополнительные методы типа float .
- float.as_integer_ratio() – метод возвращает кортеж из пары целых чисел, которые представляют собой числитель и знаменатель наименьшей дроби, равной данному вещественному числу. Если вещественное число отрицательное, то первое число в возвращаемом кортеже будет со знаком минус. При этом второе число всегда положительное (см. пример №8 ).
- float.is_integer() – метод возвращает True , если дробная часть вещественного числа равна нулю.
- float.hex() – метод возвращает строковое представление числа с плавающей точкой в шестнадцатеричной системе счисления. При этом следует иметь в виду, что экспонента в таком представлении обозначается буквой p , т.к. буква e представляет собой допустимый символ в 16 -ной системе счисления.
- float.fromhex(float_num_str) – этот метод класса осуществляет преобразование строкового представления вещественного числа float_num_str в шестнадцатеричной системе счисления обратно в вещественное число в десятичной системе счисления.
Пример №8. Дополнительные методы типа float.
Комплексные числа
Комплексные числа в Python представлены типом complex , который хранит два вещественных числа: первое представляет собой действительную часть комплексного числа, а второе – мнимую. Как и принято в математике, действительная и мнимая части литерала комплексного числа объединяются знаком плюс или минус (см. пример №9 ). Однако за мнимой частью в Python следует символ j , как это принято в инженерии (в математике мнимая единица обозначается через i ).
Пример №9. Литералы комплексных чисел в Python.
Как видно из примера, если действительная часть комплексного числа равна 0 , ее можно опустить. Кроме того, отдельные части комплексного числа доступны в виде атрибутов real и imag , а получить сопряженное данному комплексное число можно изменив знак мнимой части при помощи метода complex.conjugate() .
Вполне возможно, что сталкиваться с комплексными числами вам придется довольно редко, однако иметь представление о наличии такого типа все-таки не помешает.
Математические операции в Python
Все известные нам со школы математические операции полностью поддерживаются в Python . Перечислим их и рассмотрим примеры.
- x + y – сложение двух чисел.
- x — y – вычитание двух чисел.
- x * y – умножение двух чисел.
- x / y – деление двух чисел. При делении вещественных и целых чисел (даже без остатка) результат всегда является вещественным числом. Если же в делении присутствуют комплексные числа, интерпретатор возвращает комплексный результат (см. пример №10 ).
- x // y – деление с округлением вниз. Например, 1//2 = 0 , -1//2 = -1 , 1//-2 = -1 , -1//-2 = 0 .
- x % y – операция возвращает остаток от деления целых и вещественных чисел. Если оба числа целые, то возвращается целочисленный остаток (тип int ). Например, 5%3 = 2 , -5%3 = 1 (т.к. -5 = 3*(-2) + 1 ), 6%3 = 0 . Если хотя бы одно число вещественное, остаток от деления также возвращается в вещественном виде (тип float ), при чем из-за неточности представления вещественных чисел результат может оказаться неточным. Например, 5.2%3 = 2.2 (все в порядке), -5.2%3 = 0.7999999999999998 (получили неточный результат, хотя ожидали 0.8 , т.к. -5.2 = 3*(-2) + 0.8 ). Что касается комплексных чисел, то для них операция неприменима.
- +x – операция используется в основном для повышения удобочитаемости кода, т.к. по факту она ничего не делает.
- -x – инверсия знака числа отличного от нуля, т.е. изменение плюса на минус или наоборот. Для нуля ничего не происходит.
- x ** y – возведение в степень. Отметим, что 0**0 = 1 .
Пример №10. Базовые математические операции в Python.
Что касается математических выражений, в которых используются сразу несколько операций, то для их группировки предназначены круглые скобки (см. пример №11 ). При этом внутри скобок приоритет отдается сперва возведению в степень ( x**y ), затем равносильно умножению ( x*y ), делению ( x/y ), делению с округлением вниз ( x//y ) и получению остатка от деления ( x%y ); в конце равнозначно выполняются сложение ( x + y ) и вычитание ( x — y ). Более подробно о приоритете операторов в Python (и не только математических) мы поговорим позже.
Если в выражении встречаются подряд идущие операторы с одинаковым приоритетом, то по умолчанию интерпретатор обрабатывает их слева направо. Однако, во избежание ошибок, старайтесь всегда использовать скобки, непосредственно определяя порядок группировки и не полагаясь на приоритет или ассоциативность операторов.
Пример №11. Математические выражения в Python.
Обратите внимание, что при наличии в одном выражении целых и вещественных чисел, интерпретатор при необходимости осуществляет приведение целых чисел к типу float , т.е. к более объемлющему типу (в математике целые числа являются подмножеством вещественных, а те в свою очередь принадлежат множеству комплексных чисел).
В дополнение к математическим операторам Python предоставляет и ряд полезных встроенных функций, позволяющих, например, найти модуль числа или округлить его (см. пример №12 ). Опять же, перечислим их и приведем краткое описание.
- divmod(x, y) – возвращает кортеж (x//y, x%y) . При этом для комплексных чисел она не предназначена.
- pow(x, y) – возвращает x в степени y (аналог x ** y ).
- abs(x) – возвращает абсолютное значение (модуль) числа x .
- round(x, n) – округляет число до n -го разряда после запятой. Если аргумент n не указан, то число округляется до ближайшего целого.
- max(num_1, num_2, . num_n) – возвращает максимальное из двух и более чисел.
- min(num_1, num_2, . num_n) – возвращает минимальное из двух и более чисел.
- sum(num_1, num_2, . num_n) – возвращает сумму двух и более чисел.
- int(x) – приведение числа к типу int .
- float(x) – приведение числа к типу float .
- complex(re, im=0) – приведение к типу complex . Аргумент im (мнимая часть будущего комплексного числа) можно не передавать, тогда он по умолчанию будет равен нулю.
Пример №12. Встроенные математические функции в Python.
Конечно же данный перечень встроенных функций довольно мал, но не стоит забывать про математические расширения и даже целые библиотеки, которые вполне смогут удовлетворить даже самых привередливых математиков. В частности, в следующем пункте мы познакомимся с модулем math стандартной библиотеки Python . Но сперва давайте закончим рассмотрение оставшейся части операторов, использующихся с числами.
Как вы наверняка помните, помимо основных операций вроде сложения и умножения, использующихся в обычных выражениях или уравнениях, в математике широко используются и операции сравнения чисел (например, в неравенствах). Перечислим их.
- x < y – выражение вернет True , если x строго меньше y .
- x > y – выражение вернет True , если x строго больше y .
- x <= y – выражение вернет True , если x меньше или равно y .
- x >= y – выражение вернет True , если x больше или равно y .
- x == y – выражение вернет True , если x равно y (в программировании один знак равно означает присвоить , поэтому для обозначения равенства используются два знака равно).
- x != y – выражение вернет True , если x не равно y .
Использование операторов сравнения показано в примере №13 .
Пример №13. Операторы сравнения чисел в Python.
В дополнение к базовому оператору присваивания = в Python применяется целый ряд комбинированных операторов присваивания, которые объединяют операцию присваивания с другой операцией. В общем случае выражение присваивания с комбинированным оператором x op= y можно считать сокращенной записью выражения x = x op y , где вместо op следует подставлять один из арифметических операторов. Например, x += y является сокращенной записью выражения x = x + y , в котором к значению переменной x прибавляется значение переменной y , после чего результат присваивается переменной x . Использование некоторых комбинированных операторов присваивания показано в примере №14 .
Пример №14. Комбинированные операторы присваивания в Python.
Конечно же мы перечислили не все комбинированные операторы присваивания, имеющиеся в Python . Весь перечень будет представлен в таблице в следующей главе, посвященной выражениям и операторам. Сейчас же мы рассмотрим ряд математических функций модуля math , поставляющегося в комплекте стандартной библиотеки Python .
Модуль math
Модуль math обеспечивает доступ к широкому кругу математических функций и констант, которые определены стандартом языка C . При этом модуль не поддерживает операции с комплексными числами, хотя они и определены стандартом языка Python . Но это не является большой проблемой, т.к. для операций с ними нужно просто воспользоваться модулем сmath , в котором легко найти требуемые функции, но уже с поддержкой комплексных чисел. Также стоит помнить, что, если явно не указано иное, все возвращаемые функциями модуля значения являются числами с плавающей точкой.
Для начала давайте рассмотрим представленные в модуле math математические константы и функции проверки чисел на принадлежность к ним.
- math.pi – представляет значение числа π с точностью, которая зависит от конкретной платформы. Мы его помним, как число 3.14 .
- math.tau ( чит. тау) – представляет удвоенное значение числа π с точностью, которая зависит от конкретной платформы. Т. о. τ – это отношение радиуса окружности к ее длине. Приблизительно оно равно 6.28 .
- math.e – представляет значение числа e с точностью, которая зависит от конкретной платформы. Мы его помним, как число 2.72 .
- math.inf (от англ. infinity) – представляет положительную бесконечность. Отметим, что значение является типом float и может быть получено вызовом конструктора float(‘inf’) . Для получения отрицательной бесконечности следует использовать унарный оператор — (см. пример №15 ). К значению inf в математике приводит, например, операция деления на 0 .
- math.nan (от англ. not a number) – представляет значение не число , которое также является типом float и может быть получено вызовом конструктора float(‘nan’) . К значению nan приводят недопустимые математические операции (например, извлечение корня четной степени из отрицательного числа на множестве вещественных чисел) или выход за допустимый диапазон значений для данной математической функции.
- math.isinf(x) – возвращает True , если x является отрицательной или положительной бесконечностью, или False в противном случае.
- math.isnan(x) – возвращает True , если x является значением nan , или False в противном случае.
- math.isfinite(x) – возвращает False , только если x является nan , inf или -inf . Во всех остальных случаях функция возвращает True .
Пример №15. Математические константы модуля math.
Что касается конкретно функций модуля math , то в нем представлены различные функции теории чисел, степенные, логарифмические, тригонометрические, гиперболические и ряд других функций. Все мы их здесь рассматривать не будем, однако краткое описание некоторых из них приведем в списке ниже. Остальные в случае необходимости всегда можно с легкостью найти в разделе «Numeric and Mathematical Modules» справочника стандартной библиотеки в описании модуля math (или cmath для комплексных чисел). Итак.
- math.ceil(x) – возвращает x , округленное вверх до ближайшего целого (см. пример №16 ).
- math.floor(x) – возвращает x , округленное вниз до ближайшего целого.
- math.trunc(x) – возвращает целую часть x , полностью отсекая дробную.
Пример №16. Функции модуля math (часть 1).
- math.factorial(x) – возвращает факториал неотрицательного целого числа x . Отметим, что факториалы чисел 0 и 1 принимаются равными 1 .
- math.sqrt(x) – возвращает квадратный корень из неотрицательного числа x (см. пример №17 ).
- math.log(x[, base]) – возвращает логарифм числа x по основанию base . Аргумент base можно опустить, тогда функция вернет натуральный логарифм числа x .
Пример №17. Функции модуля math (часть 2).
- math.sin(x) – возвращает синус угла x , значение которого задано в радианах. Отметим, что для косинуса угла функция имеет вид math.cos(x) (см. пример №18 ).
- math.tan(x) – возвращает тангенс угла x , значение которого задано в радианах.
- math.asin(x) – возвращает арксинус числа x . Для аркосинуса нужно использовать функцию math.acos(x) , а для арктангенса – math.atan(x) .
- math.degrees(x) – преобразует угол x из радиан в градусы.
- math.radians(x) – преобразует угол x из градусов в радианы.
Пример №18. Функции модуля math (часть 3).
Обязательно откройте модуль math в справочнике стандартной библиотеки и хотя бы бегло просмотрите весь список доступных функций. Кстати, не забываем, что получить его можно и программно через dir(math) .
Модуль decimal
Как мы уже знаем, числа типа float имеют весьма ограниченную точность. И хотя во многих случаях это не существенно, бывают ситуации, когда требуется повышенная точность вычислений, пусть даже и в ущерб производительности. В таких случаях нам на помощь приходит модуль decimal , который реализует неизменяемый числовой тип Decimal , представляющий числа с фиксированной точностью (см. пример №19 ).
Пример №19. Создание чисел фиксированной точности.
Как видим, для создания числа фиксированной точности Decimal необходимо передать конструктору Decimal строку с представлением требуемого числа или использовать функцию str для приведения числа к строке. Если в выражении используются числа с различной точностью представления, то для результата интерпретатор выберет наилучшую точность представления.
В принципе, конструктору Decimal можно передавать не только строковые представления чисел, но и сами целые и вещественные числа, а также их кортежи. Однако преобразование вещественных чисел в числа фиксированной точности иногда может порождать большое число десятичных знаков.
Числа типа Decimal обычно используются в определенном контексте, который подразумевает использование установленной точности представления, методики округления (вверх, вниз и т.д.) и некоторых других настроек (см. пример №20 ).
Пример №20. Изменение точности представления чисел Decimal.
Все настройки среды для арифметических операций получаемые при помощи метода decimal.getcontext() доступны для изменения. Кроме того, для установки нового контекста можно использовать метод decimal.setcontext() . С этим и некоторыми другими методами для работы с контекстами вы можете ознакомиться в модуле decimal стандартной библиотеки (см. раздел «Numeric and Mathematical Modules» ).
Отметим, что все арифметические операции, включая комбинированные, применимы и к числам Decimal . Однако модуль math для них неприменим. В тоже время некоторые функции, присутствующие в модуле math , реализованы как методы типа Decimal (см. пример №21 ).
Пример №21. Операции с числами Decimal.
Модуль fractions
Еще одним числовым типом в Python является тип Fraction . Он реализует объекты рациональных чисел, которые явным образом хранят числитель и знаменатель рациональной дроби (см. пример №22 ), что опять же при необходимости помогает избежать недостатков вещественных чисел.
Пример №22. Создание рациональных чисел.
В примере мы создали объекты рациональных чисел, указав в конструкторе Fraction в первом выражении числитель и знаменатель, а во втором передав ему вещественное число и строковое представление вещественного числа. Кроме того, рациональную дробь можно создать и конвертацией типа Decimal . В любом случае после создания объекты рациональных чисел могут участвовать в арифметических операциях, как обычные числа (см. пример №23 ).
Пример №23. Операции с рациональными числами.
Познакомиться с рациональными числами и их методами более подробно вы можете в модуле fractions стандартной библиотеки Python (см. раздел «Numeric and Mathematical Modules» ).
Краткие итоги параграфа
- Числа в Python представлены тремя базовыми типами: целые числа int , числа с плавающей точкой float и комплексные числа complex . Также посредством стандартной библиотеки доступны два расширения в виде чисел с фиксированной точностью Decimal и рациональных чисел Fraction .
- Для записи литералов целых чисел используется десятичная система счисления, однако разрешается записывать их в двоичной (например, 0b101 ), восьмеричной (например, -0o175 ) и шестнадцатеричной (например, 0xF5A ) системах счисления. Для перевода целых чисел из одной системы счисления в другую предназначены встроенные функции bin(int_num) , oct(int_num) , hex(int_num) , int(num_str, base=10) .
- Целые числа поддерживают длинную арифметику и могут быть сколь угодно большими. При этом следует помнить, что при обработке очень больших чисел приходится жертвовать скоростью их обработки.
- Для записи литералов вещественных чисел используется привычный нам математический синтаксис, например, -2.57 , 0.0 , .28 (ноль перед точкой можно опускать). Также допустима запись вещественных чисел в экспоненциальной форме, например, 1.2345e2 (т.е. 1.2345*10 2 ) или -1.2345E-3 (т.е. -1.2345*10 -3 ).
- В обычных условиях точность вещественных чисел ограничивается максимум 17 значащими цифрами. Поэтому не стоит сравнивать их, доверяя точности до последнего знака.
- Комплексные числа в Python хранят два вещественных числа: первое представляет собой действительную часть комплексного числа, а второе – мнимую. Например, -0.4+7j , 2-1.45j или 3.1j (нулевую действительную часть можно опускать).
- В Python поддерживаются все известные нам со школы математические операции: сложение x + y , вычитание x — y , умножение x*y , деление x/y , деление с округлением вниз x//y , получение остатка от деления x%y , возведение в степень x**y . Стоит помнить, что некоторые операции не применимы к комплексным числам.
- В дополнение к базовому оператору присваивания = в Python применяется целый ряд комбинированных операторов присваивания, которые объединяют операцию присваивания с другой операцией. Например, += , -= , *= , /= , //= , %= , **= . Как видим, в общем случае выражение присваивания с комбинированным оператором x op= y можно считать сокращенной записью выражения x = x op y , где вместо op следует подставлять один из арифметических операторов.
- Для сравнения чисел используются операторы < , > , <= , >= , == (равно), != (не равно).
- Помимо стандартных арифметических операций имеется и ряд полезных встроенных функций для работы с числами: divmod(x, y) – возвращает кортеж (x//y, x%y) , pow(x, y) – возвращает x в степени y , abs(x) – возвращает модуль числа x , round(x, n) – округляет число до n -го разряда после запятой, max(num_1, num_2, . num_n) , min(num_1, num_2, . num_n) и sum(num_1, num_2, . num_n) – соответственно возвращают максимальное, минимальное и сумму из двух и более чисел.
- Если встроенных функций недостаточно, можно воспользоваться расширением math стандартной библиотеки, которое обеспечивает доступ к широкому кругу математических функций и констант, которые определены стандартом языка C . Для комплексных чисел следует использовать модуль cmath .
- Для случаев, когда требуется повышенная точность вычислений, в Python предусмотрены дополнительные типы числовых объектов: числа с фиксированной точностью Decimal , которые позволяют устанавливать требуемую точность вычислений, способ округления и некоторые другие параметры, а также рациональные числа Fraction , которые явным образом хранят числитель и знаменатель рациональной дроби. Оба модуля доступны в стандартной библиотеке Python в разделе «Numeric and Mathematical Modules» .
Вопросы и задания для самоконтроля
1. Какие из представленых литералов чисел относятся к типу int , а какие к типу float : 3.4 , 0 , 5.0 , 0.0 , 123+4.1j , 795 ? Проверьте типы программно. Показать решение.
Ответ. Тип int : 0 и 795 . Тип float : 3.4 , 5.0 , 0.0 .
2. Определите системы счисления целых чисел 0x1101 , 0b1101 , 0o755 , 0X755 и 755 , записанные в исходном коде программы. Показать решение.
Ответ. Двоичная СС: 0b1101 ; восьмеричная СС: 0o755 ; десятичная СС: 755 , шестнадцатеричная СС: 0x1101 и 0X755 .
3. Какая операция над целыми числами никогда не дает в результате целое число? Показать решение.
Ответ. Результат деления целых чисел всегда имеет тип float , даже при делении без остатка. Например, 15/5 вернет 3.0 , а не 3 .
4. Как можно получить действительную и мнимую части комплексного числа? А как получить число, сопряженное к данному комплексному числу? Показать решение.
Ответ. Отдельные части комплексного числа доступны в виде атрибутов real и imag , а получить сопряженное данному комплексное число можно изменив знак мнимой части при помощи метода complex.conjugate() .
5. Вычислите устно и программно значения выражений 2*(3 + 4) и 2*3 + 4 . Объясните результаты. Показать решение.
Ответ. В Python как и в математике первыми выполняются действия в скобках, поэтому 2*(3 + 4) = 14 . Если скобки отсутствуют, то первыми выполняются операции с более высоким приоритетом. А так как приоритет умножения выше приоритета сложения, то 2*3 + 4 = 10 .
6. Каким будет тип результата вычисления выражения 7 — 3 + 2.0 и почему? Проверьте свой ответ программно. Показать решение.
Ответ. Результатом будет вещественное число, т.к. интерпретатор всегда преобразует числа к более сложному числовому типу, который встречается в выражении.
7. Каким будет остаток от деления числа 63 на число 5 ? Проверьте свой ответ программно. Показать решение.
8. Как получить доступ к функциям модуля math ? Показать решение.
Ответ. Нужно импортировать его при помощи инструкции import math .
9. Исправьте в коде пять ошибок (по одной в каждом блоке). Показать решение.
10. Дополнительные упражнения и задачи по теме расположены в разделе «Числа и математические выражения» нашего сборника задач и упражнений по языку программирования Python .