为什么是((unsigned int)x)* y ==((unsigned int)(x * y)总是如此?

时间:2015-07-29 13:52:51

标签: c++ implicit-conversion

我刚写了这些代码:

int x = -1;//x must be negative
unsigned int y = 1;//y must be positive
bool b;
for(; ; x--, y++){
    b = ((unsigned int)x) * y == ((unsigned int)(x * y));
}

然后我发现b总是如此。在我看来,((unsigned int)x)* y会溢出,但((unsigned int)(x * y))不会。我真的很难相信这是真的。这只是巧合还是这种现象背后有任何法律?

3 个答案:

答案 0 :(得分:8)

x * y中,由于通常的算术转换,x已转换为unsigned。 §5/ 10:

enter image description here

即。您的第一个表达式(unsigned)(x * y)相当于(unsigned)((unsigned)x * y),而(unsigned)x * y相当于unsigned int - 您的第二个表达式。

请注意,signed的等级等于(intRequireJS的等级§4.13/ 1.4:

  

任何无符号整数类型的等级应等于等级   相应的有符号整数类型。

答案 1 :(得分:3)

  

这只是巧合还是这种现象背后有任何法律?

不,这不是巧合,这可以通过通常的算术转换执行的隐式转换来解释。

乘法运算符对其操作数执行usual arithmetic conversions,以使它们成为公共类型和此子表达式:

(unsigned int)(x * y)

将导致x转换为unsigned int。这使得:

(unsigned int)(x * y)

相当于:

((unsigned int)x) * y

很多时候使用正确的警告标志可以帮助解决难题,使用-Wall和gcc它会发出以下警告:

warning: self-comparison always evaluates to true [-Wtautological-compare]
   b = ((unsigned int)x) * y == ((unsigned int)(x * y));
                             ^

在clang中使用-Wconversion标志会发出以下警告( see it live ):

warning: implicit conversion changes signedness: 'int' to 'unsigned int' [-Wsign-conversion]
b = ((unsigned int)x) * y == ((unsigned int)(x * y));
                                             ^ ~

供参考,草案C ++标准部分5.6 [expr.mul] 表示:

  

通常的算术转换是在操作数上执行的   并确定结果的类型。

涵盖常规算术转化的部分5表示:

  

否则,应对两个操作数执行整数提升(4.5)。 61 然后以下   规则应适用于推广的操作数:

     

[...]

     
      
  • 否则,如果具有无符号整数类型的操作数的等级大于或等于   另一个操作数的类型的等级,带有符号整数类型的操作数应转换为   具有无符号整数类型的操作数的类型。
  •   

我们可以在4.13 [em.rank] 部分找到cppreference here及C ++标准草案所涵盖的排名:

  

任何无符号整数类型的等级应等于相应的有符号整数类型的等级。

答案 2 :(得分:1)

这里有两个要点:

  1. 通常的算术转换

  2. 无符号整数运算不能溢出

  3. 引用标准:

      

    §5表达式[expr]

         

    9许多期望操作数的二元运算符   算术或枚举类型会导致转换和产生结果   以类似的方式输入类型。目的是产生一种共同的类型   也是结果的类型。这种模式通常被称为   算术转换,定义如下:

         

    [...]

         

    - 否则,如果具有无符号整数类型的操作数的等级大于或等于   另一个操作数的类型的等级,带有符号整数类型的操作数应转换为   具有无符号整数类型的操作数的类型

    所以(第1点),两个操作数具有相同的等级,有符号的操作数将被转换为无符号类型。因此,((unsigned int)x) * y((unsigned int)(x * y))评估始终相同。

    现在让我们看看它们是否始终有效。

      

    §3.9.1基本类型[basic.fundamental]

         

    4无符号整数,声明无符号整数,应遵守算术模2n的定律,其中n是数字   特定大小为整数的值表示中的位数.46

         

    46)这意味着无符号算术不会溢出,因为结果无法表示   无符号整数类型以模数减少,该数字大于可由其表示的最大值   得到无符号整数类型。

    所以(第2点),((unsigned int)x) * y溢出(y也不会x,因为for循环继续,所以未定义行为将会发生)。