Диапазоны типа данных с плавающей точкой в C?
Я читаю книгу на языке C, говоря о диапазонах с плавающей запятой, автор дал таблицу:
Type Smallest Positive Value Largest value Precision
==== ======================= ============= =========
float 1.17549 x 10^-38 3.40282 x 10^38 6 digits
double 2.22507 x 10^-308 1.79769 x 10^308 15 digits
Я не знаю, откуда взялись числа в столбцах "Наименьшее положительное" и "Наибольшее значение".
Ответы
Ответ 1
Эти цифры поступают из стандарта IEEE-754, который определяет стандартное представление чисел с плавающей запятой. Статья в Википедии по ссылке объясняет, как добраться до этих диапазонов, зная количество бит, используемых для знаков, мантиссы и экспонента.
Ответ 2
32-битное число с плавающей запятой содержит 23 + 1 бит мантиссы, и используется 8-разрядный показатель (от -126 до 127), поэтому наибольшее число, которое вы можете представить, это:
(1 + 1 / 2 + ... 1 / (2 ^ 23)) * (2 ^ 127) =
(2 ^ 23 + 2 ^ 23 + .... 1) * (2 ^ (127 - 23)) =
(2 ^ 24 - 1) * (2 ^ 104) ~= 3.4e38
Ответ 3
Значения для типа данных с плавающей точкой состоят из 32 битов, чтобы представить число, которое распределено следующим образом:
1 бит: бит знака
8 бит: показатель p
23 бит: мантисса
Показатель хранится как p + BIAS
, где BIAS равен 127, мантисса имеет 23 бита и 24-й скрытый бит, который предполагается 1. Этот скрытый бит является самым значимым битом (MSB) мантиссы, а экспоненту необходимо выбирается так, чтобы оно равно 1.
Это означает, что наименьшее число, которое вы можете представить, это 01000000000000000000000000000000
, которое равно 1x2^-126 = 1.17549435E-38
.
Наибольшее значение 011111111111111111111111111111111
, мантисса - 2 * (1 - 1/65536), а показатель - 127, который дает (1 - 1 / 65536) * 2 ^ 128 = 3.40277175E38
.
Те же принципы применяются к двойной точности, за исключением того, что бит:
1 бит: бит знака
11 бит: экспоненциальные биты
52 бит: бит мантиссы
BIAS: 1023
Таким образом, технически ограничения исходят из стандарта IEEE-754 для представления чисел с плавающей запятой, а приведенное выше - как эти ограничения возникают
Ответ 4
Как уже сообщал dasblinkenlight, цифры исходят из того, что числа с плавающей запятой представлены в IEEE-754, а у Андреаса хорошая разбивка математики.
Однако - будьте осторожны, что точность чисел с плавающей запятой не является точно 6 или 15 значащими десятичными цифрами, как предлагает таблица, поскольку точность номеров IEEE-754 зависит от количества значительных двоичных цифр.
-
float
имеет 24 значащих двоичных разряда, которые в зависимости от представленного числа преобразуются в 6-8 десятичных цифр точности.
-
double
имеет 53 значащих двоичных разряда, что составляет приблизительно 15 десятичных цифр.
Еще один мой ответ имеет дополнительное объяснение, если вы заинтересованы.
Ответ 5
Это следствие размера экспоненциальной части типа, например, в IEEE 754. Вы можете просмотреть размеры с FLT_MAX, FLT_MIN, DBL_MAX, DBL_MIN в float.h.
Ответ 6
Бесконечность, NaN и субнормалы
Это важные предостережения, о которых ни один другой ответ не упомянул.
Сначала прочтите это введение в IEEE 754 и субнормальные числа: Что такое субнормальное число с плавающей запятой?
Тогда для одинарной точности (32-разрядных):
-
IEEE 754 говорит, что если показатель степени равен единице (0xFF == 255
), то он представляет либо NaN, либо бесконечность.
Вот почему наибольшее бесконечное число имеет показатель степени 0xFE == 254
а не 0xFF
.
Затем с уклоном становится:
254 - 127 == 127
-
FLT_MIN
- это наименьшее нормальное число. Но есть меньшие субнормальные! Те занимают слот экспоненты -127
.
Все утверждения следующей программы проходят на Ubuntu 18.04 amd64:
#include <assert.h>
#include <float.h>
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
float float_from_bytes(
uint32_t sign,
uint32_t exponent,
uint32_t fraction
) {
uint32_t bytes;
bytes = 0;
bytes |= sign;
bytes <<= 8;
bytes |= exponent;
bytes <<= 23;
bytes |= fraction;
return *(float*)&bytes;
}
int main(void) {
/* All 1 exponent and non-0 fraction means NaN.
* There are of course many possible representations,
* and some have special semantics such as signalling vs not.
*/
assert(isnan(float_from_bytes(0, 0xFF, 1)));
assert(isnan(NAN));
printf("nan = %e\n", NAN);
/* All 1 exponent and 0 fraction means infinity. */
assert(INFINITY == float_from_bytes(0, 0xFF, 0));
assert(isinf(INFINITY));
printf("infinity = %e\n", INFINITY);
/* ANSI C defines FLT_MAX as the largest non-infinite number. */
assert(FLT_MAX == 0x1.FFFFFEp127f);
/* Not 0xFF because that is infinite. */
assert(FLT_MAX == float_from_bytes(0, 0xFE, 0x7FFFFF));
assert(!isinf(FLT_MAX));
assert(FLT_MAX < INFINITY);
printf("largest non infinite = %e\n", FLT_MAX);
/* ANSI C defines FLT_MIN as the smallest non-subnormal number. */
assert(FLT_MIN == 0x1.0p-126f);
assert(FLT_MIN == float_from_bytes(0, 1, 0));
assert(isnormal(FLT_MIN));
printf("smallest normal = %e\n", FLT_MIN);
/* The smallest non-zero subnormal number. */
float smallest_subnormal = float_from_bytes(0, 0, 1);
assert(smallest_subnormal == 0x0.000002p-126f);
assert(0.0f < smallest_subnormal);
assert(!isnormal(smallest_subnormal));
printf("smallest subnormal = %e\n", smallest_subnormal);
return EXIT_SUCCESS;
}
GitHub вверх по течению.
Скомпилируйте и запустите с:
gcc -ggdb3 -O0 -std=c11 -Wall -Wextra -Wpedantic -Werror -o subnormal.out subnormal.c
./subnormal.out
Выход:
nan = nan
infinity = inf
largest non infinite = 3.402823e+38
smallest normal = 1.175494e-38
smallest subnormal = 1.401298e-45