frac указано что-то иное (не все нули), то это значение называется "not a number" (NaN). Оно может вылезти, если результат невозможно представить реальными числами (настоящие джедаи помнят про мнимые числа, это не тот случай), либо если происходит что-то странное в духе "бесконечность минус бесконечность".В качестве примера можно взять 8-битный формат, где на
exp отводится k=4 бит, а на frac даётся n=3 бит (понятно, что на знак в любом случае 1 бит). В этом случае значение bias B = 2 ** (4-1) - 1 = 7.Как мы представим ноль? Ну, очевидно вот так (биты разграничены по соответствующим полям):
0 0000 000
e=0, E = 1 - 7 = -6.f можно записать как 0 / 8 (тк в поле frac у нас все нули), M = f = 0 / 8. Выходит формула:((-1) ** 0) * (0/8) * (2 ** -6) = 0
Классно. Теперь число
7/8, то есть 0.875. Его представление:0 0110 110
e = 0110 = 6, тогда E = 6 - 7 = -1.Теперь
f. У нас три бита, 2 ** 3 = 8, а само значение frac = 110, то есть 6. Выходит, что f = 6/8, M = 14 / 8.Подставляем:
((-1) ** 0) * (14 / 8) * (2 ** -1) = 0.875
Попробуем ещё взять вот такое число, это в нашем случае самое большое из денормированных:
0 0000 111
Выходит, что
e = 0, E = -6. При этом f = M = 7/8. Подставим:((-1) ** 0) * (7/8) * (2 ** -6) ~ 0.013671875
А если самое маленькое из нормированных?
0 0001 000
Тут
e = 1, E = -6, f = 0 / 8, M = 8 / 8. Подставляем:((-1) ** 0) * (8/8) * (2 ** -6) ~ 0.015625
Аналогично, самое маленькое позитивное число, которое мы можем представить (грубо говоря, самое близкое к нулю):
0 0000 001
Тут
e = 0, E = -6, f = M = 1/8. В формуле:((-1) ** 0) * (1/8) * (2 ** -6) = 0.001953125
Вот это как раз случай с денормированными числами, когда мы представляем очень близкое к нулю значение.
Кстати, тут один интересный момент. Смотрите, что у нас вышло:
0 0000 000 = 0
0 0000 001 = 0.001953125
0 0000 111 = 0.013671875
0 0001 000 = 0.015625
0 0110 110 = 0.875
Я расположил эти числа по возрастанию от самого маленького десятичного к самому большому. Но при этом можно видеть, что их двоичные аналоги тоже стоят по возрастанию! Это вовсе не случайно: стандарт создавался с учётом того, что программистам наверняка потребуется сортировать такие числа по тем же принципам, что и для целых чисел (хотя тут есть небольшая проблема, если в первом бите появляется
1, то есть число отрицательное).Собственно говоря, из примера выше мы видим, что представить "любое" число мы с помощью такого стандарта не можем. К примеру, у нас идёт "перескок" от
0.013671875 сразу к 0.015625, и сделать с этим особенно ничего не получится. Да, можно использовать не 8 бит, а 32 или даже 64 (двойная точность), но, как вы понимаете, всё равно покрыть все возможные случаи никак не выйдет. Поэтому в том же Rust есть понятие "эпсилон" (мы его с вами видели на стриме), то есть определённая погрешность, которую стоит учитывать.