可以存储在 IEEE 754 双精度类型中而不会丢失精度的最大“非浮动”整数是多少?
可以存储在双精度中而不会丢失精度的最大/最大整数与双精度的最大可能值相同。也就是说,DBL_MAX
或大约 1.8 × 10308(如果您的双精度是 IEEE 754 64 位双精度)。它是一个整数。它被准确地表示出来。你还想要什么?
继续,问我最大的整数是多少,这样它和所有较小的整数都可以存储在 IEEE 64 位双精度中而不会丢失精度。 IEEE 64 位双精度有 52 位尾数,所以我认为是 253:
253 + 1 无法存储,因为开头的 1 和结尾的 1 之间的零太多。
可以存储小于 253 的任何值,其中 52 位显式存储在尾数中,然后有效的指数为您提供另一个。
253 显然可以存储,因为它是 2 的小幂。
或者另一种看待它的方式:一旦从指数中移除偏差,并忽略与问题无关的符号位,double 存储的值是 2 的幂,加上 52 位整数乘以 2exponent - 52。因此,使用指数 52,您可以存储从 252 到 253 - 1 的所有值。然后使用指数 53,您可以在 253 之后存储的下一个数字是 253 + 1 × 253 - 52。因此,精度损失首先发生在 253 + 1。
9007199254740992(即 9,007,199,254,740,992 或 2^53),没有任何保证:)
程序
#include <math.h>
#include <stdio.h>
int main(void) {
double dbl = 0; /* I started with 9007199254000000, a little less than 2^53 */
while (dbl + 1 != dbl) dbl++;
printf("%.0f\n", dbl - 1);
printf("%.0f\n", dbl);
printf("%.0f\n", dbl + 1);
return 0;
}
结果
9007199254740991 9007199254740992 9007199254740992
double dbl = 1; while (dbl + 1 != dbl) dbl *= 2; while (dbl == --dbl);
,它会产生相同的结果
while (dbl == --dbl)
将永远循环或根本不循环。 :) (在这种情况下,根本不是,因为它是 2^N)。你必须从下面接近它。它确实也会导致比预期结果少一(因为 while 循环中的一检查会减少 dbl)。它取决于执行顺序,如果在评估左侧之前或之后进行减量(据我所知,这是未定义的)。如果是前者,它将永远为真并永远循环。
dbl + 1 != dbl
中使用 while (dbl + 1 != dbl) dbl++;
的弱点可以使用 long double
数学进行评估 - 考虑 FLT_EVAL_METHOD == 2
。这可能以无限循环结束。
IEEE 754 double(64 位)中可以表示的最大整数与该类型可以表示的最大值相同,因为该值本身就是一个整数。
这表示为 0x7FEFFFFFFFFFFFFF
,它由以下部分组成:
符号位 0(正)而不是 1(负)
最大指数 0x7FE(2046 表示减去偏差后的 1023)而不是 0x7FF(2047 表示 NaN 或无穷大)。
最大尾数 0xFFFFFFFFFFFFF,即 52 位全为 1。
在二进制中,该值是隐含的 1,后跟尾数中的另外 52 个 1,然后是指数中的 971 个零 (1023 - 52 = 971)。
确切的十进制值为:
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368
这大约是 1.8 x 10308。
Wikipedia 在与 IEEE 754 的链接相同的上下文中这样说:
在典型的计算机系统上,“双精度”(64 位)二进制浮点数具有 53 位的系数(其中一个是隐含的)、11 位的指数和一个符号位。
2^53 刚好超过 9 * 10^15。
您需要查看尾数的大小。 IEEE 754 64 位浮点数(有 52 位,隐含加 1)可以精确表示绝对值小于或等于 2^53 的整数。
1.7976931348623157 × 10^308
http://en.wikipedia.org/wiki/Double_precision_floating-point_format
确实,对于 64 位 IEEE754 double,可以精确表示直到 9007199254740992 == 2^53 的所有整数。
但是,还值得一提的是,4503599627370496 == 2^52 之外的所有可表示数字都是整数。超过 2^52 之后,测试它们是否为整数就变得毫无意义,因为它们都被隐式四舍五入到附近的可表示值。
在 2^51 到 2^52 的范围内,唯一的非整数值是以“.5”结尾的中点,这意味着计算后的任何整数测试都必须预期产生至少 50% 的错误答案。
在 2^51 以下,我们还有“.25”和“.75”,因此将一个数字与其四舍五入的对应数字进行比较以确定它是否是整数就开始有意义了。
TLDR:如果要测试计算结果是否可能是整数,请避免大于 2251799813685248 == 2^51 的数字
正如其他人所指出的,我将假设 OP 要求最大的浮点值,以便所有小于自身的整数都可以精确表示。
您可以使用 float.h
中定义的 FLT_MANT_DIG
和 DBL_MANT_DIG
来不依赖显式值(例如,53):
#include <stdio.h>
#include <float.h>
int main(void)
{
printf("%d, %.1f\n", FLT_MANT_DIG, (float)(1L << FLT_MANT_DIG));
printf("%d, %.1lf\n", DBL_MANT_DIG, (double)(1L << DBL_MANT_DIG));
}
输出:
24, 16777216.0
53, 9007199254740992.0