Диапазоны типа данных с плавающей точкой в 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
Я не знаю, откуда взялись числа в столбцах "Наименьшее положительное" и "Наибольшее значение".
6 ответов
Эти числа взяты из стандарта IEEE-754, который определяет стандартное представление чисел с плавающей запятой. Статья в Википедии по ссылке объясняет, как достичь этих диапазонов, зная количество бит, используемых для знаков, мантиссы и показателя степени.
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
Значения для типа данных с плавающей запятой получены из общего числа 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 для представления чисел с плавающей запятой, и как это происходит
Бесконечность, 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;
}
Скомпилируйте и запустите с:
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
Как dasblinkenlight уже ответил, числа получены из того, как числа с плавающей запятой представлены в IEEE-754, и у Андреаса есть хорошая разбивка математики.
Однако следует помнить, что точность чисел с плавающей запятой точно не равна 6 или 15 значащим десятичным цифрам, как показано в таблице, поскольку точность чисел IEEE-754 зависит от количества значащих двоичных цифр.
float
имеет 24 значащих двоичных разряда - что в зависимости от представленного числа преобразуется в 6–8 десятичных знаков точности.double
имеет 53 значащих двоичных цифр, что составляет примерно 15 десятичных цифр.
Другой мой ответ имеет дальнейшее объяснение, если вам интересно.
Это является следствием размера показательной части типа, как, например, в IEEE 754. Вы можете проверить размеры с FLT_MAX, FLT_MIN, DBL_MAX, DBL_MIN в float.h.