3

这是clang中的错误吗?

这将打印出最大的 double 值:

long double a = DBL_MAX;
printf("%Lf\n", a);

这是:

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

这将打印出最大的 long double 值:

long double a = LDBL_MAX;
printf("%Lf\n", a);

这是:

/* … bigger, but not displayed here. For a good reason. ;-) */

这很清楚。

但是当我使用算术表达式时,即编译时可计算为初始值设定项时,我得到了一个令人惊讶的结果:

long double a = 1.L + DBL_MAX + 1.L;
printf("%Lf\n", a); 

这仍然打印出 DBL_MAX 而不是 DBL_MAX + 2!?

如果计算是在运行时完成的,也是一样的:

long double b = 2.L;
long double a = DBL_MAX;
printf("%Lf\n", a+b);

仍然是 DBL_MAX。

$ clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin12.4.0
Thread model: posix
4

4 回答 4

8

IEE754 浮点的double尾数为 53 位宽(52 物理位 + 1 隐式位)。这意味着double可以准确地表示-2^53...+2^53范围内的连续整数(即 from -9007199254740992to +9007199254740992)。之后,该类型就不能再精确地表示连续的整数了。相反,该类型只能表示偶数整数值。根据某些特定于实现的规则,任何奇数值都将舍入为相邻的偶数值因此,完全可以预期,由于四舍五入,添加19007199254740992insidedouble可能不会导致任何结果。从该限制开始,您至少必须添加2才能看到值的变化(直到您达到添加的点2也将不再有任何效果,您必须至少添加4,依此类推)。

long double如果它大于double您的平台,则相同的逻辑适用于。在 x86 上long double可能指的是带有 64 位尾数的硬件 80 位浮点类型。这意味着即使使用这种类型,您精确表示连续整数的范围也仅限于-2^64...+2^64.

的值DBL_MAX是far, FAR, FAAAAR!在那个范围之外。这意味着尝试添加1不会DBL_MAX对价值产生任何影响。添加2也不会有任何效果。既不会4,也不会1024,甚至也不会4294967296。您必须在2^960区域中添加一些东西(实际上)才能对以 80 位格式存储nextafter(2^959)的值产生影响。DBL_MAXlong double

于 2013-06-27T16:58:14.557 回答
8

Not a bug. long double in clang/x86_64 has 64 bits of precision, and results are rounded to fit in that format.

This will all be clearer if we use hex instead of binary. DBL_MAX is:

0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

The exact mathematical result of 1.L + DBL_MAX is therefore:

0xfffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001

... but that is not representable as a long double, so the computed result is rounded to the closest representable long double, which is just DBL_MAX; adding 1 does not (and should not) change the value.

(It rounds down instead of up because the next larger representable number is

0xfffffffffffff801000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

which is much farther away from the mathematically precise result than DBL_MAX is).

于 2013-06-27T16:14:00.363 回答
4

This is expected behavior.

long double a = 1.L + DBL_MAX + 1.L;

The long double type is floating point: it has a finite amount of precision. The result of most operations is rounded to the nearest representable value.

See What Every Programmer Should Know About Floating-Point Arithmetic.

于 2013-06-27T16:13:56.783 回答
2

一个技术上不太正确的答案,希望能有所帮助:

数字由符号、指数和分数表示。

在此页面上,提供了有关 C 数据类型的信息 ( https://en.wikipedia.org/wiki/C_data_types )。该图表声称 long double 不能保证是比 double “更大”的数据类型;但是,从 C99 开始,如果它存在于目标架构中,则可以保证这一点(附录 F IEC 60559 浮点算法)。您的结果表明DBL_MAXLDBL_MAX在您的实现中,它确实使用了更多位。

这就是正在发生的事情:

您有以下格式的号码:

双倍那将是

<1 位><11 位><52 位>

总而言之,你有这个 80 位表示(https://en.wikipedia.org/wiki/Extended_precision

<1 位><15 位><64 位>

您可以将 double 类型放入 long double 类型中,这样不会导致任何问题。但是,请注意小数点是“浮动的”(因此得名),并非数字中的所有数字都被表示。计算机代表最高有效数字,然后是指数(例如,就像我写 1234567 E 234 一样,请注意我没有写该数字的所有 234 位数字)。当您尝试向其添加 1 时,不会表示一个位的数字(由于指数的大小),因此在四舍五入后将被忽略。

有关更多详细信息,请在此处阅读浮点数 ( https://en.wikipedia.org/wiki/Double_precision_floating-point_format )

于 2013-06-27T17:33:27.520 回答