Это кросс-пост из моего блога.

Эта статья представляет собой просто упрощение стандарта IEEE 754. Здесь мы увидим, как числа с плавающей запятой не хранятся в памяти, исключения/округления с плавающей запятой и т. д. Но если вы хотите найти более авторитетные источники, перейдите на

  1. Что каждый программист должен знать об арифметике с плавающей запятой
  2. https://en.wikipedia.org/wiki/IEEE_754-1985
  3. https://en.wikipedia.org/wiki/Плавающая_точка.

Числа с плавающей запятой, хранящиеся путем кодирования мантиссы и показателя степени (вместе со знаковым битом)

  • Строка выше содержит 2–3 абстрактных термина, и я думаю, что вы не сможете понять приведенную выше строку, пока не прочитаете дальше.

Структура памяти чисел с плавающей запятой

+-+--------+-----------------------+
| |        |                       |
+-+--------+-----------------------+
 ^    ^                ^
 |    |                |
 |    |                +-- significand(width- 23 bit) 
 |    |
 |    +------------------- exponent(width- 8 bit) 
 |
 +------------------------ sign bit(width- 1 bit)

Типичная 32-битная схема памяти с плавающей запятой одинарной точности имеет следующие поля:

  1. подписать
  2. экспонента
  3. мантисса (также известная как мантисса)

Подписать

  • Старший бит указывает на знак.
  • 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

Где хранится десятичная точка?

  • Десятичная точка нигде явно не хранится.

["Нажми сюда, чтобы прочитать больше . . . !»]