简体   繁体   中英

Bitwise shift unsigned long on Arduino

Why does the following not work on Arduino?

unsigned long test = 1 << 20;

I've tested the bit shifting using the following sketch.

void setup() {

  Serial.begin(9600);

  unsigned long test = 0;

  for(int i=0; i<32; i++)
  {
    test = 1 << i;
    Serial.print("i:");
    Serial.print(i);
    Serial.print(" dec:");
    Serial.println(test);
  }
}

void loop() {

}

Which gives me the following output:

i:0 dec:1
i:1 dec:2
i:2 dec:4
i:3 dec:8
i:4 dec:16
i:5 dec:32
i:6 dec:64
i:7 dec:128
i:8 dec:256
i:9 dec:512
i:10 dec:1024
i:11 dec:2048
i:12 dec:4096
i:13 dec:8192
i:14 dec:16384
i:15 dec:4294934528   <--- ???
i:16 dec:0            <--- ???
i:17 dec:0            <--- ???
i:18 dec:0            <--- ???
i:19 dec:0            <--- ???
i:20 dec:0            <--- ???
i:21 dec:0            <--- ???
i:22 dec:0            <--- ???
i:23 dec:0            <--- ???
i:24 dec:0            <--- ???
i:25 dec:0            <--- ???
i:26 dec:0            <--- ???
i:27 dec:0            <--- ???
i:28 dec:0            <--- ???
i:29 dec:0            <--- ???
i:30 dec:0            <--- ???
i:31 dec:0            <--- ???

What happens at bit 15 and ongoing??

Testing the whole code in Xcode gives me the expected output.

int seems to be only 16 bit wide on your target machine. 1 is an int , therefore shifting it by more than 15 bits invokes undefined behavour. The solution is simple, you should use a long constant:

 unsigned long test = 1UL << 20;

The language you write in is not exactly C, but this solution should still be correct.

Incidentally, 1 << 40 invokes undefined behaviour if int is 32 bits. Below is a simple test:

#include <stdio.h>

int a = 1, b = 40;

int main() {
    printf("1 << 40 = %d\n", 1 << 40);
    printf("1 << 40 = %d\n", 1 << 40);
    printf("1 << 40 = %d\n", 1 << 40);
    printf("%d << %d = %d\n", a, b, a << b);
}

On OS/X with clang , I get this output:

~/dev/stackoverflow > make t42
clang -O3 -Wall -o t42 t42.c
t42.c:6:32: warning: shift count >= width of type [-Wshift-count-overflow]
    printf("1 << 40 = %d\n", 1 << 40);
                               ^  ~~
t42.c:7:32: warning: shift count >= width of type [-Wshift-count-overflow]
    printf("1 << 40 = %d\n", 1 << 40);
                               ^  ~~
t42.c:8:32: warning: shift count >= width of type [-Wshift-count-overflow]
    printf("1 << 40 = %d\n", 1 << 40);
                               ^  ~~
3 warnings generated.
~/dev/stackoverflow > ./t42
1 << 40 = 1386850896
1 << 40 = 256
1 << 40 = 512
1 << 40 = 256
~/dev/stackoverflow > ./t42
1 << 40 = 1477618256
1 << 40 = 256
1 << 40 = 512
1 << 40 = 256

clang warns the programmer about the problem and insists on generating undefined behaviour, with consistently inconsistent output. Amazing isn't it? A good example of why one should never ignore compiler warnings

You are shifting 1 which is a 16-bit int . This works up to 16384 but 32768 is -32768 or 0x8000 which is then sign-extended when assigned to unsigned long as 0xFFFF8000 , which is 4294934528 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM