C语言中浮点数数据类型的范围是什么?

39

我正在阅读一本关于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

我不知道“最小正数”和“最大值”这两列中的数字从哪里来。


4
它们来自浮点类型的范围。 - Kendall Frey
14
正确但无用的答案是“IEEE 754”。 - Jerry Coffin
2
你的意思是为什么限制那些值? - SirGuy
C标准是否需要IEEE 754? - foo
5
@foo,IEEE 754 格式很常见,有些书籍容易混淆实现特性和语言要求,并且有些人容易忽略书中明确指出的展示一种或多种常见实现特性而非语言所必须的内容。请问需要翻译哪种语言? - AProgrammer
显示剩余4条评论
6个回答

32

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

1
在这里,我解释了为什么奇怪的“-126”到“127”范围只包含253个数字而不是预期的255个数字(其中一个是无穷大,另一个是子规范):https://dev59.com/Z2kw5IYBdhLWcg3wJHKF#53204544 - Ciro Santilli OurBigBook.com

23

这些数字来自于IEEE-754标准,该标准定义了浮点数的标准表示方法。通过链接中的维基百科文章解释如何知道符号、尾数和指数所使用的位数,并计算出这些范围。


9
浮点数据类型的值总共有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标准,用于表示浮点数,以上就是这些限制产生的方式。

1
值得一提的是,这是最小的正常数,还有更小的次正常数:https://dev59.com/Z2kw5IYBdhLWcg3wJHKF#53204544 - Ciro Santilli OurBigBook.com

4

无穷大、NaN和次正规数

这些是其他答案中尚未提到的重要注意事项。

首先阅读IEEE 754和次正规数的介绍:什么是次正规浮点数?

然后,对于单精度浮点数(32位):

  • IEEE 754 says that if the exponent is all ones (0xFF == 255), then it represents either NaN or Infinity.

    This is why the largest non-infinite number has exponent 0xFE == 254 and not 0xFF.

    Then with the bias, it becomes:

    254 - 127 == 127
    
  • FLT_MIN is the smallest normal number. But there are smaller subnormal ones! Those take up the -127 exponent slot.

以下程序在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

2
如dasblinkenlight所回答的那样,这些数字来自于IEEE-754浮点数表示方式,而Andreas则有一份很好的数学分解说明。
但请注意,浮点数的精度并不像表格中所示的那样仅有6或15位有效十进制数字,因为IEEE-754数字的精度取决于二进制的有效数字位数。
- float具有24个有效二进制数字 - 根据所表示的数字而定,可转换为6-8位十进制数字的精度。 - double具有53个有效二进制数字,约为15个十进制数字。
如果您感兴趣,我的另一个回答中还有进一步的解释。

1

这是类型指数部分大小的结果,例如在IEEE 754中。您可以使用float.h中的FLT_MAX、FLT_MIN、DBL_MAX和DBL_MIN来检查大小。


这些宏是C标准宏吗?我为什么要问这个问题是因为limits.h中有INT_MAX和INT_MIN。 - finalsemester.co.in
这些似乎在limits.h中不可用。 - MightyPork

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接