Bit shifting limit?

1

I'm trying to create a subnet mask from CIDR notation eq. /8 would mean that the 8 leading bits would be 1's. I'm achieving this by left shifting (I'm working with 32 bits here) (uint)(0xffffffff << (32-8)).

The code works fine until I get a /0 mask which leads to the code (uint)(0xffffffff << 32)

Now left shifting (uint)(0xffffffff << 31) works as intended 10000000.00000000.00000000.00000000.

But left shifting (uint)(0xffffffff << 32) gives 11111111.11111111.11111111.11111111. While expected outcome would be 00000000.00000000.00000000.00000000.

What's the simplest way around this? Handle /0 with a if-statement and just set all to 0?

c#
bit
bit-shift
bitmask
cidr
asked on Stack Overflow Oct 17, 2020 by GitGudAllu • edited Oct 18, 2020 by phuclv

2 Answers

0

I'd say the correct algorithm would be something like:

0xffffffff & (uint)((((ulong)0x1 << mask) - 1) << (32-mask))

Where mask is the number after the /

I've implemented it in c# here (since you don't specify a language), and seems to work: https://dotnetfiddle.net/j1IfZP

answered on Stack Overflow Oct 17, 2020 by Jcl
0

From the documentation:

The left-shift operation discards the high-order bits that are outside the range of the result type and sets the low-order empty bit positions to zero

That means for shifting left on a 32-bit type, only the low 5 bits in the shift count is taken. Since 32 = 0b10_0000 which needs 6 bits to store and after masking out the low 5 bits it'll become zero. So 0xffffffff << 32 is equivalent to 0xffffffff << 0

To solve that you either need to shift in a higher precision

return (uint)(((1UL << mask) - 1) << (32 - mask))

or check the shift count before shifting

return mask == 0 ? 0xFFFFFFFFU : 0xFFFFFFFFU << (32 - mask);

The latter is better on 32-bit platforms

answered on Stack Overflow Oct 17, 2020 by phuclv

User contributions licensed under CC BY-SA 3.0