为什么溢出会导致0?

时间:2016-02-28 16:27:53

标签: d integer-overflow

我搜索了文档,但我找不到任何解释这个。

我有一个D程序:

import std.stdio;
void main () {
  writeln(int.max);
  int a = 2;
  int b = 180;
  writeln(a^^b);
}

它写道:

2147483647
0

我溢出int,但我得到0而不是垃圾或包装。

如果我使用realdouble,显然输出将是正确的。

我编写了一个用C语言进行实验的程序(不是C是D,但D编译为本机代码而C是可移植的汇编程序,因此它们应该具有可比性):

#include <stdio.h>
#include <math.h>
#include <limits.h>
int main(void) {
  double i = pow(2, 128);
  int j    = (int) i;
  printf("int max:          %d\n", INT_MAX);
  printf("print dbl as int: %d\n", i);
  printf("cast dbl -> int:  %d\n", j);
  printf("double:           %f\n", i);
    return 0;
}

它给出了:

int max:          2147483647
print dbl as int: -1254064128
cast dbl -> int:  -2147483648
double:           340282366920938463463374607431768211456.000000

第二行和第三行很少是两次相同的东西,因为我认为它是未定义的行为,这就是重点。

我知道D想要成为更好的C,这样做的方法是消除未定义的行为。

但是,如果D是一种系统编程语言(它甚至具有内联asm),为什么D拒绝包装溢出?

1 个答案:

答案 0 :(得分:4)

它DID包装溢出。你碰巧尝试了碰巧归零的案例族。试试3^^180。我得到了-949019631。仅仅因为一个数字在屏幕上看起来很漂亮并不意味着它不是垃圾!

考虑2 ^^ n == 1&lt;&lt; ñ。当你一次又一次地移动一个位时会发生什么?最终,右边的所有位都变为零!然后当你截断它以适应64位值时,你将全部归零。

但是,无论如何,让我详细介绍一下。首先,批评你的C:

// snip. note that i is type double
  printf("print dbl as int: %d\n", i);

这一行在两个级别上是错误的:它传递64位双精度,其中printf期望32位int,并且它将这些位重新解释为int,这与转换为int完全不同。

如果你想在D中执行此操作,则需要使用union显式重新解释位或通过中间指针强制转换。如果你愿意,你甚至可以切掉其他32位!

使用正确的显式强制转换的下一行是正确编写的,但仍然是未定义的行为,因为当它太大而不适合时,将一个double转换为int,既不是C也不是D(也不是底层硬件)做出任何承诺约。

然后回到D. D中的^^运算符只是将表达式重写为std.math.pow(a, b)std.math.pow针对不同类型具有不同的实现。由于这两个参数在这里都是积分的,所以它根本不进行浮点计算 - 它有一个纯粹的int / long实现,就像乘法一样。

所以你的C比较不太正确,因为在C中,你使用double并尝试转换,而在D中,它根本没有触及浮点。整数乘法被定义为通过二进制补码和截断来工作,这就是这里发生的事情。它溢出,将所有零留在后面。