scipilot
scipilot

Reputation: 7477

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

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.

Upvotes: 2

Views: 238

Answers (2)

Some programmer dude
Some programmer dude

Reputation: 409482

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.

Upvotes: 2

Mat
Mat

Reputation: 206929

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.

Upvotes: 5

Related Questions