How can shift left << give different results in different functions?

2

I have been debugging a problem in a C++ library, and found a strange difference between literal 0 and a variable set to 0 but only inside the library. If I copy the code out into my own code it works as expected.

The library code is shifting a 1 down a long integer to mask off one bit at a time. When sending a certain code (REPEAT key) you send a kind of dummy data packet, and "no bits" so it transmits the headers and no data. I was assuming the for-loop would skip the loop if there were no bits to send.

I have simplified it down to break apart the assignment, and added the following debugging. I'm just repeating the steps for both nbits and the literal 0 I added.

void  IRsend::sendNEC (unsigned long data,  int nbits){

    Serial.println(data, HEX);
    Serial.print(" nbits = ");
    Serial.println( nbits);
    if(nbits == 0) Serial.println("nbits == 0"); else Serial.println("nbits != 0 "); 
    Serial.print(" 0 - 1 = ");
    Serial.println(0 - 1);
    Serial.print(" nbits - 1 = ");
    Serial.println(nbits - 1);
    Serial.print(" (1UL << (0 - 1) = ");
    Serial.println(1UL << (0 - 1));
    Serial.print(" (1UL << (nbits - 1) = ");
    Serial.println(1UL << (nbits - 1));
    ...

    // the part I was originally debugging:
        for (unsigned long  mask = 1UL << (nbits - 1);  mask;  mask >>= 1) {
    ...
}

called like:

  irsend.sendNEC(0xFFFFFFFF, 0);

This surprisingly gives the following output:

FFFFFFFF
 nbits = 0
 nbits == 0
 0 - 1 = -1
 nbits - 1 = -1
 (1UL << (0 - 1) = 0
 (1UL << (nbits - 1) = 1

How can the last two results be different? nbit is 0, so why does literal 0 shift differently when they both end up as -1 (I added the extra parentheses to be sure)?

And why, when I copy the exact same debug code into my code which calls that library, and call a test function like:

void runtest(unsigned long data,  int nbits){
/// same debug ... 
}
void setup() {
  runtest(0xFFFFFFFF, 0);
}

I get the expected output:

-- Same test again --
FFFFFFFF
 nbits = 0
nbits == 0
 0 - 1 = -1
 nbits - 1 = -1
 (1UL << (0 - 1) = 0
 (1UL << (nbits - 1) = 0

I can only guess that 0 == 0 exactly, e.g. if its defaulting to a longer type 0?

This is using the Arduino IRRemote library: https://github.com/z3t0/Arduino-IRremote/blob/master/ir_NEC.cpp

Its running on an ATmega328, I'm building on MacOSX presumably with avr-gcc in the Arduino 1.8.10 IDE.

c++
bitwise-operators
bit-shift
asked on Stack Overflow Dec 3, 2019 by scipilot • edited Dec 3, 2019 by scipilot

2 Answers

4

From a draft C++ standard expr.shift:

The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the width of the promoted left operand.

Your code has undefined behavior, so anything can happen.

answered on Stack Overflow Dec 3, 2019 by Mat
2

From this shift reference

... if the value of the right operand is negative or is greater or equal to the number of bits in the promoted left operand, the behavior is undefined

[Emphasis mine]

You're not allowed to have negative shifts, it's undefined behavior.


User contributions licensed under CC BY-SA 3.0