我搜索了文档,但我找不到任何解释这个。
我有一个D程序:
import std.stdio;
void main () {
writeln(int.max);
int a = 2;
int b = 180;
writeln(a^^b);
}
它写道:
2147483647
0
我溢出int
,但我得到0
而不是垃圾或包装。
如果我使用real
或double
,显然输出将是正确的。
我编写了一个用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拒绝包装溢出?
答案 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中,它根本没有触及浮点。整数乘法被定义为通过二进制补码和截断来工作,这就是这里发生的事情。它溢出,将所有零留在后面。