2

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

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

4 回答 4

10

在您的第一个示例中,该数字-2被转换为无符号整数。乘法的结果是 -112,当表示为无符号时是 2^32 - 112 = 4294967184。然后这个结果最终被转换double为赋值。

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

double d3 = -2.0 * a * b

就像文字-2.0一样。double

于 2012-10-06T17:09:16.023 回答
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)。但这里的问题是它是无符号整数,因此它被视为一个非常大的正整数(即 4294967184),因为它不会将第一个 1 视为符号位。

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

另一个示例之所以有效,是因为您将 a 类型转换为 double 并将 b 类型转换为 double,因此当将 -2 与 double 相乘时,您的计算机会将其置于 double 结构中,因此将考虑符号位。

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

也会起作用。

要了解已签名和未签名的内容,请查看

于 2012-10-06T17:28:53.930 回答
1

double d1 = -2 * a * b;

右侧的所有内容都是整数类型,因此右侧将被计算为整数类型。a并且b是无符号的,因此这决定了结果的特定类型。那-2怎么办?它已转换为unsigned int. 使用 2s 补码算法将负整数转换为无符号整数。这-2变成了一个非常大的无符号正整数。

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

现在右手边是混合整数和浮点数,所以右手边将被计算为浮点类型。那-2怎么办?它已转换为双精度。现在转换很简单:-2转换为 adouble变为-2.0

于 2012-10-06T17:12:30.733 回答
1

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

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

这确实是一个奇怪的系统,并导致了无数的错误。例如,溢出本身导致了今年 6 月 30 日的 Linux 错误,导致全球许多计算机挂了。我听说它还使几个 Java 系统崩溃。

于 2012-10-06T17:50:16.997 回答