Это кросс-пост из моего блога.
Эта статья представляет собой просто упрощение стандарта IEEE 754. Здесь мы увидим, как числа с плавающей запятой не хранятся в памяти, исключения/округления с плавающей запятой и т. д. Но если вы хотите найти более авторитетные источники, перейдите на
- Что каждый программист должен знать об арифметике с плавающей запятой
- https://en.wikipedia.org/wiki/IEEE_754-1985
- https://en.wikipedia.org/wiki/Плавающая_точка.
Числа с плавающей запятой, хранящиеся путем кодирования мантиссы и показателя степени (вместе со знаковым битом)
- Строка выше содержит 2–3 абстрактных термина, и я думаю, что вы не сможете понять приведенную выше строку, пока не прочитаете дальше.
Структура памяти чисел с плавающей запятой
+-+--------+-----------------------+ | | | | +-+--------+-----------------------+ ^ ^ ^ | | | | | +-- significand(width- 23 bit) | | | +------------------- exponent(width- 8 bit) | +------------------------ sign bit(width- 1 bit)
Типичная 32-битная схема памяти с плавающей запятой одинарной точности имеет следующие поля:
- подписать
- экспонента
- мантисса (также известная как мантисса)
Подписать
- Старший бит указывает на знак.
0
указывает на положительное значение,1
указывает на отрицательное.
Экспонента
- Следующие 8 бит используются для экспоненты, которая может быть положительной или отрицательной, но вместо того, чтобы резервировать еще один знаковый бит, они кодируются таким образом, что
1000 0000
представляет0
, поэтому0000 0000
представляет-128
, а1111 1111
представляет127
. - Как работает эта кодировка? перейти к смещению экспоненты или увидеть его в следующем пункте практически.
значимость
- Остальные 23 бита используются для мантиссы (она же мантисса). Каждый бит представляет собой отрицательную степень 2 отсчетов слева, поэтому:
01101 = 0 * 2^-1 + 1 * 2^-2 + 1 * 2^-3 + 0 * 2^-4 + 1 * 2^-5 = 0.25 + 0.125 + 0.03125 = 0.40625
В ПОРЯДКЕ! Мы закончили с основами.
Разберемся практически
- Итак, мы рассматриваем очень известный пример значения с плавающей запятой
3.14
(PI). - Знак: здесь ноль, так как PI положительный!
Вычисление экспоненты
3
легко:0011
в двоичном формате- Остальные,
0.14
0.14 x 2 = 0.28, 0 0.28 x 2 = 0.56, 00 0.56 x 2 = 1.12, 001 0.12 x 2 = 0.24, 0010 0.24 x 2 = 0.48, 00100 0.48 x 2 = 0.96, 001000 0.96 x 2 = 1.92, 0010001 0.92 x 2 = 1.84, 00100011 0.84 x 2 = 1.68, 001000111 And so on . . .
- Итак,
0.14 = 001000111...
Если вы не знаете, как преобразовать десятичное число в двоичное, обратитесь к этому плавающему в двоичное. - Добавить
3
,11.001000111... with exp 0 (3.14 * 2^0)
- Теперь сдвиньте его (нормализуйте) и соответствующим образом отрегулируйте показатель степени
1.1001000111... with exp +1 (1.57 * 2^1)
- Теперь вам нужно только добавить смещение
127
к показателю степени1
и сохранить его (т.е.128
=1000 0000
)0 1000 0000 1100 1000 111...
- Забудьте верхние
1
мантиссы (которая всегда должна быть1
, за исключением некоторых специальных значений, поэтому она не сохраняется), и вы получите:0 1000 0000 1001 0001 111...
- Таким образом, наше значение
3.14
будет представлено примерно так:
0 10000000 10010001111010111000011 ^ ^ ^ | | | | | +--- significand = 0.7853975 | | | +------------------- exponent = 1 | +------------------------- sign = 0 (positive)
- Количество битов в показателе степени определяет диапазон (минимальное и максимальное значения, которые вы можете представить).
Подводя итог
- Если вы сложите все биты в мантиссе, они не составят в сумме
0.7853975
(что должно быть в соответствии с точностью до 7 цифр). Они выходят на0.78539747
. - Недостаточно битов для точного хранения значения. мы можем хранить только приближение.
- Количество битов в мантиссе определяет точность.
- 23 бита дают нам примерно 6 десятичных цифр точности. 64-битные типы с плавающей запятой дают примерно от 12 до 15 цифр точности.
Странно! Но факт
- Некоторые значения не могут быть представлены точно, независимо от того, сколько битов вы используете. Точно так же, как такие значения, как 1/3, не могут быть представлены конечным числом десятичных цифр, такие значения, как 1/10, не могут быть представлены конечным числом битов.
- Поскольку значения приблизительны, расчеты с ними также приблизительны, и накапливаются ошибки округления.
Посмотрим, как все работает
#include <stdio.h> #include <string.h> /* Print binary stored in plain 32 bit block */ void intToBinary(unsigned int n) { int c, k; for (c = 31; c >= 0; c--) { k = n >> c; if (k & 1) printf("1"); else printf("0"); } printf("\n"); } int main(void) { unsigned int m; float f = 3.14; /* See hex representation */ printf("f = %a\n", f); /* Copy memory representation of float to plain 32 bit block */ memcpy(&m, &f, sizeof (m)); intToBinary(m); return 0; }
- Этот C-код выведет на консоль бинарное представление числа с плавающей запятой.
f = 0x3.23d70cp+0 01000000010010001111010111000011
Где хранится десятичная точка?
- Десятичная точка нигде явно не хранится.
["Нажми сюда, чтобы прочитать больше . . . !»]