mysterious add

hi,
what is going on here?

A very simple program:

byte ascii2num(char c) {
  // wandelt eine Hexadezimalzahl in die entspr. Dezimalzahl um
  if (isdigit(c)) return(byte(c)-48);
  else return(byte(c)-55);
}

void setup() {
  Serial.begin(19200);

  Serial.println(F("sketch_testDecod"));
  Serial.print("F=");Serial.print(ascii2num('F'));  // test ascii2num()
  Serial.print("  5=");Serial.println(ascii2num('5'));
  
  Serial.print(ascii2num('F')<<4);
  Serial.print("+");Serial.print(ascii2num('A'));
  Serial.print("=");Serial.print(ascii2num('F')<<4 + ascii2num('A'));
  Serial.print("=");Serial.println((ascii2num('F')<<4) + ascii2num('A'));
  
}

The result on Serial Monitor is:

sketch_testDecod
F=15  5=5
240+10=-16384=250

I don't understand this, why -16384? Anyway the compiler seems to change the sequence of processing?
Can somebody explain it to me?

SupArdu

Rule number 1 to keep C readable, NOTHING follows a ; :wink:

And next, -16384 = 0b1100000000000000. So that's 4 + 10 zero's or a shift of 14 of 00001111 casted to an signed int. + has a higher Operator Precedence than shifting so is done first.

Also, what is this?

byte(c)

Is it a cast? If so, not a syntax I've seen before. Could be valid, but much more common is:

(byte)c

"byte()" is an arduino function.

Danois90:
"byte()" is an arduino function.

Ahhh...thanks.
Do you happen to know in which file it's defined?

Danois90:
"byte()" is an arduino function.

No, it’s a C style functional cast.

That kind of confusion is partly why explicit casting is done like this in modern C++:

static_cast<byte>(c)

what is going on here?

Let us re-write the program of OP as follows for the shake of clarity (not executable):

byte ascii2num(char c) 
{
  // wandelt eine Hexadezimalzahl in die entspr. Dezimalzahl um
  if (isdigit(c)) 
    return(byte(c)-48);
  else 
    return(byte(c)-55);
}

void setup() 
{
  1. Serial.begin(19200);
  2.
  3. Serial.println(F("sketch_testDecod"));
  4. Serial.print("F=");
  5. Serial.print(ascii2num('F'));  // test ascii2num()
  6. Serial.print("  5=");
  7. Serial.println(ascii2num('5'));
  8.
  9. Serial.print(ascii2num('F')<<4); //
  10. Serial.print("+");
  11. Serial.print(ascii2num('A'));
  12. Serial.print("=");
  13.
  14. Serial.print(ascii2num('F')<<4 + ascii2num('A'));
  15. Serial.print("=");
  16. Serial.println((ascii2num('F')<<4) + ascii2num('A'));
}

a. The ascii2num() function is returning 8-bit unsigned value (byte) to the calling program.
b. In Line-9, we are receiving 15 (00001111). When it is shifted to the left by 4-bit, we get 1111000, which is still of type byte. So, the print function prints a positive value of 240 (F0h = 15x16 + 0x0 = 240).

c. In Line-14, all the mysteries are happening; Post#1 has briefly exposed it; we will dig a little bit deeper here. We are expecting that the print function will show 245760; but it is showing -16384. How?

(1) ascii2num('A') returns +10 (00001010). // byte x1 = 0x0A; 8-bit
(2) ascii2num('F') returns +15 (00001111) // byte x2 = 0x0F; 8-bit
(3) number of shifts to be made to the left = x1 + 4 = 14
(4) If doing by manual calculation, we expect that x2 will take the following bit pattern after being shifted to the left by 14-bit:
0000 1111 00 0000 0000 0000 = (unsigned) 3C000h = 245760.

(5) The value in Step-(4) is 18-bit (forget the leading 0s). The compiler chooses an identifier (x3) of 32-bit wide of type long and not unsigned long to hold the value. Now, it is the hardware circuit of the processor (or the compiler I am not sure) populates the upper 16-bit of x3 with the sign bit (1) of the lower 16-bit of x3.

Now, long x3 = 0b1111 1111 1111 1111 1100 0000 0000 0000 (0xFFFFC000).

The print function, for good reason, treats x3 as signed number, and it prints:
-16384 (-1x215 + 1x214 + 0 + ... + 0 = -32768 + 16384 = -16384).

(6) We may view Line-14 as follows:

byte x1 = ascii2num('A');  // x1 = 0x0A
byte x2 = ascii2num('F'); // x2 = 0x0F
x1 = x1 + 4;                  // x1 = 14
long x3 = x2 << x1;      // x3 = 1111 1111 1111 1111 1100 0000 0000 0000 (sign bit extended)
                                   //casted to an signed int. as has been told in Post#1).

Serial.println(x3);          //prints: -16384