r/C_Programming • • 4d ago

Question Using 1<<N "just for data size"

Hi, I was reading through some code and found something interesting.

Personally, I usually do this when I need to specify an array length (or declare a size):

#define MAXLEN 4096

but when I read some styles there are some lines like this.

/* max length is 2^12; little endian */
#define MAXLEN (1<<12)

If bitwise operations aren't actually involved, I'm not really sure why some programmers write 1<<N just to represent 2N. Although it definitely make sense in low-level programming (or memory-constrained environments), I don't see any real advantage to using 1<<N when resource is plenty. It's harder to read, and programmers like me even need a calculator just to figure out the decimal value.

How do you think about using 1<<N when it's not strictly required? Is it just a matter of coding style?

P.S) Sorry for poor English.

12 Upvotes

47 comments sorted by

72

u/zhivago 4d ago

It shows a clear intent to specify a power of 2.

Remember that bitwise operations are all just arithmetic operations.

Use it where it powers of two have significant meaning. :)

43

u/The_Drakeman 4d ago

I prefer the << syntax when I'm using enums that are intended to be bitwise-or'd together

    enum Foo
    {
        Option1 = 1 << 0,
        Option2 = 1 << 1,
        Option3 = 1 << 2,
    }

and so on. It makes it really easy to see that your math is right, and after doing this enough times it's just easier for me than trying to remember what 8192 * 2 is when I go to add the next entry, and even more so after that.. After I do this enough times, it just kind of becomes the default way of declaring things across the codebase whenever I need a power of 2. The compiler should be resolving all those bit shifts at compile time anyway so there's no runtime hit, and when you're trying to read and edit code quickly, eventually you just learn to work with the bit shift syntax a little faster. I think the improvement is to readability in certain codebases, more than anything else. Rarely is the decimal value actually important to understand, but communicating that it is a power of 2 is, so 1 << something communicates that very quickly and easily.

19

u/irqlnotdispatchlevel 4d ago

I like to extract this in a macro so then you can read it as Option1 = BIT(0),.

3

u/Whole-Low-2995 3d ago

Good, I'll consider this

2

u/tux2603 2d ago

Oooh that's good

22

u/TheThiefMaster 4d ago

It's a matter of intent. If you're specifically indexing a list with a 12-bit number, you should use the bitwise form as the "12" is the important part.

If it's an arbitrary number, it should just be a number.

The bitwise form is also less prone to typos - consider 1<<17 vs 131072 - the former is obviously a binary power, the latter... you'd have to hope for a comment.

24

u/aioeu 4d ago

Even with a comment, if I saw 131082 I probably wouldn't even notice it was wrong.

3

u/Whole-Low-2995 4d ago

That's a good point!

14

u/musbur 4d ago

Apart from all the other valid comments: Since in your example 1 << 12 is a constant expression, it gets optimized away by the compiler anyway. The choice has nothing to do with (or influence on) resource requirement.

13

u/digitaljestin 3d ago edited 3d ago

It's harder to read, and programmers like me even need a calculator just to figure out the decimal value.

It's harder to read if you care about the decimal value. However, that's not when programmers use this convention. We use this to show bit position. For that it's much easier to read.

1

u/Whole-Low-2995 3d ago

Yup I guess it's differemt by situations

8

u/tux2603 2d ago

Idk, I feel that something like (1<<28) is much easier to read at a glance than 268435456. I've also seen things like (256 * 1024 * 1024), but honestly (1<<28) is so much faster to write and it's immediately obvious what it means

13

u/This_Growth2898 4d ago

I would rather use hex for powers of 2, like

#define MAXLEN 0x1000

And yes, this is about a style, but bitwise operations like this are mostly involved when you use bit flags. I don't see any meaningful use of them to describe the size.

3

u/Whole-Low-2995 4d ago

That's cool too.

2

u/Wertbon1789 3d ago

This is the style I see most of the time for sizes. I would only use the shift for bit flags.

7

u/Jack-of-Games 4d ago

When did you start programming? For those of us who started back in the 8-bit days, working with binary is second nature so 1<<N is extremely readable but I suspect that programmers who started more recently just don't do bit manipulation any more.

For people who are comfortable with binary, 1<<N expresses intent better than a decimal number. Also, while 4096 is pretty obvious, would you know that someone who wrote 8386608 had typo'd writing out 2^23 or even that they meant to refer to 2^23? Writing 1<<23 avoids this issue.

5

u/mikeblas 4d ago

What has endian-ness to do with it?

1

u/Whole-Low-2995 3d ago

If I remember correctly sometimes different endianness made a problem(when I was in a lab). So we just wrote endianness+architecture when writing a code. But I agree that's not a good convention

3

u/greg_kennedy 3d ago edited 3d ago

C source code hex/binary/etc literals - i.e. things you write in the source code directly - are always interpreted as "big endian", during compilation they will be correctly mapped to platform representation (if needed). See this Stack Overflow answer. In other words, this:

int x = 0x100;
printf("x = %d", x);

will always output x = 256, regardless of architecture.

Therefore, 1 << 12 is always 4096, it will not somehow become interpreted as "0x00 10" and give you a 16 element array :)

1

u/Whole-Low-2995 3d ago

Nice, I could correct misconceptions

2

u/mikeblas 3d ago

That doesn't make any sense.

1

u/Whole-Low-2995 3d ago

Yup I think so too

1

u/Whole-Low-2995 3d ago

plus that's not related to this as you said :) That's related to MSB/LSB. So it should be like this:

// rs232, lsb

6

u/Key_River7180 2d ago

Sobre people prefer it as they deal better with powers of 2

5

u/ttv90 4d ago

Once you read 1<<12 as a bit position instead of a number, decimal starts feeling like the weird choice.

5

u/skyMark413 3d ago

Difference in what they convey.

Maxlen 4096 means max value. Maxlen (1<<12) means max 12 bits of data.

If I have a i32 variable, with maxlen 4096 I know of a lot of unreachable states. But I don't always have i32. I may be working on a microcontroller with 12b registers, or a communication protocol where 12 bits are transferred, or an accelerator where 12b operations are much faster than 13+. (1<<12) Tells me the exact bit size is important and it might be a constraint

Also, bit masking, and less error prone for large numbers (who can correctly type out 1<<23 here with confidence?)

1

u/Whole-Low-2995 3d ago

Oh, 223 is huge. I missed that point

1

u/Vladislav20007 2d ago

pretty sure 1<<23 is 8388608.

5

u/FedUp233 3d ago

Personally, for most cases I prefer something like (4 x 1024) for K or 3 x 1024 x 1024) for M - sorry for the x instead of the “*” but reddit kept interpreting the star as formatting.

Or maybe, “#define K 1024”, “#define M (K x K)” the “4 x K” or “3 x M” although that can look a bit weird so usually the previous form. Any compiler worth anything will fold the constant expressions into a single constant.

Though for smaller powers of 2, 64K or less, I often just write out the decimal value assuming most any programmer should easily recognize them.

I’ve often found it weird that being so common and after am this time c and c++ haven’t extended numeric constants to accept the K, M, and G suffixes.

3

u/AnToMegA424 2d ago

You can put a backslash (\) right before any formatting character to make Reddit write it as-is instead of using it for formatting

For example this text is in bold, meanwhile **this one isn't** even though it uses the same markdown format for **bold** :]

2

u/FedUp233 2d ago

Yeah, I realized this was due to the markdown interpretation myself - right after I posted it! Didn’t think it was worth going back and fixing it since the meaning seemed clear.

Btw: if you are not thinking of markdown ahead of time the behavior (at least in the Reddit all) seems much more mysterious since as you type the expression everything looks fine till you hit the discs after the second number, at which time the star just disappears and the numbers scrunch up together still in regular font. It’s not very obvious that it’s a markdown processor doing it, although I tried it again just now and it behaved differently than it did when I wrote the comment. Weird. But then the Reddit iPad app seems to be one of the buggiest things I’ve ever used!

2

u/Whole-Low-2995 2d ago

Actually I use this style too. (4 * 1024 * 1024) is quite good for writing http servers. 4 * (1<<20) is fine though.

4

u/sciencekm 4d ago

For me, this is makes it obvious which bit is set. If, for example, I have an LFSR with taps at bits 22, 21, 19 and 17, I would make a mask of:

#define lfsrmask ((1 << 22) | (1 << 21) | (1 << 19) | (1 << 17))

This is way better than:

#define lfsrmask 6946816 // ???

1

u/Whole-Low-2995 4d ago

Yup that looks neat. We can't see each bits with 6946816.

4

u/Breath-Present 4d ago

for me, i wouldul use 1<<N for bit flag, and use 1024 or 0x400 for length and offset

1

u/Whole-Low-2995 3d ago

It's similar to my original opinion but I guess I have to take a look at my code too :)

3

u/DevBoiAgru 4d ago

It indeed does the same thing as writing the number directly but with time you just see it and go "Oh yeah thats raising 2 to this power" anyways. Apart from that it's especially useful for higher powers and creating bit masks. Ultimately it comes down to personal preference. One more thing for your example, it would also be easier to change it down the line if needed, since just changing 1<<12 to 1<<16 is easier in case the max length changes than rewriting the entire number.

3

u/P-39_Airacobra 4d ago

A number like 4096 can look arbitrary to someone who doesnt have the powers of 2 memorized. 1<<N makes it much clearer what the size is intended to be.

2

u/Whole-Low-2995 3d ago

Oh, it took time to memorize powers of 2. Now I remember. I guess one of my repo needs to be refactored

3

u/flatfinger 3d ago

If code is reliant upon the buffer size being a power of 2, I'd prefer:

#define FOO_BUFFSIZE_SHIFT 8
#define FOO_BUFFSIZE (1 << FOO_BUFFSIZE_SHIFT)
#define FOO_BUFF_MASK ((FOO_BUFFSIZE-1)

to only allow the buffer size to be configured to be a power of two. While defining the buffer size as e.g. 1<<12 might make me suspect that something might rely upon its being a power of two, defining things as shown above would make that more explicit.

2

u/hainguyenac 4d ago

Maybe the programmer doesn't want to reverse calculate the number to which power of 2 that is, so writing that helps them recognize it at a glance.

1

u/Whole-Low-2995 4d ago

That can be a good solution

2

u/Paul_Pedant 3d ago

Why not this -- it makes just as much sense. Or maybe (65536 >> 4), designed to deter other members of my team from fiddling around with my code. Hex constants are also helpful here.

#define MAXLEN (128 << 5)

Of course, the correct way to do this is to fstat() the partition data where the file resides, and use the actual block size from st_blksize for the device (or some multiple thereof).

1

u/theNbomr 2d ago

CPU runtime resources don't factor into it at all, because it is a compile time computation (unless you are worried about shaving milliseconds off compilation times). It will end up being a constant in the compiled object code.

2

u/Cathierino 2d ago

Depends on the meaning. If some size is specifically defined by a number of bits (perhaps because some address or offset is encoded in a bit field), then I would do that. If it's an arbitrary size, I would usually not do that. Even when the size is a power of two for aesthetics reasons. I only do stuff like 8 * 1024 * 1024 when the number is specifically meant to represent a readable size, for example when defining max file size.