【问题标题】:C++: double vs unsigned int. Why it runs like this?C++:double vs unsigned int。为什么会这样运行?
【发布时间】:2012-09-27 12:46:26
【问题描述】:

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

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

【问题讨论】:

    标签: c++ int double unsigned


    【解决方案1】:

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

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

    double d3 = -2.0 * a * b
    

    因为 -2.0double 文字。

    【讨论】:

    • 感谢您的解释!但是为什么:“-122”我们在这里:“2^32 - 122”?什么是“122”?
    • 这不是它的工作原理。数字-2 转换为unsigned int。在具有 32 位整数的机器上,-2 变为 4294967294。将其乘以 56 (7*8) 不会产生 -112。怎么可能?它产生了一个相当大的数字,240518168464。这对于unsigned int 来说太大了。这变成了 4294967184,因为对于无符号整数,无法由得到的无符号整数类型表示的结果以比得到的无符号整数类型可以表示的最大值大一的数字为模减少 (换句话说,模 2^n)。
    【解决方案2】:

    double d1 = -2 * a * b;

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

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

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

    【讨论】:

      【解决方案3】:

      double 已签名。这意味着第一位(最高有效位又名符号位)确定该数字是正数还是负数。

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

      double d1 = -2 * a * b;
      

      执行时,您的机器将整个 (-2 * a * b) 放入无符号整数结构(如 a 和 b)中,并生成以下二进制 1111 1111 1111 1111 1111 1111 1001 0000(因为它是二进制补码112 是 0000 0000 0000 0000 0000 0000 0111 0000)。但这里的问题是它是无符号整数,所以它被视为一个非常大的正整数(即 4294967184),因为它不会将第一个 1 视为符号位。

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

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

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

      也可以。

      要了解已签名和未签名,请查看this

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-20
          • 1970-01-01
          • 2016-02-26
          • 2011-04-08
          相关资源
          最近更新 更多