5

I am trying to write a computer programme that will take two numbers from the user as inputs and will print the bigger number using bitwise operation. I want it to end the programme and return 1 if user inputs negative number or floating number or keep looping back otherwise.

So I wrote the following -

#include <stdio.h>

unsigned int big (unsigned int x, unsigned int y)
{
    unsigned int big = x, small = y;
    for (;;)
    {
        big >>= 1; small >>= 1;
        if ((big == 0) || (small == 0))
        {
            if (big == 0) { return y; }
            if (small == 0) { return x; }
        }
    }
}

int main (void)
{
    for (;;)
    {
        unsigned int x, y;
        printf("Give me a number : ");
        if ((scanf("%u", &x) != 1) || (x < 0)) { printf ("error"); break; };
        printf("Give me another number : ");
        if ((scanf("%u", &y) != 1) || (y < 0)) { printf ("error"); break; };
        printf("The bigger number is %u\n\n", big (x, y));
    }
    return 1;
}

There seems to be a problem with what I wrote. The function big() is giving wrong output when x == 1 && y == 0 . Why is it not working and how can it be made to work?

3
  • 1
    "Why is it not working and how can it be made to work?" - have you tried to debug your program ? Also see: How to debug small programs. Commented Sep 6 at 16:56
  • 2
    Hint for why it is not working with x == 1, y == 0 : you shift big and small before you check for zero (which in this case will make both zero). Commented Sep 6 at 16:59
  • I do not know how to debug unfortunately. I have visual studio and am learning it. Thanks for the resource. I will adopt the style and philosophy that you are promoting. Commented Sep 7 at 3:16

3 Answers 3

5

There are multiple issues in the code:

  • the function big() does not work if the numbers have the same number of significant bits, eg: if you enter 1 and 0 you will get 0, which is incorrect, but the same for 3 and 2, 6 and 4 etc.

  • your method would work if you shift the values after the test and if you first mask off bits that are identical in both values.

  • the tests in big and small are redundant, you can remove the first if.

  • testing x < 0 in the main function is moot: x is an unsigned int thus cannot be negative. You might want to use a different parsing method.

Here is a modified version:

#include <stdio.h>

unsigned int big(unsigned int x, unsigned int y)
{
    unsigned int xx = x & ~y;
    unsigned int yy = y & ~x;
    for (;; xx >>= 1, yy >>= 1) {
        if (xx == 0)
            return y;
        if (yy == 0)
            return x;
    }
}

int main(void)
{
    for (;;) {
        unsigned int x, y;
        printf("Give me a number: ");
        if (scanf("%u", &x) != 1) { printf ("error\n"); break; };
        printf("Give me another number: ");
        if (scanf("%u", &y) != 1) { printf ("error\n"); break; };
        printf("The bigger number is %u\n\n", big(x, y));
    }
    return 1;
}

Here is an input function that is more restrictive:

#include <errno.h>
#include <limits.h>
#include <stdlib.h>

int get_unsigned(const char *prompt, unsigned int *dest) {
    for (;;) {
        char buf[128];
        printf("%s: ");
        fflush(stdout);
        if (!fgets(buf, sizeof buf, stdin)) {
            fprintf(stderr, "unexpected end of file\n");
            return 0;
        }
        int len = 0;
        sscanf(buf, " %*[0-9] %n", &len);
        if (!len || buf[len] != '\0') {
            printf("invalid input: %s\n", buf);
            continue;
        }
        errno = 0;
        unsigned long v = strtoul(buf, NULL, 0);
        if (errno || v > UINT_MAX) {
            printf("number too large: %s\n", buf);
            continue;
        }
        *dest = (unsigned int) v;
        return 1;
    }
}

int main(void) {
    for (;;) {
        unsigned int x, y;
        if (!get_unsigned("Give me a number", &x)
        ||  !get_unsigned("Give me another number", &y))
            break;
        printf("The bigger number is %u\n\n", big(x, y));
    }
    return 1;
}

Here is a simpler way to obtain the larger value without loops or tests, but using subtraction (-) which strictly speaking is not a bitwise operation:

#include <limits.h>

unsigned int big(unsigned int x, unsigned int y) {
    // compute mask as -1 if (x > y) and 0 otherwise
    unsigned mask = (y - x) >> (sizeof(x) * CHAR_BIT - 1);
    return (x & mask) | (y & ~mask);
}
Sign up to request clarification or add additional context in comments.

7 Comments

A simple method for input (that isn't guaranteed to work, but probably will) would be to use intmax_t to take the inputs and bounds check against 0 and UINT_MAX before calling big.
Several issues with this interesting idea: some legacy C libraries do not support %jd, there is no guarantee what INTMAX_MAX > UINT_MAX, scanf() would have undefined behavior on values that exceed the range of the destination type and the OP also wants to reject floating point literals. Suggesting a combination of fgets() and explicit parsing and reliable conversions is a more sensible approach.
I definitely agree with "a combination of fgets()....". I just meant to suggest a simple quick-and-dirty solution; I missed that OP wants to exclude floating point inputs. But you are right: scanf isn't really suited for interactive input at all.
I am not getting how initializing the temp variables with x & ~y and ~x & y is solving the problem of "both numbers having the same highest significant bit." What is the mechanism? Thanks.
I mean I can clearly see that it works when I do it on pen and paper. But, what made you come up with this in the first place?
Your loop stops when all bits have been shifted out of at leas one value, making the other one the largest. As I wrote in the answer, masking off identical bits in both values (ie: settings them to 0) solves the problem because the most significant bit in the resulting values, by definition is present only in a single value, which is the larger one and will be detected as such by your loop.
@chqrlie can you please take a look at my answer posted below and comment on the input management? I took x and y input as ints, manually rejected negative numbers, typecasted them into unsigned ints before passing them to the function big(). This is okay I hope unless it has any problem I am failing to diagnose? Thank you.
2

You can start the comparison with the highest bits:

unsigned int big(unsigned int x, unsigned int y)
{
    for (unsigned int m = ~((unsigned int)-1 >> 1); m; m >>= 1) 
    {
        unsigned int bit_x = x & m;
        unsigned int bit_y = y & m;
        if (bit_x) 
          { if (!bit_y) return x; }
        else if (bit_y) return y;
    }
    return x;
}

If only shifts and comparisons are allowed:

unsigned int big(unsigned int x, unsigned int y)
{
    unsigned int xx = x, yy = y;
    for (;;) 
    {
        if (xx == 0) return y;
        if (yy == 0) return x;
        unsigned int new_xx = xx << 1;
        unsigned int new_yy = yy << 1;
        unsigned int hi_bit_x = (new_xx >> 1) != xx;
        unsigned int hi_bit_y = (new_yy >> 1) != yy;
        if (hi_bit_x) 
          { if (!hi_bit_y) return x; }
        else if (hi_bit_y) return y;
        xx = new_xx;
        yy = new_yy;
    }
    return x;
}

Comments

0

Based on the feedback provided by other users it appears the code in the question needs to be corrected by following the steps mentioned below -

  1. In the main() the variables x and y should be declared as int instead of uint . In this way negative input values can be manually rejected and the variables can then be passed on to big() by typecasting them into unsigned int . This is a beginner level solution it needs to be noted as typecasting is not liked very much by the advanced level C programmers.

  2. In the big() the temporary variables need to be initialized as unsigned int big = x & ~y, small = ~x & y; instead of unsigned int big = x, small = y; . Doing this will make sure that the programme gives the correct answer when both x and y happen to have the same highest significant bit.

  3. Conditions need to be checked at the end of the loop instead of at the beginning. This can be achieved by using a do-while loop.

     #include <stdio.h>
    
     unsigned int big (unsigned int x, unsigned int y)
     {
         unsigned int big = x & ~y, small = ~x & y;
         do {
             if (!big) { return y; }
             if (!small) { return x; }
             big >>= 1; small >>= 1;
         } while (1);
     }
    
     int main (void)
     {
         for (;;)
         {
             int x, y;
             printf ("Give me a number: ");
             if ((scanf ("%d", &x) != 1) || (x < 0)) { printf ("error\n"); break; }
             printf("Give me another number: ");
             if ((scanf ("%d", &y) != 1) || (y < 0)) { printf ("error\n"); break; }
             printf ("The bigger number is %u\n\n", big ((unsigned int)x, (unsigned int)y));
         }
         return 1;
     }
    

1 Comment

Instead of do/while(1), I strongly suggest using a for(;;) loop. do/while loops are error prone and tend to hide subtle bugs. This one is correct, but for(;;) is more idiomatic for an infinite loop. Btw Conditions need to be checked at the end of the loop instead of at the beginning is not correct, Conditions need to be checked before shifting the values. Also parsing the input with scanf() does not allow for detecting and rejecting floating point inputs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.