23

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<int>(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!

sourcenouveau
  • 29,356
  • 35
  • 146
  • 243
  • From my own testing in VS2008 static_cast(lUnsigned) does not change the bits. – sourcenouveau Nov 18 '10 at 19:20
  • It is an implementation detail what the underlying integer representation is. Making any assumption of 2 compliment will eventually bite you in the ass. – Martin York Nov 18 '10 at 19:31
  • Good point Martin. I have a complication though that I know my value is a signed 2s complement number because it's coming from hardware, and due to a library, the value gets stored in an unsigned int. I just want to get it back to a sane type! :) – sourcenouveau Nov 18 '10 at 19:37
  • Why are you looking in the C99 spec, when asking a question about C++? – Steve Jessop Nov 19 '10 at 10:53
  • @Steve Jessop--I'm just more familiar with the C language and was not sure if C++ was the same or different. – sourcenouveau Nov 19 '10 at 13:59
  • @emddudley: oh, I see. In this case they're the same. The equivalent in C++ is 4.7/3: "If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined." – Steve Jessop Nov 19 '10 at 14:02

7 Answers7

20

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • To do the cast without invoking implementation defined behaviour you can use the answer to this: http://stackoverflow.com/questions/13150449/efficient-unsigned-to-signed-cast-avoiding-implementation-defined-behavior – qbt937 Nov 30 '14 at 17:27
3

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.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • So referring to non-pointer (value) types only: which casts will cause the bits to change, and which will not? It sounds like static_cast will sometimes change the bits, but reinterpret_cast will never change the bits. – sourcenouveau Nov 18 '10 at 19:11
  • One's are only prepended if the original value was negative. What actually occurs is *sign extension*; the additional high order bits are filled with the sign bit value of the smaller type. – Clifford Nov 18 '10 at 19:19
  • @emddudley: You've pointed out that casts (really, `static_cast`s) from floating point types to integral types will usually change bits (exception: `0` is all `0` bits in both types). I'm assuming the truncation case (larger bit count to smaller) is also an obvious case where bits change. I don't think `reinterpret_cast` is guaranteed to not change the bits when size changes if it's valid in that case. The problem is that the standard waves the "compiler dependent" flag on this question. @Clifford: Thanks. Fixed. – Mike DeSimone Nov 18 '10 at 19:33
2

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).

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Good grief, does someone still make computers that aren't two's comp? – Crashworks Nov 18 '10 at 20:56
  • @Crashworks, if you don't point out the incredibly rare cases then somebody is sure to give you a downvote. – Mark Ransom Nov 18 '10 at 21:29
  • I don't think that casting from `signed` to `unsigned`, for equal bit counts, on one's complement machines will change the bit pattern either. You'd have to have a format where *nonnegative* integers are represented differently whether `signed` or `unsigned`. – Mike DeSimone Nov 18 '10 at 21:55
  • @Mike DeSimone: for example the signed value `-1`, in ones' complement has bit pattern `111...10`. When converted to unsigned, it has value UINT_MAX, with bit pattern all 1. No implementation represents non-negative integers differently between signed and unsigned (unless you count padding bits) - the standard forbids it. – Steve Jessop Nov 19 '10 at 10:46
  • @crashworks: I don't know. I don't try to remain aware of every single C++ implementation in the world, and I don't care to answer questions about "what can happen in C++?" in a way which could become out of date due to someone porting C++ to a different platform. If the questioner wants to ask about a particular CPU architecture, they're welcome to :-) I don't mind answering in a way that could become out of date due to a revolution in CPU design which sweeps away the current overwhelming use of 2's complement signed integers. – Steve Jessop Nov 19 '10 at 10:49
  • @Steve: I find it startling that, on a one's complement machine, converting a signed value to unsigned also converts it from one's complement (`111...10`) to two's complement (`111...11`) before reinterpreting it as unsigned. I'd expect the result to be UINT_MAX - 1, just as I would expect a cast of -0 to yield UINT_MAX. – Mike DeSimone Nov 19 '10 at 13:20
  • @Mike DeSimone: 4.7/2, "If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2^n where n is the number of bits used to represent the unsigned type)". A cast of -0 yields 0. The point is that integer conversions are defined in terms of the actual values represented, not in terms of the representation. – Steve Jessop Nov 19 '10 at 13:55
1

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));
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

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.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • -1 for incorrect assertion "every computer ... uses 2's complement". E,g, the [Unisys Clearpath](http://en.wikipedia.org/wiki/UNIVAC_1100/2200_series) uses 1's complement. – Cheers and hth. - Alf Nov 18 '10 at 20:32
  • But the 1's complement mode of the Unisys Clearpath is only used for running software written in the dark ages. Which doesn't apply to the OP, who is writing it now. – TonyK Nov 18 '10 at 22:09
0

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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • I am not sure I can understand this, but this seems very interesting though. Do you have a reference, or could you please expand? Thank you. – Benoit Nov 19 '10 at 09:51
  • MSalters, you're the first person to mention this... it's an interesting idea, I'll experiment with it. – sourcenouveau Nov 19 '10 at 13:57
-4

Casting is just a way to override the type-checker, it shouldn't actually modify the bits themselves.

Dylan Lukes
  • 935
  • 7
  • 10
  • C/C++ don't usually use a sign bit for integral values (these are found in floating point numbers). Instead, negative integers are represented using two's complement representation. http://en.wikipedia.org/wiki/Two's_complement – Dylan Lukes Nov 18 '10 at 19:02
  • 2
    int c = (int)1.23 will definitely modify the bits. – The Archetypal Paul Nov 18 '10 at 19:04
  • @Paul sorry, I wasn't thinking about float-->int. That's really more coercing than casting though, no? – Dylan Lukes Nov 18 '10 at 19:10
  • @Paul Additionally, int c = 1.23; is just as valid, since floats and ints can be coerced between with no casting required. – Dylan Lukes Nov 18 '10 at 19:13
  • @Dylan: `int c = 1.23` ia an *implicit* cast, but a cast nonetheless. In two's complement, the MSB is *still* referred to as the sign bit (it has a place value -(2^(n-1)) where n is the number of bits, so if it is 1, the number is *always* negative. – Clifford Nov 18 '10 at 19:25
  • @Dylan Lukes: There is no requirement to use 2 complement. It is quite valid for a compiler to use 1 compliment or a sign bit. – Martin York Nov 18 '10 at 19:30
  • @Dylan, yes, it's valid but I used an explicit cast to make the point clear. And that's the whole question, really - when is casting coercion? – The Archetypal Paul Nov 18 '10 at 20:01