Получи случайную криптовалюту за регистрацию!

Введение Давайте напишем простейшую программу на питоне: pri | Holy Python

Введение

Давайте напишем простейшую программу на питоне:

print(0.1 + 0.2)

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

0.30000000000000004

И это проблема не питона, вы можете запустить такую же программу на js или c++. Вывод будет таким же.

Что же происходит на самом деле?

Давайте разбираться.

IEEE

Чем отличается целое число от дробного с точки зрения хранения?

Правильно в дробном числе есть точка. Точку не возможно представить с помощью нулей и единиц, поэтому нужна другая, специальная форма хранения такого числа. Данную задачу решил "Институт инженеров электроники и электротехники"(IEEE), он создал стандарт хранения дробных чисел - IEEE 754.

Как перевести дробное число из десятичной системы счисления в двоичную?

Перед тем как перейти к изучению стандарта IEEE 754 давайте рассмотрим как перевести дробное число из десятичной системы счисления в двоичную.

В качестве примера возьмём число 3.25. Чтобы перевести его в двоичную систему счисления, нужно перевести целую и дробную часть в двоичную систему поотдельности.

Начнём с целой части. Здесь все просто, мы делим число на 2 пока последнее частное не станет меньше двух. Последнее частное и все остатки записываем в обратном порядке.

Пример:

3 ÷ 2 = 1 (1)

3 в двоичной системе счисления - это 11

Перейдём к дробной части. Здесь вместо деления нам нужно умножение. Мы умножаем дробную часть на 2 пока не получим в ответе единицу. А затем просто склеиваем целые части ответов.

Пример:

0.25 * 2 = 0.5
0.5 * 2 = 1.0

0.25 в двоичной системе - это 01

IEEE 754

Итак, мы успешно перевели дробное число в двоичный вид: 11.01

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


(-1)^s*1.M*10^E

s - знак числа
m - мантиса(дробная часть числа)
e - это количество знаков на которое нужно сдвинуть точку, чтобы перед ней осталась только одна единица.

Чтобы сохранить число в такой форме стандарт предлагает 3 основных формата хранения:

1. Одинарный - 32 бит
2. Двойной - 64 бит
3. Четырехкратный - 128 бит

Чем больше размер тем выше точность хранимого числа.

В качестве простого примера рассмотрим 32 битный формат.

Первый бит - бит знака. Если он равен нулю то число положительное, если он равен единице то число отрицательное.

Следующие 8 бит - выделены для хранения степени. Чтобы определить знак степени, нам нужно прибавить 127 к значению степени. Если число будет больше 127, то степень положительная, иначе - отрицательная.

Оставшиеся 23 бита - выделены для хранения мантиссы. Если мантисса меньше 23 бит, оставшиеся пространство заполняют нулями.

Вы можете спросить, а где хранится целая часть и основание степени?

Ответ прост - целая часть всегда равна единице, а основание степени всегда равно десяти => для экономии памяти их можно не хранить!

Давайте сохраним наше число в памяти.

1. Сдвинем точку на 1 знак влево 1.101. e = 1
2. Первый бит знака равен нулю, так как число положительное
3. e + 127 = 128 => число положительное.
4. Переведём 128 в двоичную систему счисления.

128 ÷ 2 = 64 (0)
64 ÷ 2 = 32 (0)
32 ÷ 2 = 16 (0)
16 ÷ 2 = 8 (0)
8 ÷ 2 = 4 (0)
4 ÷ 2 = 2 (0)
2 ÷ 2 = 1 (0)

128 в двоичной системе счисления -
10000000
5. Запишем 10000000 в 8 бит предназначеные для степени.
6. Запишем 101 в 23 бита предназначеные для хранения дробной части. Оставшиеся биты заполним нулями.
7. Мы сохранили наше число в памяти!

Вот результат выполнения всех шагов:


0 10000000 10100000000000000000000


Отлично, теперь попробуем перевести сохранённое число обратно:

s = 0 (знак положительный)
m = 101
e = 1

-1^s * 1.m * 10^e = 11.01

Осталось перевести 11.01 в десятичную систему счисления и мы получим наше число - 3.25.

Проблема

Не всегда всё происходит настолько гладко. Некоторые дробные числа просто нельзя перевести в двоичную систему счисления.

Пример: