Это возможно (по крайней мере, для IEEE 754 float
и double
значений) вычислить наибольшее значение с плавающей запятой с помощью (псевдо-код):
~(-1.0) | 0.5
Прежде чем мы сможем сделать наш битовую вертел, мы» Мне нужно преобразовать значения с плавающей запятой в целые числа, а затем снова вернуться. Это можно сделать следующим образом:
uint64_t m_one, half;
double max;
*(double *)(void *)&m_one = -1.0;
*(double *)(void *)&half = 0.5;
*(uint64_t *)(void *)&max = ~m_one | half;
Как это работает? Для этого нам нужно знать, как будут кодироваться значения с плавающей запятой.
Самый старший бит кодирует знак, следующий бит k
кодирует показатель экспоненты, а младшие разряды будут содержать дробную часть. Для полномочий 2
дробная часть равна 0
.
Показатель будет сохранен с смещением (смещением) 2**(k-1) - 1
, что означает, что показатель 0
соответствует шаблону со всеми, но с самым высоким битом.
Есть два показателя битовые модели с особым значением:
- если не бит не установлен, то значение будет
0
, если дробная часть равна нулю; в противном случае значение является субнормальная
- , если все биты установлены, то значение либо
infinity
или NaN
Это означает, что наибольший регулярный показатель будет закодирован с помощью установки всех битов, кроме самого нижнего, что соответствует значение 2**k - 2
или 2**(k-1) - 1
, если вы вычтите смещение.
Для double
значения, k = 11
, т.е. самый высокий показатель будет 1023
, поэтому наибольшее значение с плавающей запятой порядка 2**1023
который составляет около 1E+308
.
Наибольшее значение будет иметь
- знаковый бит установлен в
0
- всех, но самый низкий показатель бит установлен в
1
- все дробные биты, установленные в
1
Теперь, можно понять, как работают наши магические числа:
-1.0
имеет свой знак установлен бит, показатель степени смещения - то есть все биты, но самый высокий в настоящее время - и дробная часть 0
~(-1.0)
имеет только самый высокий показатель степени бит и все дробные биты устанавливают
0.5
имеет знаковый бит и дробную часть 0
; показатель будет смещение минус 1
, то есть все, но высокий и самый низкий показатель степени бит будет присутствовать
Когда мы объединяем эти два значения через логическое или, мы получим битовую комбинацию мы хотели.
Вычисление работает для x86 80-битной расширенных значений точности (ака long double
), а также, но немного-вертел должен быть сделан побайтно, как нет целого типа достаточно велик, чтобы вместить значения на 32- бит.
Уклонение на самом деле не должно быть 2**(k-1) - 1
- он будет работать для произвольного уклона, если он нечетный. Смещение должно быть нечетным, поскольку в противном случае битовые шаблоны для показателя 1.0
и 0.5
будут отличаться в других местах, чем младший бит.
Если база b
(ака радикс) типа с плавающей точкой не 2
, вы должны использовать b**(-1)
вместо 0.5 = 2**(-1)
.
Если наибольшее значение экспоненты не является резервным, используйте 1.0
вместо 0.5
. Это будет работать независимо от базы или смещения (что означает, что она больше не ограничена нечетными значениями). Разница в использовании 1.0
заключается в том, что бит наименьшего экспоненты не будет очищен.
Резюмируя:
~(-1.0) | 0.5
работает до тех пор, как радикс является 2
, смещение нечетно и самый высокий показатель зарезервирован.
~(-1.0) | 1.0
работает для любого радиуса или смещения, пока не указан самый высокий показатель экспоненты.
Почему, по вашему мнению, это означает, что вы избегаете float.h? ну, я думаю, он хочет, чтобы вы использовали какую-то функцию вроде ldexp, FLT_RADIX (или как ее называли) и т. д. –
Хорошо, потому что float.h уже имеет доступные значения min/max, которые в этом случае выглядят как обман. Но да, есть некоторые другие константы, определенные в float.h, которые, вероятно, могут быть использованы для определения реальных диапазонов. – Ree