Let’s start by analysing the result of t - d
.
t
is an unsigned int
while d
is an int
, so to do arithmetic on them, the value of d
is converted to an unsigned int
(C++ rules say unsigned gets preference here). So we get 10u - 16u
, which (assuming 32-bit int
) wraps around to 4294967290u
.
This value is then converted to float
in the first declaration, and to int
in the second one.
Assuming the typical implementation of float
(32-bit single-precision IEEE), its highest representable value is roughly 1e38
, so 4294967290u
is well within that range. There will be rounding errors, but the conversion to float won’t overflow.
For int
, the situation’s different. 4294967290u
is too big to fit into an int
, so wrap-around happens and we arrive back at the value -6
. Note that such wrap-around is not guaranteed by the standard: the resulting value in this case is implementation-defined(1), which means it’s up to the compiler what the result value is, but it must be documented.
(1) C++17 (N4659), [conv.integral] 7.8/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type;
otherwise, the value is implementation-defined.