Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When left shift isn't the same as multiply by 2

I attempted KR exercise 2.1 - determine the range of long variables by direct calculation.

#include <stdio.h>
#include <limits.h>

int main()
{
    unsigned short long_bits =  sizeof( long ) * CHAR_BIT;
    printf( "LONG_MAX   = %ld\n", ( 1L << ( long_bits - 1 ) ) - 1 );
    printf( "LONG_MIN   = %ld\n", ( -1 ) * ( 1L << ( long_bits - 1 ) ) );
    printf( "ULONG_MAX (1) = %lu\n", ( 1UL << long_bits ) - 1 );    // doesn't work

    printf( "ULONG_MAX (2) = %lu\n", 2*( 1UL << ( long_bits - 1 ) ) - 1 );  // work
    printf( "\n" );
}
  • ULONG_MAX (1) = 0 is wrong because the left shift overflow I suppose.
  • ULONG_MAX (2) = 18446744073709551615 seems correct by replacing the last left shift with a multiply by 2.

So it appears that left shift operator suffers from overflow, but multiplication does not? Does this intermediate calculation 2*( 1UL << ( long_bits - 1 ) ) promote to some type more than long? In my machine, long and long long are exactly the same ( 8 Bytes ).


Edit: As Lundin pointed out, all needed for ULONG_MAX is printf( "ULONG_MAX = %lu\n", ~0L );

Using left shift in this case caused UB, and multiply by 2 is potentially UB, too (although result of case 2 looks correct).

like image 890
artm Avatar asked Sep 25 '22 22:09

artm


1 Answers

The behaviour of left-shifting a value is only defined if the amount you are left-shifting by is less than the size of the type. Therefore, 1UL << long_bits is undefined behaviour and anything can happen, including dæmons flying out of your nose.

Let n be the number of bits in the type we are working with. In practice, depending on platform, there are two behaviours that happen in this case: Either, any left-shift by n or more produces 0 (as the whole bit-pattern is shifted out), or the left-shift is reduced modulo n, so a left-shift by n places behaves like a left shift by 0 places, yielding 1UL << 0 or 1.

like image 195
fuz Avatar answered Nov 10 '22 23:11

fuz