I am working on a project where I need to get a range of signed 16-bit ints, negative and positive values, and send them to a function to analyse during unit tests.
For different reasons, the function only takes an array of unsigned 16-bit ints, so I need to store the signed ints in an unsigned 16-bit integer array and completely preserve the same bit pattern. I am using gcc (Debian 8.3.0-6) 8.3.0.
unsigned short arr[450];
unsigned short arrIndex = 0;
for (short i = -32768; i < (32767 - 100) ; i = i + 100 )
{
arr[arrIndex] = i;
printf("short value is : %dn", i);
printf("unsigned short value is : %dn", arr[arrIndex]);
arrIndex++;
}
Even tho i’m telling printf to print signed values, I am surprised to see that the bit patterns are actually different for those values less than zero. The first few values are below:
short value is : -32768
unsigned short value is : 32768
short value is : -32668
unsigned short value is : 32868
short value is : -32568
unsigned short value is : 32968
What is happening here, and how would I preserve the bit pattern for the values of i below zero?
5
Answers
In C, if you call a variadic function and pass in an integral type of any sort, the language will automatically promote it to a signed or unsigned
int
of the same type. When you then print things out using the%d
modifier, you’re seeing the promoted int as a result.For example, when you call
The (negative) value of
i
is getting promoted to asigned int
with the same value, which is why it prints out as negative. When you then callThe (unsigned) value of
arr[arrIndex]
gets promoted to anunsigned int
, which is why you see the positive value displayed.To fix this, change your
printf
so that you tell the compiler to display the results specifically asshort
variables:Now, you’ll see the values agreeing.
The data is copied correctly, bit-by-bit as you wanted. It’s the just the printing that displays it as a signed value because
arr
is declared as an array of unsigned values.%d
prints the data passed asint
s (by standard definition? not sure), which on common platforms is 4-bytes. The argument passed toprintf
is upgraded to anint
before being printed, which, depending on whether the argument in question is signed or not, will require sign-extension or not.When printing
i
, which is a signed value, the value will be sign-extended before being printed. For example, ifi
is-1
(which, is represented as0xFFFF
on a 2-byte signed value using two’s complement), theni
will be upgraded as theint
value0xFFFFFFFF
(which also is-1
, but represented with four bytes).However, if
i
is equal to-1
, then, when doingarr[arrIndex] = i
,arr[arrIndex]
will indeed be set to0xFFFF
, copied bit-by-bit as you wanted. However, sincearr[arrIndex]
is unsigned, in the world of the unsigned,0xFFFF
represents65535
. Then, when the time comes to printarr[arrIndex]
, sincearr[arrIndex]
is unsigned, the value will not be sign-extended, since it is an unsigned value.0xFFFF
would therefore be upgraded to0x0000FFFF
, which is equal to65535
, and printed as such.We can verify this by forcing
arr
to be considered signed before being printed. That way,arr
will be treated the same wayi
is treated.Output:
Or, we could directly declare
arr
as an array of signed values to achieve the same result:With the very common 2’s complement encoding the following is sufficient.
BITD day with ones’ complement and sign-magnitude, this was not sufficient and code would use a
union
ofshort
andunsigned short
.Yet by virtue of how a negative value 2’s complement is converted to unsigned, the bit pattern is preserved for same sized types.
The bit patterns are the same. They went through different conversion paths to get printed and so have different text output.
When printing
short, unsigned short
, best to use a h printf modifier.The values are being copied correctly. Let’s look at the following code:
Here I’m created four signed short variables, and setting them to bit patterns. Forget “positive” and “negative” for a moment – I’m just shoving a bit pattern into those variables. In the subroutine
printit
those values are printed as signed decimal, unsigned decimal, and hex (to verify it’s all the same bit pattern). Now, look at the results:Now you can see that I just copied the values you used (-32768, -32668, and -32568) and assigned them to the variables. The only difference is that I converted them to hexadecimal first. Same bit pattern. Same results. But, except in a few rare cases, the signed decimal value interpretation of a bit pattern where the decimal value is negative is NOT the same as the unsigned decimal interpretation of a bit pattern. I suggest reading up on One’s Complement for binary numbers, and Two’s Complement representation of negative binary numbers.
Please, check the
for
loop limits, as if you go from-32768
to<(32767-100)
in leaps of 100 values, you fill 655 array elements, and you have only declared 450.Also, to print an
unsigned short
value, you need to use%u
(or the equivalent%hu
, asshort
s are converted toint
forprintf()
usage) format specifier.Use this example:
It will produce: