user2793162
user2793162

Reputation:

What is happening with signed/unsigned int conversion?

Just double checking. On some tutorial I found such code:

#include <iostream>
using namespace std;

/* This program shows the difference between
 * signed and unsigned integers.
*/
int main()
{
   short int i;           // a signed short integer
   short unsigned int j;  // an unsigned short integer

   j = 50000;

   i = j;
   cout << i << " " << j;

   return 0;
}

output: -15536 50000

Then it explains the output: "The above result is because the bit pattern that represents 50,000 as a short unsigned integer is interpreted as -15,536 by a short."

I think this is a wrong explanation - or it is an English issue? I think the reason negative value is output is that 50000 didn't fit in the 2 byte signed int, am I wrong?

Upvotes: 2

Views: 1634

Answers (6)

Stephen P
Stephen P

Reputation: 14800

An unsigned short is a 16-bit value that doesn't use a sign bit — all values are assumed to be positive.

The decimal value 50,000 stored in binary is

1100 0011 0101 0000
^

The leftmost bit (Most Significant Bit - MSB) is 1 and represents 2^15 which is 32,768

The whole bit pattern is 32,768 + 16,384 + 512 + 256 + 64 + 16 = 50,000

When you cast this to a signed short, the bit pattern is not changed, but the MSB no longer represents 32,768 — it now represents the sign of the number, and the remaining bits are the 2's-complement of the value. In a signed value, that top bit is the sign, and 1 is negative.

This doesn't happen with 30,000 because that is

0111 0101 0011 0000
^

When this is converted to signed the left-most 0 represents the sign, and 0 is positive so the rest of the bits are still interpreted as they are, not as a 2's-complement, and they therefore still represent the same value of 30,000

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145269

First, integral types are required to be represented using a pure binary system, and so far the tutorial is correct.

Second, a short is required to be at least 16 bits. If it's more, then you won't see the effect that you did, or any effect. It's unclear from your description whether the tutorial blindly assumes that short is necessarily 16 bits (wrong), or whether it's just using some concrete example, with the understanding that it depends on compiler etc.

Third, the conversion to signed type … is formally Implementation Defined Behavior if the value cannot be represented. This means that you are not guaranteed a change of value. Instead you can, in principle, get any effect, such as a crash.

[example of other behavior lacking because I'm unable to cajole g++ 4.8.2 into trapping for your example code, even with -ftrapv]

… yields a value that's either the same, if it can be represented, or otherwise defined by the implementation.

That said, C++ guarantees that unsigned arithmetic is performed modulo 2n, where n is the number of value representation bits, e.g. 16 in your example. And with the very common two's complement form representation of signed integers, a negative integer value -x is represented as the bitpattern for -x + 2n. So if you start with the latter value (the interpretation of the bitpattern as unsigned) as 50 000, with 16 value bits and two's complement form, you get the signed value 50 000 - 216 = 50 000 - 65 536 = -15 536

Upvotes: 1

WhozCraig
WhozCraig

Reputation: 66194

The reasoning in the explanation is perhaps accurate for a specific platform, but in my opinion its irrelevant to what is ultimately printed. It would be far more accurate to say

"The above result is because the bit pattern returned from an implementation-defined conversion of an unsigned short int value of 50000 to a signed short int results in a value of -15,536."

Not shockingly, sending said-value to std::cout will produce the proper output for the signed short int result of said-value. The source of the change (the conversion) is important, and in this case it is implementation-defined. Their phrasing is weak, and the identical statement could be applied by assigning any value to any integral value, so in reality their explanation is ultimately pointless.


Not wanting to waste the previous answer (before I understood the question better), enjoy some light reading. To know for sure why this is happening you must consult your implementation documentation for conversion of this nature. Not the answer you likely want to hear, but there is reason behind it.

This is happening because of value promotion via integer conversion rank. The value you received is, in fact, implementation-dependent, and the specific reason for why are covered in the standard.

I'll leave out the most basic stuff and just get to the meat of it:

C++11 §4.7 Integral promotions [conv.integral]

  1. A prvalue of an integer type can be converted to a prvalue of another integer type. A prvalue of an unscoped enumeration type can be converted to a prvalue of an integer type.

  2. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note ]

  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.

  4. If the destination type is bool, see 4.12. If the source type is bool, the value false is converted to zero and the value true is converted to one.

  5. The conversions allowed as integral promotions are excluded from the set of integral conversions.

The value 50000 cannot be represented in a signed short int on your platform. Therefore the results are implementation-defined. By the looks of it your implementation just stores the bytes from one to the other, the result being the sign-bit (also covered in the standard but left out for brevity) is lit and the reported number is negative. Remember, though, this is implementation defined and you cannot rely on this result on all platforms.

Upvotes: 3

user3386109
user3386109

Reputation: 34829

You are correct and the book is also correct. An unsigned short can have values from 0 to 65535. A signed short can have values from -32768 to 32767. Therefore, any value between 0 and 32767 works for both signed and unsigned.

However, a number like 50000 is too big for a signed short, so when you assign 50000 to a signed short, it causes numeric overflow.

Upvotes: 0

ChengDuum
ChengDuum

Reputation: 77

50,000 is represented in binary as - 1100 0011 0101 0000.

In a signed bit, the leftmost bit is the sign. In a signed integer, a '0' would represent a negative(hence the -15536), where in an unsigned integer this would make no difference. As for why the number itself changed, I have no idea.

Upvotes: 0

pm100
pm100

Reputation: 50180

your answer and the books answer are correct

50000 = 0xc350

a signed 16 bit short containing 0xc350 is interpreted as -15,536

So they are correct (interpretation of bit pattern)

if i was a 32 bit int then putting 0xc350 in it would be interpretted as 50,000

so you are correct (I too small)

Upvotes: 3

Related Questions