A type conversion can
keep the conceptual value (the bitpattern may have to be changed), or
keep the bitpattern (the conceptual value may have to be changed).
The only C++ cast that guaranteed always keeps the bitpattern is const_cast
.
A reinterpret_cast
is, as its name suggests, intended to keep the bitpattern and simply reinterpret it. But the standard allows an implementation very much leeway in how to implement reinterpret_cast
. In some case a reinterpret_cast
may change the bitpattern.
A dynamic_cast
generally changes both bitpattern and value, since it generally delves into an object and returns a pointer/reference to a sub-object of requested type.
A static_cast
may change the bitpattern both for integers and pointers, but, nearly all extant computers use a representation of signed integers (called two's complement) where static_cast
will not change the bitpattern. Regarding pointers, suffice it to say that, for example, when a base class is non-polymorphic and a derived class is polymorphic, using static_cast
to go from pointer to derived to pointer to base, or vice versa, may change the bitpattern (as you can see when comparing the void*
pointers). Now, integers...
With n value bits, an unsigned integer type has 2^n values, in the range 0 through 2^n-1 (inclusive).
The C++ standard guarantees that any result of the type is wrapped into that range by adding or subtracting a suitable multiple of 2^n.
Actually that's how the C standard describes it; the C++ standard just says that operations are modulo 2^n, which means the same.
With two's complement form a signed value -x has the same bitpattern as the unsigned value -x+2^n. That is, the same bitpattern as the C++ standard guarantees that you get by converting -x to unsigned type of the same size. That's the simple basics of two's complement form, that it is precisely the guarantee that you're seeking. :-)
And nearly all extant computers use two's complement form.
Hence, in practice you're guaranteed an unchanged bitpattern for your examples.