3

我的代码:

int main()
{
long long a = pow(2,63) - 1;
long long b = pow(2,63);
double c  = pow(2,63) - 1;
double d = pow(2,63);
printf("%lld %lld \n%f %f \n%lld %lld\n", a, b, c, d, (long long)c, (long long)d);

return 0;
}

并且执行结果是(在win7 x64中使用gcc的代码块):

9223372036854775807 9223372036854775807
9223372036854775800.000000 9223372036854775800.000000
-9223372036854775808 -9223372036854775808

问题:

为什么a == b

我知道这是c == d因为double.

但是为什么(long long)c(long long)d不是9223372036854775800呢?

为什么(long long)c != a(long long)d != b

4

6 回答 6

4

pow(2,63) - 1都是在双精度浮点运算中完成的。特别是,-1转换为-1.0并且太小而无关紧要

于 2013-10-08T14:13:29.477 回答
2

为什么 a == b

因为您的编译器(gcc)计算了要初始化ab使用的值,并且发现(证明?)两者都匹配或超过了 a 的最大可能值long long,所以它使用该最大值LLONG_MAX(或0x7FFFFFFFFFFFFFFF,或9223372036854775807在您的平台上)初始化了两者。

请注意(正如 Pascal Cuoq 所指出的),这是未定义的行为,是由在初始化and时将 a 转换double为 a时溢出引起的。虽然 gcc 如上所述处理此问题,但其他编译器可以以不同方式处理此问题long longab

我知道 c ==d 因为 double 的精度

之所以cd保持相同的值确实是因为 a 的精度double

  • pow(2, 63)可以用分数1和指数准确表示63
  • pow(2, 63) - 1无法准确表示

它没有显示的原因9223372036854775808(存储在cand中的精确值d)是因为printf精度,在您的平台上显然只显示 17 位数字。您可以使用例如强制它显示更多内容。,但在 Windows 上可能不会因为这个错误%20.0f而有所作为。

为什么 (long long)c 和 (long long)d 不是 9223372036854775800 ?

因为cd保存值9223372036854775808,或0x8000000000000000,当打印为有符号值时变为-9223372036854775808

请注意,这又是未定义的行为(由于有符号溢出)。

为什么 (long long)c != a 和 (long long)d != b?

因为它们的计算方式不同。ab由编译器计算,而(long long) c(long long) d是在运行时计算的。

虽然通常,这些不同的计算方式应该产生相同的结果,但我们在这里处理的是未定义的行为(如前所述),所以一切都会发生。在您的情况下,编译器的结果与运行时结果不同。

于 2013-10-08T15:07:55.473 回答
2

为什么a == b?我知道这是c == d因为双精度。

出于完全相同的原因。整数类型没有 的重载pow,所以算术是使用double. 由于double通常具有 52 位有效位,因此将 1 添加或减去 1 到与 2 63一样大的值将无效。

为什么(long long)c(long long)d不是9223372036854775800

因为long long是64位有符号类型,最大可表示值为2 63 -1。c并且d可能都具有超出范围的值 2 63(或什至稍大的值)。正如您所观察到的,在典型的 2s 补码平台上,这很可能会溢出以给出大约 -2 63的值。但请注意,这是未定义的行为;如果浮点转换溢出,您将无法依赖任何东西。

为什么(long long)c != a(long long)d != b

我不知道; 对我来说,a并且b具有相同的大负值。看起来您的实现的一些怪癖导致ab最终得到值 2 63 -1 而不是预期的 2 63。与处理浮点数一样,您应该期望像这样的小舍入误差。

您可以使用整数算术获得确切的结果:

long long a = (1ULL << 63) - 1;
unsigned long long b = 1ULL << 63;

请注意无符号算术的使用,因为如上所述,有符号(1LL << 63)会溢出。

于 2013-10-08T14:57:08.760 回答
1

因为pow返回双精度和双精度丢失。这就是为什么a==b

于 2013-10-08T14:13:01.760 回答
0

长长-> %lld

长双->%Lf

双 -> %f

浮动-> %f

整数 -> %d

请阅读 << C 上的指针>> 中的第 15 章了解更多详细信息。

于 2013-10-09T10:02:26.343 回答
0

pow(2, 63)相当于pow((double) 2, (double) 63)

事实上,C++11 26.8 [c.math] 第 3 段说<cmath>提供了声明,double pow(double, double)第 11 段说(强调我的)

  1. 如果对应于 double 形参的任何实参的类型为 long double,则对应于 double 形参的所有实参都将有效地转换为 long double。
  2. 否则,如果与形参对应的任何实double参具有类型double或整数类型,则与形参对应的所有实double参都被有效地强制转换为double.
  3. 否则,对应于双参数的所有参数都被有效地转换为浮点数。

现在,文字263ints,因此,pow(2, 63)等价于pow((double) 2, (double) 63)。然后返回的类型double没有“看到”和之间的差异所需的 63 位2^63精度2^63 - 1

我推荐阅读这篇文章和 Howard Hinnant 的出色回答。

于 2013-10-08T14:14:52.790 回答