Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is ((unsigned int)x) * y == ((unsigned int)(x * y) always true?

I just wrote these codes:

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));
}

Then I just found that b is always true. In my opinion, ((unsigned int)x) * y will overflow, but ((unsigned int)(x * y))won't. It's really hard for me to believe this to be true. Is this just coincidence or is there any law behind this phenomenon?

like image 984
Yuan Wen Avatar asked Dec 14 '22 12:12

Yuan Wen


2 Answers

In x * y, x is already converted to unsigned as a result of the usual arithmetic conversions. §5/10:

enter image description here

I.e. your first expression, (unsigned)(x * y), is equivalent to (unsigned)((unsigned)x * y) which in turn is equivalent to (unsigned)x * y - your second expression.


Note that the rank of unsigned int equals the rank of (signed)int by §4.13/1.4:

The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type.

like image 174
Columbo Avatar answered Dec 24 '22 11:12

Columbo


Is this just coincidence or is there any law behind this phenomenon?

No, this is not a coincidence, this can be explained by the implicit conversion performed by the usual arithmetic conversions.

The multiplication operator performs the usual arithmetic conversions on its operands to bring them to a common type and for this sub-expression:

(unsigned int)(x * y)

will result in x being converted to unsigned int. Which makes:

(unsigned int)(x * y)

equivalent to:

((unsigned int)x) * y

A lot of times using the correct warning flags can help to solve a puzzle, using -Wall with gcc it gives the following warning:

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

Using -Wconversion flag with clang gives the following warning (see it live):

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

For reference the draft C++ standard section 5.6 [expr.mul] says:

The usual arithmetic conversions are performed on the operands and determine the type of the result.

And section 5 which covers the usual arithmetic conversions says:

Otherwise, the integral promotions (4.5) shall be performed on both operands.61 Then the following rules shall be applied to the promoted operands:

[...]

  • Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.

We can find rank covered on cppreference here and the draft C++ standard in section 4.13 [conv.rank]:

The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type.

like image 31
Shafik Yaghmour Avatar answered Dec 24 '22 13:12

Shafik Yaghmour