2012-10-06 31 views
1

我尝试乘以三个数字,但我得到一个奇怪的结果。为什么我得到如此不同的结果?C++:double与unsigned int。为什么它像这样运行?

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 

回答

9

在第一个示例中,数字-2被转换为无符号整型。乘法结果为-112,表示为无符号时为2^32 - 112 = 4294967184.然后,此结果最终转换为double以进行分配。

在第二个例子中,所有的数学运算都在双精度上完成,导致正确的结果。你会得到相同的结果,如果你做的事:

double d3 = -2.0 * a * b 

-2.0double文字。

+0

感谢您的解释!但为什么:“-122”我们在这里:“2^32 - 122”?什么是“122”? – JavaRunner

+1

这不是它的工作原理。数字“-2”被转换为“unsigned int”。在32位整数的机器上,'-2'变成4294967294.乘以56(7 * 8)不会产生-112。它怎么样?它产生了一个相当大的数字,240518168464.这对于'unsigned int'来说太大了。这变成4294967184,因为使用无符号整数*,无法用结果无符号整数类型表示的结果减少了模数,该数字大于可以由所得无符号整数类型*表示的最大值的数字*(换句话说,模2^n)。 –

1
在右手侧

double d1 = -2 * a * b;

一切都是一体型的,所以右侧将被计算为一体型。 ab是无符号的,所以决定了结果的具体类型。那-2呢?它被转换为unsigned int。使用二进制补码算法将负整数转换为无符号整数。该-2成为一个非常大的正无符号整数。

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

现在的右手侧是混合整数和浮点数,因此右手侧将被计算为浮点型。那-2呢?它被转换为双倍。现在转换很简单:-2转换为double变成-2.0

3

双签署。这意味着第一位(最重要的位又称符号位)决定了这个数字是正数还是负数。

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

double d1 = -2 * a * b; 

当被执行时,设备把整个(-2 * A * B)在一个unsigned int结构(像a和b)和它产生如下的二进制1111 1111 1111 1111 1111 1111 1001 0000 (因为它是112的二进制补码,即0000 0000 0000 0000 0000 0000 0111 0000)。但是这里的问题在于它是无符号整型,因此它被视为一个非常大的正整数(4294967184),因为它不会将第一个1作为符号位处理。

然后你把它放在双倍这就是为什么你有0.00000印。

另一个例子的工作原理是因为你将a变为double并且b变成double使得-2与double相乘时,你的计算机将把它放在一个双重结构中,因此符号位将被考虑。

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

也可以工作。

为了得到一个感觉有关签署和未签署,检查this

1

在C和C++内置的运算总是应用在同类型的两个变量。如果它们最初不同(或者太小),那么非常精确的一组规则指导促销两个变量中的一个(或两个)。

在这种精确的情况下,-2是由signed int类型(同义词int)的默认而abunsigned int类型。在这种情况下,规则指出-2应该被提升为unsigned int,并且因为在你的系统上你可能有32位int和一个2补码表示,所以最终结果为2**32 - 2(4 294 967 294)。然后这个数字乘以a,结果取模2**32(4294967282),然后b,模2**32再次(4294967184)。

这真是一个奇怪的系统,并导致无数的错误。例如,溢出本身导致今年6月30日发生的Linux错误,该错误在世界各地挂起了许多计算机。我听说它也撞毁了一对Java系统。

相关问题