11

Maybe I do not understand C++ properly or is it a compiler's bug?

uint8_t a = 0x00;
uint8_t b = 0xFF;

if( a - b == 1 )
{
    doNothing();
}

doNothing is not called (as expected), because result of (a-b) was implicitly casted to the type of second operand in compare operation. And for numbers it's signed int. Okay.

if( a - b == (uint8_t)1 )
{
    doNothing();
}

doNothing STILL isn't called, but now I do not understand the reason for it! I have explicitly casted the number to uint8!

if( (uint8_t)(a - b) == 1 )
{
    doNothing();
}

Now doNothing is finally called, but again why? How can subtraction of two uint8 return an int?

Compiler is uVision ARMCC for ARM Cortex M3.

Eric Postpischil
  • 141,624
  • 10
  • 138
  • 247
Amomum
  • 5,447
  • 5
  • 27
  • 54
  • See this question for a more thorough explanation of the casting rules: http://stackoverflow.com/questions/5563000/implicit-type-conversion-rules-in-c-operators – Dale Wilson Sep 26 '13 at 17:38
  • 3
    As I recall, Keil C is not fully conforming, and people have experienced considerable confusion because of it. In this case, though, it seems to be following the standard. BTW, why is this question tagged both C and C++? Which language are you using? – Keith Thompson Sep 26 '13 at 17:46
  • @Keith Thompson, I don't know language standarts that well and I thought that implicit casting rules could be different. So I checked it in both C and C++ (and for this bit it's the same). – Amomum Sep 26 '13 at 18:03
  • @DaleWilson, I couldn't find anything about chars in there. – Amomum Sep 26 '13 at 18:07
  • @Amomum exactly -- everything is automatically promoted to (at least) an int. – Dale Wilson Sep 26 '13 at 18:37
  • Not the issue, but there is no such thing as an implicit cast. A cast is something you write in your source code to tell the compiler to do a conversion. A conversion that results from a cast is an explicit conversion. A conversion that the compiler does without a cast is an implicit conversion. – Pete Becker Sep 26 '13 at 19:19
  • @PeteBecker oh, I just thought that cast and conversion are synonims. Thanks for clearing that. – Amomum Sep 28 '13 at 11:54
  • @Amomum, And by the way, even snippet #3 works, it is Undefined behavior, and you shouldn't dependency on it. – ZijingWu Sep 29 '13 at 08:52
  • @ZijingWu, why is it undefined exactly? I can not cast signed int to uint8? – Amomum Sep 30 '13 at 12:19
  • 1
    @Amomum, sorry my fault I just checked the standard, it is defined and result of the cast is 1. – ZijingWu Oct 01 '13 at 14:07

3 Answers3

12

In a - b, the operands are promoted to int before the subtraction, so the result is -255, not 1.

That's why both the first and second examples fail; it's nothing to do with the other operand of ==. The third converts -255 back to uint8_t, reducing it modulo 256, so the result is 1 as expected.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
2

well I'm not the best when it comes to mathematics nor hex but it seems that a = 0 and b = 255 so it equals -255 not 1.

Farouq Jouti
  • 1,605
  • 8
  • 14
1

ARM Cortex M3 is a 32 bit processor. So result of a - b is 0xFFFFFF01 which is not equal to 1 (1 ==> 0x00000001 in 32 bit representation) so doNothing() function is not called!

In case 2, if you typecast 1 to uint8_t, which is 0xFFFFFF01 is not equal to 0x01 so doNothing() function not called again!

In case 3, when you typecast a - b output to uint8_t then a - b result is 0x01 which is equal to 1, hence doNothing is called.

Basavaraju B V
  • 174
  • 1
  • 8
  • But why result of subtraction of two uint8_t is not uint8_t by default? – Amomum Sep 26 '13 at 18:04
  • because of the 32 bit processor! in 32 bit processor all variables will of length 32 bit! by using uint8_t to a variable you will use only the lower 8 bits of that variable remaining 24 bits will be 0s. to make it understand in a easier way uint8_t typecast is equivalent of masking the 32 bit variable with (0x000000FF). in 32 bit process all the variables or constants is 32 bits in lenght! uint8_t will just tells how many bits you will use in those 32 bits! in your case a - b ==> 0x00000000 - 0x000000FF will results in 0xFFFFFF01. so if you typecast to uint8_t then result will be 0x00000001. – Basavaraju B V Sep 26 '13 at 18:18
  • 1
    @Amomum Because the standard says so. Types smaller than `int` are promoted to `int` (if they fit; otherwise to `unsigned`) before any binary operations. – James Kanze Sep 26 '13 at 18:23
  • I just do not understand, why result of this operation is not automatically casted back to uint8_t, why higher bits are not masked away. Just seems strange to me. – Amomum Sep 26 '13 at 18:23
  • @JamesKanze, well, that's pretty much explains it. And even when I explicitly casted (uint8_t)1 - it still was casted to int? – Amomum Sep 26 '13 at 18:24