1

As far as I know a char is an 8 bit variable

char 0x80 == 0xFFFFFF80

void setup() {
  Serial.begin(9600);
}

void loop() {
  char x = 0x80;
  if(x != 0x80){
    Serial.println(x, HEX);
  }

  delay(1000);
}
4

To illustrate the reasons better let's look at the actual content of the data.

0x80 is 0b10000000 in binary. A char is a signed variable, which means that the most significant bit is the sign bit, with two's complement representation

So in 8-bit signed representation the value 0b10000000 is a sign bit and zero value - which is -128 in decimal notation, which is "-128 + the 7 least significant bits", or -128 + 0 in this case.

When you print a signed value on the Arduino as HEX there is no 8-bit specific function for it. Neither is there a 16 bit function. However there is a 32-bit signed function, which is the closest match available. So the compiler performs type promotion to 32-bit signed, then represent it as hexadecimal.

Now -128 in signed 32-bit is calculated in the same way - take the value of the most significant bit, negate it, and add the value of all the other bits to it. With 32-bits the value of the most significant bit, in decimal, is 2147483648, so negate that into -2147483648. Now we need to make that into -128, so we need the difference between the two to add to it. That's 2147483520.

So you get the MSB + 2147483520, which in hexadecimal, is 0xFFFFFF80.

Of course, the compiler doesn't go through that whole routine - instead it uses something called sign extension. This method basically means copying the sign bit from the original smaller (char) type into every bit to the left of it in the larger type:

                           10000000 8 bit signed
                  11111111 10000000 16 bit signed
11111111 11111111 11111111 10000000 32 bit signed

All those are -128 in two's complement, just stored in different size variables. So when you come to print it, that is just what you get - the two's complement representation of -128 in the variable size that has been accepted by the print function.

You might find this site useful for experimentation and furthering your understanding of variable data storage.

2

char is signed so 0x80 is really -0x80 (==-128). When promoted to a higher width the sign gets copied into the new bits 0xffffff80 (still ==-128).

if you put unsigned in front of char you won't get that issue (though may encounter others).

void setup() {
  Serial.begin(9600);
}

void loop() {
  unsigned char x = 0x80;
  if(x != 0x80){
    Serial.println(x, HEX);
  }

  delay(1000);
}
2

Sign extension. Try this:

void loop() {
  char x = 0x80;
  if(x != (char) 0x80){
    Serial.println(x, HEX);
  }

Before any operation involving different types, the lower ones must be promoted to higher ones.

The constant 0x80 is an integer value, so x must be promoted to int before the test. The first bit of an int value is the sign bit. Sign bit is extended (copied to the left side bits). So x become 0xFFFFF80 and then you compare it against 0x0000080.

Every time you put a char where an int is excepted the same thing happen.

See? Programming is hard, programming in C is harder, programming in C++ is the hardest.

C is full of traps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.