質問

I'm trying to do, what I imagined to be, a fairly basic task. I have two unsigned char variables and I'm trying to combine them into a single signed int. The problem here is that the unsigned chars start as signed chars, so I have to cast them to unsigned first.

I've done this task in three IDE's; MPLAB (as this is an embedded application), MATLAB, and now trying to do it in visual studio. Visual is the only one having problems with the casting.

For an example, two signed chars are -5 and 94. In MPLAB I first cast the two chars into unsigned chars:

unsigned char a = (unsigned char)-5;
unsigned char b = (unsigned char)94;

This gives me 251 and 94 respectively. I then want to do some bitshifting and concat:

int c = (int)((((unsigned int) a) << 8) | (unsigned int) b);

In MPLAB and MATLAB this gives me the right signed value of -1186. However, the exact same code in visual refuses to output results as a signed value, only unsigned (64350). This has been checked by both debugging and stepping through the code and printing the results:

printf("%d\n", c);

What am I doing wrong? This is driving me insane. The application is an electronic device that collects sensor data, then stores it on an SD card for later decoding using a program written in C. I technically could do all the calculations in MPLAB and then store those on the SDCARD, but I refuse to let Microsoft win.

I understand my method of casting is very unoptimised and you could probably do it in one line, but having had this problem for a couple of days now I've tried to break the steps down as much as possible.

Any help is most appreciated!

役に立ちましたか?

解決

The problem is that an int on most systems is 32-bits. If you concatenate two 8-bit quantities and store it into a 32-bit quantity, you will get a positive integer because you are not setting the sign bit, which is the most significant bit. More specifically, you are only populating the lower 16 bits of a 32-bit integer, which will naturally be interpreted as a positive number.

You can fix this by explicitly using as 16-bit signed int.

#include <stdio.h>
#include <stdint.h>

int main() {
    unsigned char a = (unsigned char)-5;
    unsigned char b = (unsigned char)94;
    int16_t c = (int16_t)((((unsigned int) a) << 8) | (unsigned int) b);
    printf("%d\n", c);
}

Note that I am on a Linux system, so you will probably have to change stdint.h to the Microsoft equivalent, and possibly change int16_t to whatever Microsoft calls their 16-bit signed integer type, if it is different, but this should work with those modifications.

他のヒント

This is the correct behavior of the standard C language. When you convert an unsigned to a signed type, the language does not perform sign extension, i.e. it does not propagate the highest bit of the unsigned into the sign bit of the signed type.

You can fix your problem by casting a to a signed char, like this:

unsigned char a = (unsigned char)-5;
unsigned char b = (unsigned char)94;
int c = (signed char)a << 8 | b;
printf("%d\n", c); // Prints -1186

Now that a is treated as signed, the language propagates its top bit into the sign bit of the 32-bit int, making the result negative.

Demo on ideone.

Converting an out-of-range unsigned value to a signed value causes implementation-defined behaviour, which means that the compiler must document what it does in this situation; and different compilers can do different things.

In C99 there is also a provision that the compiler may raise a signal in this case (terminating the program if you don't have a signal handler). I believe it was undefined behaviour in C89, but C99 tightened this up a bit.

Is there some reason you can't go:

signed char x = -5;
signed char y = 94;
int c = x * 256 + y;

?

BTW if you are OK with implementation-defined behaviour, and your system has a 16-bit type then you can just go, with #include <stdint.h>,

int c = (int16_t)(x * 256 + y);

To explain, C deals in values. In math, 251 * 256 + 94 is a positive number, and C is no exception to that. The bit-shift operators are just *2 and /2 in disguise. If you want your value to be reduced (mod 65536) you have to specifically request that.

If you also think in terms of values rather than representations, you don't have to worry about things like sign bits and sign extension.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top