2

I'm writing some C code to look at the bit representations of integers. In the process I found code that returns different results based on optimization settings. Am I just doing something wrong, or is this a compiler bug?

Here is the code:

#include <stdio.h>

int test(int x, int n)
{
  int TMin_n = -(1 << (n-1));
  int TMax_n = (1 << (n-1)) - 1;
  return x >= TMin_n && x <= TMax_n;
}

int main(){

    int x = 0x80000000;
    int n = 0x20;
    if(test(x,n)){
        printf("passes\n");
    }
    else{
        printf("fails\n");
    }
}

If I compile it this way I get a passing result

$ gcc tester.c -o tester
$ ./tester
passes

If I compile it this way I get a failing result

$ gcc -O1 tester.c -o tester
$ ./tester
fails

The problem is that

x <= TMax_n

is evaluating as false in the optimized case only.

Here is my platform details:

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

1 Answer 1

5

Your platform apparently uses 32 bit int type. 0x20 is 32, which means that 1 << (n-1) inside your function attempts to shift 1 by 31 bit. The behavior is undefined, since this expression has signed type (1 has type int) and value of 231 is not representable in it.

6.5.7 Bitwise shift operators

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] If E1 has a signed type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

What you are observing is the consequences of that undefined behavior. There's nothing unusual in seeing its manifestations to depend on the compiler's optimization settings.

Sign up to request clarification or add additional context in comments.

2 Comments

@djp3: C language does not have such thing as "just shift the bits". As you see above the << operator is defined as multiplication by a power of 2. And if that multiplication overflows, the behavior is undefined. More precisely, if you want to "just shift the bits" make sure to use unsigned types as operands. With signed types once you start shifting out of (or into) the sign bit, things typically fall apart. Left shift is undefined, right shift is implementation-defined. On C++ side of things C++14 switched to implementation-defined for this left shift. But C keeps it undefined for now.
@djp3: Note also that even if you compiled this as C++14 and ended up with the expected signed 0x80000000 after the shift, the subsequent unary - or binary - 1 would still overflow things and trigger undefined behavior.

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.