On 03.01.2019 17:24,
james...@alumni.caltech.edu wrote:
> On Thursday, January 3, 2019 at 8:04:15 AM UTC-5, Paavo Helde wrote:
>> Found a new C++ quiz question (in the hard way unfortunately). What does
>> the following program do when executed?
>>
>> #include <iostream>
>> #include <stdint.h>
>> int main() {
>> uint16_t x = 50000;
>> uint16_t y = 50000;
>> uint32_t z = x*y;
>> std::cout << z << "\n";
>> }
>>
>> Hint: when compiling with gcc do not forget the -ftrapv option.
>
> If UINT_MAX == 0xFFFF, then x and y get multiplied as uint16_t values.
Effectively, but formally not quite.
In this case type `int` cannot represent all values of the source type
`uint16_t`, and so integral promotion of the operands to `*`, IF it is
performed, will convert them to `unsigned`. Which in this case is also
16 bits. So that's the “effectively”, but the formal type for the case
of promotion is `unsigned`, not `uint16_t`, which *might* be a distinct
type, e.g. `unsigned short`.
Promotion can happen when `uint16_t` is a distinct type from `unsigned`,
e.g. when it's `unsigned short`, so that it has lower conversion rank
than `int`.
Relevant standardese:
C++17 §8.6/2 about multiplicative operators, including `*`: “The usual
arithmetic conversions are performed on the operands and determine the
type of the result.”
C++17 8/11.5 about usual arithmetic conversions: “Otherwise [not
floating point], the integral promotions (7.6) shall be performed on
both operands. Then the following rules shall be applied […]”
C++17 §7.6/1 about integral promotion: “A prvalue of an integer type
other than bool , char16_t , char32_t , or wchar_t whose integer
conversion rank (7.15) is less than the rank of int can be converted to
a prvalue of type int if int can represent all the values of the source
type; otherwise, the source prvalue can be converted to a prvalue of
type unsigned int”
C++17 $7.15/1.3, about conversion ranks: “The rank of long long int
shall be greater than the rank of long int, which shall be greater than
the rank of int, which shall be greater than the rank of short int,
which shall be greater than the rank of signed char.”
C++17 $7.15/1.4: “The rank of any unsigned integer type shall equal the
rank of the corresponding signed integer type.”
So, in the case where promotion is performed, which as far as I can see
is only when `uint16_t` is defined as `unsigned short`, the result type
is `unsigned`, not `uint16_t`.
Disclaimer: it's late in the day for me, AND I don't have a
standard-conforming 16-bit C++ compiler to try this out on. But. Still
sounds right when I read what I wrote! :)
> The product is well defined as 2500000000 modulo 0x10000 = 63744, which
> then gets converted to uint31_t without change of value, with the result
> that 63744 should be printed out by the cout.
>
> In the extremely unlikely (but permitted) case that UINT_MAX > 65535
> while INT_MAX < 65535, then x and y will both be promoted to unsigned
> int before the multiplication, and the result will be 2500000000 modulo
> (UINT_MAX+1ULL). This will be converted to uint32_t by taking it modulo
> 0x100000000, which might involve a change of value if
> UINT_MAX > 0xFFFFFFFF. That value in decimal format will be printed out
> by cout.
>
> If INT_MAX > 65535, then x and y will both be promoted to int before the
> multiplication. If so:
>
> If INT_MAX >= 2500000000, then the multiplication will not overflow.
> Converting the result to uint32_t in order to save it in z will leave
> the value unchanged, and that is what will be printed out by cout.
>
> Otherwise, the multiplication will overflow, with undefined behavior. However, if int is a 2's complement type, the actual behavior is likely to be exactly the same as the previous case.
>
> Which of these cases applied when you found the answer "the hard way"?
Cheers!,
- Alf