C ++:double vs unsigned int。为什么会像这样运行?

时间:2012-10-06 17:04:42

标签: c++ int double unsigned

我尝试乘以三个数字,但得到一个奇怪的结果。为什么我会得到如此不同的结果?

unsigned int a = 7;
unsigned int b = 8;
double d1 = -2 * a * b;
double d2 = -2 * (double) a * (double) b;
double d3 = -2 * ( a * b );

// outputs:
// d1 = 4294967184.000000
// d2 = -112.000000
// d3 = 4294967184.000000

4 个答案:

答案 0 :(得分:9)

在第一个示例中,数字-2将转换为unsigned int。乘法结果为-112,当表示为无符号时为2 ^ 32 - 112 = 4294967184.然后,此结果最终转换为double以进行赋值。

在第二个示例中,所有数学都在双精度数上完成,从而得到正确的结果。如果您这样做,您将获得相同的结果:

double d3 = -2.0 * a * b

因为-2.0double字面值。

答案 1 :(得分:3)

双签署。这意味着第一位(最高有效位,也就是符号位)确定该数字是正数还是负数。

unsigned int无法处理负值,因为它使用第一位(最高位)来扩展它可以表示的“正”数字的范围。所以在

double d1 = -2 * a * b;

执行时,你的机器将整个(-2 * a * b)置于无符号的int结构(如a和b)中,它产生以下二进制1111 1111 1111 1111 1111 1111 1001 0000(因为它是两者的补码) 112的是0000 0000 0000 0000 0000 0000 0111 0000)。但这里的问题是它是unsigned int因此它被视为一个非常大的正整数(即4294967184),因为它不会将前1作为符号位。

然后你把它放在一个双,这就是你打印.00000的原因。

另一个例子是有效的,因为你将a转换为double而b转换为double,所以当-2乘以double时,你的计算机将把它放在双重结构中,因此,将考虑符号位。

double d3 = -2 * (double) (a * b)

也会奏效。

要了解签名和未签名,请检查this

答案 2 :(得分:1)

  

double d1 = -2 * a * b;

右侧的所有内容都是整数类型,因此右侧将被计算为整数类型。 ab是无符号的,因此指示结果的特定类型。那-2怎么样?它被转换为unsigned int。使用2s补码算法将负整数转换为无符号整数。 -2成为一个非常大的正无符号整数。

  

double d2 = -2 * (double) a * (double) b;

现在右侧是混合整数和浮点数,因此右侧将被计算为浮点类型。那-2怎么样?它被转换为双倍。现在转换非常简单:转换为-2的{​​{1}}变为double

答案 3 :(得分:1)

在C和C ++中,内置运算符始终应用于两个相同类型的变量。如果两个变量中的一个(或两个)变量最初不同(或太小),则一组非常精确的规则会指导促销

在这种精确的情况下,-2默认为signed int类型(int的同义词),而ab的类型为{{1} }}。在这种情况下,规则规定unsigned int应该提升为-2,并且因为在您的系统上,您可能有32位unsigned int和2补码表示,这最终成为int(4 294 967 294)。然后将此数字乘以2**32 - 2,结果取模a(4 294 967 282),然后2**32,再次模数b(4 294 967 184)。

这真是一个奇怪的系统,并导致了无数的错误。例如,溢出本身导致今年6月30日的Linux漏洞导致全世界许多计算机被绞死。我听说它也破坏了几个Java系统。