简体   繁体   中英

Bitwise operations and shifts problems

I am testing the function fitsBits(int x, int n) on my own and I figure out there is a condition that doesn't fit in this function, what is the problem?

/*
* fitsBits - return 1 if x can be represented as an 
*  n-bit, two's complement integer.
*   1 <= n <= 32
*   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
*   Legal ops: ! ~ & ^ | + << >>
*   Max ops: 15
*   Rating: 2
*/
int fitsBits(int x, int n) {
   int r, c;
   c = 33 + ~n;
   r = !(((x << c)>>c)^x);
   return r;
}

It seems like it gives the wrong answer in

fitsBits(0x80000000, 0x20);

It gives me 1, but actually it should be 0... How could I fix it? Thank you!

Left shifts that cause overflow are undefined for signed types. Hence the compiler may optimise (x<<c)>>c as simply x , and the entire function reduces down to return 1; .

Probably you want to use unsigned types.

A second cause of undefined behavior in your code is that c may be greater than or equal to the width of int. A shift of more than the width of the integer type is undefined behavior.

fitsBits(0x80000000, 0x20);

This function returns 1 , because the first argument of your function is int , which is (in practice these days) a 32 bit signed integer. The largest value that signed 32 bit integer can represent is 0x7FFFFFFF, which is less than the value you are passing in. Because of that your value gets truncated and becomes -0x80000000 , something that 32 bit integer can represent. Therefore your function returns 1 (yes, my first argument is something that can be represented using 0x20 = 32 bits).

If you want your function to properly classify number 0x80000000 as something that cannot be represented using 32 bits, you need to change the type of the first argument of your function. One options would've been using an unsigned int , but from your problem definition it seems like you need to properly handle negative numbers, so your remaining option is long long int , that can hold numbers between -0x8000000000000000 and 0x7FFFFFFFFFFFFFFF .

You will need to do couple more adjustments: you need to explicitly specify that your constant is of type long long by using LL suffix, and you now need to shift by 64 - c , not by 32 - c :

#include <stdio.h>

int fitsBits(long long x, int n) {
   long long r;
   int c;
   c = 65 + ~n;
   r = !(((x << c)>>c)^x);
   return r;
}

int main() {
    printf("%d\n", fitsBits(0x80000000LL, 0x20));
    return 0;
}

Link to IDEONE: http://ideone.com/G8I3kZ

r = (((x << c)>>c)^x); //This will give you 0, meaning r = 0;

OR

r = !((x << c)>>c);

Your function can be simplified to

int fitsBits(int x) {

    int r, c; 
    c = 33; 
    r = (((x << c)>>c)^x);

    return r;
}

Note that when NOT( ! ) is brought you're asking for opposite of r

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM