Home > Software design >  inconsistent results with left shift operator (<<)
inconsistent results with left shift operator (<<)

Time:11-03

I am learning bit manipulation using C. I encountered a problem when writing a program that converts a binary to decimal, particularly in the for loop of the program. The following is my code:

unsigned int binary_to_uint(const char *b)
{
    unsigned int result = 0;
    int i, len;
    if (!b)
        return (0);
    len = strlen(b);
    for (i = 0; i < len; i  )
    {
        if (b[i] == '1')
        {
            result  = 2 << (i-1); /*where my issue is*/
        }
        else if (b[i] == '0')
            continue;
        else
            return (0);
    }
    return (9);
}

I tried debugging and I realized my problem was originating from the if statement

I therefore did some experiment with the code in the if* statement:

int main() {
    // Write C code here
    int i = 0;
    printf("result of 2 << (%d - 1): %d\n", i, 2 << (i - 1));
    printf("result of 2 << (0 - 1): %d", 2 << (0 - 1));

    return 0;
}

In the first printf, displays result of 2 << (0 - 1): 0 in the console, while in the second printf, displays result of 2 << (0 - 1): 1 in the console. My expectation is that both printf should display the exact same thing, that is the value of 2 << -1 is 1, however that is not the case. Can someone please help me understand what is going on. why did the use of the variable i change the outcome of the shift operator to 0?

CodePudding user response:

The right-hand operand of the << or >> operators is required to be non-negative. Using a negative value for this operand triggers undefined behavior. Because of this, you can't use a negative value to shift in the opposite direction.

This is detailed in section 6.5.7p3 of the C standard:

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

In your particular case, undefined behavior is manifesting as different results between using i-1 and 0-1 for the right operand.

Instead of shifting 2 by i-1 places, instead shift 1 by i places:

result  = 1 << i; 

CodePudding user response:

If b[i] is equal to '1' then you need to shift the unsigned constant 1u instead of the signed constant 2 and using len - i - 1 instead of i-1. Moreover the last expression can be negative when i is equal tp 0.

    if (b[i] == '1')
    {
        result  = 1u << len - i - 1;
    }

Here is a demonstration program

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

unsigned int binary_to_uint( const char *b )
{
    unsigned int result = 0;

    size_t len = strlen( b );

    if (CHAR_BIT * sizeof( unsigned int ) < len)
    {
        return 0;
    }
    else
    {
        for (size_t i = 0; i < len; i  )
        {
            if (b[i] == '1')
            {
                result  = 1u << len - i - 1;
            }
            else if (b[i] != '0')
            {
                return 0;
            }
        }
    }

    return result;
}

int main( void )
{
    const char *b = "1111";

    printf( "\"%s\" = %u\n", b, binary_to_uint( b ) );
}

The program output is

"1111" = 15
  • Related