9

I have a code which uses comparison of 64-bit integers. It looks similar to the following:

#include <cstdio>

long long getResult()
{
    return 123456LL;
}

int main()
{
    long long result = getResult();

    if (result > 0x000FFFFFFFFFFFFFLL
        || result < 0xFFF0000000000000LL)
    {
        printf("Something is wrong.\n");

        if (result > 0x000FFFFFFFFFFFFFLL
            || result < -4503599627370496LL)
        {
            printf("Additional check failed too.\n");
        }
        else
        {
            printf("Additional check went fine.\n");
        }
    }
    else
    {
        printf("Everything is fine.\n");
    }

    return 0;
}

When this code is compiled in g++ (tried different versions on Ubuntu 12.04 x64: 4.6.3, 4.6.4, 4.7.3, 4.8.0) with flags -Wall -pedantic -std=c++0x test.cpp -o test I get -Wsign-compare warning for second line of the first if statement (output from g++-4.8):

test.cpp:13:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
 || result < 0xFFF0000000000000LL)
             ^

And when the test program is run I get two lines of text:

Something is wrong.
Additional check went fine.

When compiling same code on Windows using MS Visual Studio 11 Express Update 2 with default project options for either x86 or x64 architecture I don't get neither the warning nor this output, instead the output is:

Everything is fine.

Is it a problem in the code? If yes, could you point it? Or is it a problem with the compiler used?

Adding additional type cast for the second constant in the first if statement removes the warning in g++.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
alerion
  • 93
  • 1
  • 4
  • 1
    That's a nice first question, including a complete example and all relevant information. – Benjamin Bannier May 30 '13 at 11:12
  • 2
    `0xFFF0000000000000`, as a positive value, doesn't fit in long long. However, it does fit in an unsigned long long, so that's the type gcc uses. – Marc Glisse May 30 '13 at 12:42
  • 1
    The suffix `LL` can be used to force the compiler to chose a longer type (`1LL` is long long), but not to chose a smaller type, you want a cast for that (`(long long)0xFFF0000000000000` (strictly speaking this is implementation defined)). – Marc Glisse May 30 '13 at 13:05

1 Answers1

11

According to [lex.icon] in the standard, the hexadecimal-literal 0xFFF0000000000000LL has type unsigned long long, because the value doesn't fit in a long long (see Unsigned hexadecimal constant in C? and C interpretation of hexadecimal long integer literal "L" for more information on this.)

This means G++'s warning is correct, you are comparing long long result to an unsigned long long literal.

Clearly as an unsigned value, 123456LL is less than 0xFFF0000000000000LL, so G++'s result is also correct.

MSVC seems to have a bug [Edit: or behaves differently for compatibility reasons, see comments], because this assertion fails:

static_assert(0xFFF0000000000000LL > 0, "big number is big");

MSVC gives the literal 0xFFF0000000000000LL type long long, as shown by this invalid code that is accepted by MSVC:

auto i = 0xFFF0000000000000LL;
long long& l = i;

A C++03 example that should compile without errors is:

template<typename T>
void f(T t)
{
    unsigned long long& l = t;
}

int main()
{
    f(0xFFF0000000000000LL);
}

GCC, Clang, Intel and Solaris CC all get this example right, VC++ gets it wrong.

Community
  • 1
  • 1
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • +1 for the nice ways to validate the bug using `static_assert` and auto type deduction :) – legends2k May 30 '13 at 13:04
  • Does VC++ support `long long` as a C++11 feature, or as a C++03 extension? `long long` was not part of standard C++ before C++11, was it? –  May 30 '13 at 13:16
  • Under clang's `-fms-extensions` flag `0xFFF0000000000000LL` becomes signed. I believe this is because, as @hvd indicates, VC++ had `long long` before C++11 and this behavior is needed for compatibility. – bames53 May 30 '13 at 15:26
  • @hvd, the question's tagged [tag:c++11] ([tag:c++] was only added later) and VC++ defaults to C++11, doesn't it? All the other compilers also supported `long long` (and `unsigned long long`!) years ago, the C++11 rules come straight from C99. I didn't know about Clang's altered behaviour with that flag though, so it's obviously a known/intended behaviour for VC++, not just a bug – Jonathan Wakely May 30 '13 at 23:49
  • @JonathanWakely I wasn't asking about the question, but about the implementation. I was suspecting that VC++ used a conforming extension to C++03, rather than a buggy implementation of a C++11 feature. If you say that VC++ defaults to C++11 (I really don't know), rather than C++03 plus extensions, that answers my question, thanks. –  May 31 '13 at 06:01