When does casting change a value's bits in C++?
I have a C++ unsigned int
which is actually storing a signed value. I want to cast this variable to a signed int
, so that the unsigned and signed values have the same binary value.
unsigned int lUnsigned = 0x80000001;
int lSigned1 = (int)lUnsigned; // Does lSigned == 0x80000001?
int lSigned2 = static_cast<int>(lUnsigned); // Does lSigned == 0x80000001?
int lSigned3 = reinterpret_cast<in开发者_StackOverflow社区t>(lUnsigned); // Compiler didn't like this
When do casts change the bits of a variable in C++? For example, I know that casting from an int
to a float
will change the bits because int
is twos-complement and float
is floating-point. But what about other scenarios? I am not clear on the rules for this in C++.
In section 6.3.1.3 of the C99 spec it says that casting from an unsigned to a signed integer is compiler-defined!
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.
If you cast from a smaller signed integral type to a larger signed integral type, copies of the original most significant bit (1
in the case of a negative number) will be prepended as necessary to preserve the integer's value.
If you cast an object pointer to a pointer of one of its superclasses, the bits can change, especially if there is multiple inheritance or virtual superclasses.
You're kind of asking for the difference between static_cast
and reinterpret_cast
.
If your implementation uses 2's complement for signed integer types, then casting from signed to unsigned integer types of the same width doesn't change the bit pattern.
Casting from unsigned to signed could in theory do all sorts of things when the value is out of range of the signed type, because it's implementation-defined. But the obvious thing for a 2's complement implementation to do is to use the same bit pattern.
If your implementation doesn't use 2's complement, then casting between signed and unsigned values will change the bit pattern, when the signed value involved is negative. Such implementations are rare, though (I don't specifically know of any use of non-2's complement in C++ compilers).
Using a C-style cast, or a static_cast
, to cast an unsigned int
to a signed int
may still allow the compiler to assign the former to the latter directly as if a cast were not performed, and thus may change the bits if the unsigned int
value is larger than what the signed int
can hold. A reinterpret_cast
should work though, or you can type-cast using a pointer instead:
unsigned int lUnsigned = 0x80000001;
int lSigned1 = *((int*)&lUnsigned);
int lSigned2 = *(reinterpret_cast<int*>(&lUnsigned));
unsigned int is always the same size as int. And every computer on the planet uses 2's complement these days. So none of your casts will change the bit representation.
You're looking for int lSigned = reinterpret_cast<int&>(lUnsigned);
You don't want to reinterpret the value of lUnsigned, you want to reinterpret the object lUnsigned. Hence, the cast to a reference type.
Casting is just a way to override the type-checker, it shouldn't actually modify the bits themselves.
精彩评论