一些C浮点常量没有意义

问题描述

<float.h>Apple clang version 12.0.0 (clang-1200.0.32.2)中的常量似乎没有道理。

DBL_MIN_EXP-1021,而DBL_MAX_EXP1024。但是,这与wikipedia says,“指数范围从−1022到+1023,...”不匹配。

另外,DBL_MIN_EXPDBL_MIN不一致,而2.2250738585072014e-308等于2⁻¹⁰²²,有时写成0x1.0000000000000p-1022。因此,我们的指数小于最小值-1021

同样,DBL_MIN_10_EXP-307,考虑到DBL_MIN的指数为e-308,这是没有意义的。

在实际代码中使用时,DBL_MAX_EXP的双1024值溢出。例如,ldexp(1.0,1024)给出inf

这是我的C代码

#include <float.h>
#include <stdio.h>
#include <math.h>

#define SHOW_DOUBLE(s)   printf("%.17lg \t%s\n",s,#s);
#define SHOW_INT(s)      printf("%d \t%s\n",#s);

int
main()
{
    SHOW_DOUBLE(DBL_MAX);
    SHOW_DOUBLE(DBL_MIN);
    SHOW_DOUBLE(DBL_EPSILON);
    SHOW_INT(DBL_MAX_EXP);
    SHOW_INT(DBL_MAX_10_EXP);
    SHOW_INT(DBL_MIN_EXP);
    SHOW_INT(DBL_MIN_10_EXP);
    SHOW_INT(DBL_DIG);
    SHOW_INT(DBL_MANT_DIG);
    SHOW_INT(FLT_RADIX);
    SHOW_INT(FLT_ROUNDS);
    printf("%lf\n",ldexp(1.0,1024));
    return 0;
}

这是输出

1.7976931348623157e+308 DBL_MAX
2.2250738585072014e-308 DBL_MIN
2.2204460492503131e-16  DBL_EPSILON
1024                    DBL_MAX_EXP
308                     DBL_MAX_10_EXP
-1021                   DBL_MIN_EXP
-307                    DBL_MIN_10_EXP
15                      DBL_DIG
53                      DBL_MANT_DIG
2                       FLT_RADIX
1                       FLT_ROUNDS
inf

解决方法

一对一是规格的一部分。从 5.2.4.2.2浮动类型的特征,¶11

...

  • 最小负整数,以使FLT_RADIX升至小于幂为归一化浮点数emin
    • FLT_MIN_EXP
    • DBL_MIN_EXP
    • LDBL_MIN_EXP

...

  • 最大整数,以使FLT_RADIX提高到一个小于的幂,即幂是可表示的有限浮点数emax
    • FLT_MAX_EXP
    • DBL_MAX_EXP
    • LDBL_MAX_EXP

强调小于是我的

,

我问了自己同样的问题,并意识到这是因为 IEEE 754 和 C 使用两个不同的 normalized forms

  • IEEE 754 使用介于 1(含)和 2(不含)之间的有效数来表示普通数字

x = sign × significand × 2指数,编码为 S|E|F 其中 sign = (−1)S ; 有效数 =

  • 1.F if EminEEmax(正常数),
  • 0 如果 E = Emin − 1 且 F = 0(零),
  • 0.F if E = Emin − 1 and F ≠ 0(次正规数),
  • ∞ 如果 E = Emax + 1 且 F = 0(无穷大),
  • NaN if E = Emax + 1 and F ≠ 0(不是数字);

指数 = max(E,Emin) − 指数偏差 .

  • C 使用介于 0.5(含)和 1(不含)之间的significand'作为普通数字

x = sign' × significand' × 2指数',编码为 S|E|F 其中 sign' = (−1)S ; significand' =

  • 0.1F 如果 EminEE max(正常数),
  • 0 如果 E = Emin − 1 且 F = 0(零),
  • 0.0F 如果 E = Emin − 1 且 F ≠ 0(次正规数),
  • ∞ 如果 E = Emax + 1 且 F = 0(无穷大),
  • NaN if E = Emax + 1 and F ≠ 0(不是数字);

exponent′ = max(E + 1,Emin + 1) − exponent偏见

因为 x = sign × significand × 2指数 = sign' × significand' × 2exponent',IEEE 754规范化形式和C规范化形式之间的关系如下保持:

  • sign' = sign;
  • significand′ = significand/2;
  • 指数' = 指数 + 1.

特别是在 binary64 格式中,这就是为什么 exponentmin = -1022 在 IEEE 754 规范化形式和 exponent'min = -1021 以 C 规范化形式 (DBL_MIN_EXP) 和 exponentmax = 1023 以 IEEE 754 规范化形式和 exponent'max = 1024 以 C 规范化形式 (DBL_MAX_EXP)。

在 Python 中:

  • IEEE 754 规范化significand指数可以通过以下方式提取:
math.ldexp(x,-math.floor(math.log2(x))),math.floor(math.log2(x))
  • C 规范化的significand'exponent' 可以通过以下方式提取:
math.frexp(x)

例如:

>>> import math
>>> x = 12
>>> math.ldexp(x,math.floor(math.log2(x))
(1.5,3)
>>> math.frexp(12)
(0.75,4)