Here's what's happening. As per C11 6.3.1.8 Usual arithmetic conversions
(the "otherwise" comes into play here since previous paragraphs discuss what happens when either type is already floating point):
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
This means your signed value of -10
becomes an unsigned value of 0xffff'fff6
, or 4,294,967,286
. Multiplying that by 25
gives 107,374,182,150
or 0x18'ffff'ff06
(which is the result you want).
However, at this point, no float
calculations have been done, the multiplication is a pure integer calculation and the resultant value will be an integer. And that, combined with the fact your unsigned integers are 32 bits long, means it gets truncated to 0xffff'ff06
, or 4,294,967,046
.
Then you put that into the float
.
To fix this to match your expected results, you should change he expression to force this:
float z = 1.0f * (unsigned)x * y;
This changes the int * unsigned-int
calculation into a float * unsigned-int * unsigned-int
one. The unsigned
cast first ensures x
will be converted to the equivalent unsigned value and the multiplication by 1.0f
ensures the multiplication are done in the float
arena to avoid integer truncation.