【问题标题】:Floating point limits (double) defined with long double suffix L用长双后缀 L 定义的浮点限制(双精度)
【发布时间】:2017-06-28 12:22:28
【问题描述】:

1. 问题:

我对使用 gcc v4.8.5 的 Linux 中的 DBL_MAXDBL_MIN 定义有疑问。
它们在limit.h 中定义为:

#define DBL_MAX     __DBL_MAX__
#define DBL_MIN     __DBL_MIN__

其中__DBL_MIN____DBL_MAX__ 是特定于编译器的,可以通过以下方式获得:

$ gcc -dM -E - < /dev/null
...
#define __DBL_MAX__ ((double)1.79769313486231570815e+308L)
#define __DBL_MIN__ ((double)2.22507385850720138309e-308L)
...

我的问题是:
为什么值定义为long double,后缀为L,然后又转换回double

2。 问题:

为什么__DBL_MIN_10_EXP__-307 定义,但最小指数是-308,因为它在上面的DBL_MIN 宏中使用?在最大指数的情况下,它是用308 定义的,我可以理解,因为它被DBL_MAX 宏使用。

#define __DBL_MAX_10_EXP__ 308
#define __DBL_MIN_10_EXP__ (-307)

不是问题的一部分,只是我的观察:

顺便说一下,在 Visual Studio 2015 中使用 Windows,只定义了 DBL_MAXDBL_MIN 宏,没有编译器特定的重定向到带有下划线的版本。此外,最小正双精度值 DBL_MIN 和最大双精度值 DBL_MAX 比我的 Linux gcc 编译器中的值大一点(仅与上面 gcc v4.8.5 中定义的宏相比):

#define DBL_MAX        1.7976931348623158e+308
#define DBL_MIN        2.2250738585072014e–308

此外,Microsoft 编译器将long double 限制设置为double 的值,似乎它不支持真正的long double 实现。

【问题讨论】:

  • 只是猜测:由于 base10 浮点数与 base2 浮点数不完全匹配,因此获得 exact 数量可能需要强制转换。
  • 另请参阅此问题:stackoverflow.com/questions/6333543/…
  • VS,编译器,(不是 Windows,操作系统)支持 long double 作为与 double 不同的类型。它只是具有与double 相同的编码。所以使用long double 并没有带来什么好处
  • " 此外,最小正双精度值更大,最大双精度值小于来自 mmy Linux gcc 编译器的值:"您是如何确定的?是通过比较double"1.7976931348623158e+308` 转换为((double)1.79769313486231570815e+308L)?还是比较源代码?
  • MSVC 自 18 年以来一直不符合标准。想知道为什么它不符合标准是没有意义的。

标签: gcc floating-point


【解决方案1】:

以十进制指定二进制浮点数有一些微妙的问题。

为什么将值定义为带后缀 L 的 long double,然后再转换回 double?

对于典型的binary64,最大有限值大约是1.795e+308 或正好。

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368

转换为唯一double 所需的位数可能与DBL_DECIMAL_DIG 一样多(通常为 17 且至少为 10)。无论如何,使用指数表示法当然是清楚的,而且不会过于精确。

/*
1 2345678901234567 */          // Sorted 
1.79769313486231550856124...   // DBL_MAX next smallest for reference
1.79769313486231570814527...   // Exact
1.79769313486231570815e+308L   // gcc
1.7976931348623158e+308        // VS (just a hair closer to exact than "next largerst")
1.7976931348623159077293....   // DBL_MAX next largest if not limited by range

各种编译器可能无法完全按照预期转换此字符串。有时会忽略一些最低有效数字 - 尽管这是由编译器控制的。

细微转换差异的另一个来源,我认为这就是添加“L”的原因double 计算受到处理器浮点单元的影响,而处理器的浮点单元可能没有 严格遵守 IEEE 标准。更糟糕的结果可能是 1.797...e+308 常量由于微小的转换错误而转换为 infinity,“代码到 double使用双重数学。通过转换为long double,那些long double 转换错误非常小。然后将long double 结果转换为double 舍入到希望的数字。

简而言之,强制 L 数学可确保不会无意中将常数设为无穷大

我希望以下既不匹配 gcc 也不匹配 VS 的内容足以满足符合 IEEE 754 标准 FPU 的要求。

#define __DBL_MAX__ 1.7976931348623157e+308

转换回double 是使DBL_MAX 成为double。这将满足许多代码的期望,即DBL_MAXdouble 而不是long double。不过,我没有看到需要这样做的规范。

为什么DBL_MIN_10_EXP定义为-307,但最小指数为-308?

即符合DBL_MIN_10_EXP的定义。 “...最小负整数,使得 10 的幂在归一化浮点数的范围内”非整数答案在 -307 和 -308 之间,因此范围内的最小整数是 -307。

观察部分

虽然 VS 将 long double 视为不同的类型,但使用与 double 相同的编码,因此使用 L 并没有数字优势。

【讨论】:

  • 非常感谢!很好的解释!
【解决方案2】:

不知道为什么要用L后缀。

This 站点概述了 IEEE 754 浮点。

指数为 11 位,偏移量为 1023。但指数 0 和 2047 保留用于特殊数字。所以这意味着指数可以在 2046-1023=1023 到 1-1023=-1022 之间变化。

因此,对于最大归一化值,我们的指数为 2^1023。尾数的最大值略低于 2(1.111 等,点后有 52 个 1,二进制),即 ~2*2^1023 = ~1.79e308。

对于最小标准化值,我们的指数为 2^-1022。最小尾数正好是 1,给我们一个 1*2^-1022 = ~2.22e-308 的值。到目前为止一切顺利。

DBL_MIN_10_EXP 和 DBL_MAX_10_EXP 是标准化的 10 的最小/最大指数。对于最大值,1e308 小于 ~1.79e308,因此值为 308。对于最小值,1e-308 太小 - 它低于 ~2.22e-308。 1e-307 大于 ~2.22e-308,因此值为 -307。

【讨论】:

  • 谢谢你非常好的解释!
猜你喜欢
  • 2018-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多