Please help me understand this segment of code and bit shifting

I'm modifying a library and want to make sure I understand the code correctly.

I know that bitshift left means shifting a bit a spot to the left. So LED_POWER = (1 << 5) means take the number 1 and shift it five spots to the left. i.e. 00000000 becomes 00010000.
And I know that a "normal" unsigned int is 16 bits so if I want more than 16 variables, or to be able to shift more than 16 spots to the left, then I need to use an unsigned 32bit int.

What I don't understand is why this section of code compiles:

enum KeyboardLeds : uint32_t {
LED_NUM_LOCK	= (1 <<	0),
LED_CAPS_LOCK	= (1 <<	1),
LED_SCROLL_LOCK	= (1 <<	2),
...
LED_SURROUND_FIELD_ON	= (1 <<	14),
LED_REPEAT	= (uint32_t)(1 << 15),
LED_STEREO	= (uint32_t)(1 << 16),
LED_SAMPLING_RATE_DETECT	= (uint32_t)(1 << 17),
LED_SPINNING	= (uint32_t)(1 << 18),

but this one fails with the error message: enumerator value -32768 is outside the range of underlying type 'uint32_t {aka long unsigned int}' LED_REPEAT = (1 << 15).

enum KeyboardLeds : uint32_t {
LED_NUM_LOCK	= (1 <<	0),
LED_CAPS_LOCK	= (1 <<	1),
LED_SCROLL_LOCK	= (1 <<	2),
...
LED_SURROUND_FIELD_ON	= (1 <<	14),
LED_REPEAT	= (1 << 15),
LED_STEREO	= (1 << 16),
LED_SAMPLING_RATE_DETECT	= (1 << 17),
LED_SPINNING	= (1 << 18),

My understanding of the code is that it creates a 32bit unsigned integer (or is it an array?) called KeyboardLeds. I was then expecting each one of the LED_WHATEVER variables to also be a 32bit unsigned int. However the top code segement compiles but the bottom one does not. Does that mean that in the top segment the variables LED_NUMLOCK through to LED_SURROUND_FIELD_ON are 16bit unsigned ints and from LED_REPEAT onward are 32bit usigned ints? Whereas in the bottom segment they are all 16 bit unsigned ints? Is there any problem having both 16 and 32 bit variables in the top segment or should I convert them all to uint32_t?

No. It means unless you say otherwise, integer constants are 16 bits. So it doesn't matter (works) until you shifting into what should be a larger size.

The top code uses a cast; I believe that using 1L would also work.

EDIT: Yes, just tried that so I am now confirmed in my understanding.

I think you will find a preference for using the 1L long constant as a matter of style.

And in this case, go ahead and use 1L for all the elements of the enum.

a7

Can you explain that in non-programmer terms? I don't understand what you mean by 1L or cast which makes me suspect there's more in your reply that I don't understand.

Oh, sorry.

When you write, say

x = 1;

The 1 is a constant.

There are many ways to express the value of a constant in C.

1L just means a long constant whose value is 1.

As for cast, best if you google or consult your favorite source, or wait for someone else. I know what I need to but am not the one to try to 'splain it to you. :expressionless:

Google "constants in C", here's one I just did, more than you will ever know, certainly more than do I.

https://www.tutorialspoint.com/cprogramming/c_constants.htm

Actually that tutorialspont will do for casts also, see

https://www.tutorialspoint.com/cprogramming/c_type_casting.htm

HTH

a7

1 Like

Your explanation of what a bitshift does seems okay, but your example is incorrect.

(1 << 5) means take the binary number 00000001, not 00000000, and shift all bits 5 positions to the left. So the end result would be 00100000 not 00010000. I think that's the example you were thinking of, since bitshifting a zero value will always equal zero.

1 Like

In your case you would be using 1UL for unsigned long.

Cast means treat this number temporarily as another type of variable.

I think I'm clear on bit shifting, thanks @nicolajna, but I'm still not sure I fully understand the rest of the code.

If I've got it right LED_SURROUND_FIELD_ON = (1 << 14) creates a 16 bit signed integer then bit shifts it, whereas LED_REPEAT = (uint32_t) (1 << 15) creates a 32 bit unsigned integer.

So what is the purpose of the first line
enum KeyboardLeds : uint32_t { ?

It is an optional part of the definition of your enum, in this case to ensure that they are represented by unsigned long integers.

So all the enum elements are unsigned long integers. While value you are stating as well.

Presumably other parts of the code you found this in will be making use of the values as unsigned long ints.

Many uses of enum do not involve caring or knowing about how large an integer is used to represent them, nor their values.

a7