1

I am trying to read-in two separate, 3 byte messages over CAN, add them together, and send back out over CAN, on a CANBED V1 which uses a Leonardo.

My confusion is using bit shifting.

If I attempt to shift 1 by 1..14 bits I seem to understand,

uint32_t hdbs = 1 << (14) ;
Serial.println(hdbs, BIN);

this codes gives me 100000000000000 in the display window if printing binary or 16384 decimal.

But if I try a bit shift of 15 or more

uint32_t hdbs = 1 << (15) ;
Serial.println(hdbs, BIN);

I am now confused as I get

11111111111111111000000000000000 or 4294934528 in decimal

What basic concept am I missing? I tried using unsigned long, long, etc.,

If I do some simple math and use hdbs = 8 * 4095 I get the correct answer of 32760, but when I try 8*4096 I get 4294934528, which should be 32768 (which seems like would exceed a 16 bit unsigned, but I thought I was declaring hdbs as a 32 bit variable.

Rohit Gupta
  • 618
  • 2
  • 5
  • 18
Greg Smith
  • 19
  • 2

2 Answers2

1

This is really more of a C++ question, but I don't know whether or not you know that. Roughly speaking, in C, C++ (and so "Arduino" language) expressions are evaluated for types in parallel with their values from the inside out following operator precedence rules much like regular algebra.

You must ensure the type on the left-hand-side of the << is already prepared to accept the resulting value. E.g.:

uint32_t hdbs = static_cast<uint32_t>(1) << 15;

uint32_t hdbs = (uint32_t)1 << 15;

uint32_t hdbs = uint32_t(1) << 15;

uint32_t hdbs = uint32_t{1} << 15;

uint32_t hdbs = 1UL << 15;


1 is of type int, which for Leonardo is a 16-bit type. Usually when binary operators like + or / are evaluated for result type, the both sides are taken into account in something called usual arithmetic conversions. For << it just looks at the type of the thing being shifted and takes that. So your result of 1 << 15 is being evaluated in an int, which cannot store that value on the AVR. The fact that you later use that in initializing uint32_t hdbs is beside the point when calculating 1 << 15 itself.

I would have tentatively guessed you'd get 0x8000 which when converted to 32-bit under sign extension would have resulted in 11111111 11111111 10000000 00000000. But, an attempt to overflow a signed integer type like this is undefined. It cannot be reliability expected to produce any particular behaviour, let alone value.

Had you been using 32-bit chip like an ARM or ESP32/ESP8266, this would have just worked because the int type there is 32-bit.

jsotola
  • 1,554
  • 2
  • 12
  • 20
timemage
  • 5,639
  • 1
  • 14
  • 25
0

This is how C type cast works.

C expressions are of signed type by default. If an expression type has too small bit length than it may get also negative due to overflow (into the high bit which is also a sign bit).

Whenever a signed expression needs to fit into more bits, it gets expanded into longer signed value first (which is effectively the same as duplicating the high bit as many times as needed). And only after that, it will be converted to unsigned (which is basically no-op).

So to overcome this thing you need to change cast order by making 1U or 1UL instead of just 1. Then you will never run into signed values and, so, no sign bit expansion.

Matt
  • 176
  • 1
  • 4