Вычисление диапазонов типов данных с плавающей запятой - PullRequest
5 голосов
/ 08 февраля 2009

Возможно ли вычислить диапазоны типов данных с плавающей запятой, двойных и длинных двойных переносимым способом без чтения файла float.h и использования ANSI C? Под переносным я имею в виду те случаи, когда целевой компьютер не соответствует стандарту IEEE 754.

Я читаю книгу K & R, и упражнение 2-1 просит меня «вычислить» их, так что я предполагаю, что это означает, что следует полностью избегать float.h, который включает FLT_MIN, FLT_MAX, DBL_MIN и DBL_MAX (чтение этих значений напрямую, конечно, не классифицирует как "вычислительный").

Ответы [ 4 ]

11 голосов
/ 09 февраля 2009

Возможно (по крайней мере для значений 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, т.е. будут присутствовать все, кроме старшего и младшего битов экспоненты

Когда мы объединяем эти два значения с помощью логического или, мы получим требуемый битовый шаблон.


Вычисление также работает для 80-битных значений повышенной точности x86 (также известных как long double), но битовое перемешивание должно выполняться побайтово, поскольку нет целочисленного типа, достаточно большого, чтобы хранить значения на 32-битном оборудовании .

На самом деле смещение не обязательно должно быть 2**(k-1) - 1 - оно будет работать при произвольном смещении, пока оно нечетное. Смещение должно быть нечетным, потому что в противном случае битовые комбинации для показателя степени 1.0 и 0.5 будут отличаться в других местах, чем младший бит.

Если базовое значение b (он же radix) типа с плавающей запятой не равно 2, вам нужно использовать b**(-1) вместо 0.5 = 2**(-1).

Если наибольшее значение показателя не зарезервировано, используйте 1.0 вместо 0.5. Это будет работать независимо от базы или смещения (то есть больше не ограничено нечетными значениями). Разница в использовании 1.0 заключается в том, что младший бит экспоненты не будет очищен.


Подведем итог:

~(-1.0) | 0.5

работает до тех пор, пока основание равно 2, смещение нечетное и максимальный показатель зарезервирован.

~(-1.0) | 1.0

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

4 голосов
/ 08 февраля 2009

Для 99,99% всех приложений вы должны принять IEEE 754 и использовать константы, определенные в <float.h>. В остальных 0,01% вы будете работать с очень специализированным оборудованием, и в этом случае вы должны знать, что использовать на основе оборудования.

3 голосов
/ 09 февраля 2009

С риском лишнего ответа:

Нет. Не существует портативного способа вычисления диапазонов. Вот почему предоставляется заголовок <float.h> - потому что нет переносимого способа получения информации, содержащейся в нем.

2 голосов
/ 08 февраля 2009

Вы можете попробовать увеличить поплавок, пока он не переполнится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...