Search code examples
c#bitbit-shiftbitmaskcidr

Bit shifting limit?


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?


Solution

  • 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