2013-05-30 18 views
9

Mam kod, który używa porównania 64-bitowych liczb całkowitych. To wygląda podobnie do następującego:-Wyśladowanie porównywania znaków w g ++

#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; 
} 

Gdy ten kod jest kompilowany w g ++ (próbowałem różnych wersji Ubuntu 12.04 x64: 4.6.3, 4.6.4, 4.7.3, 4.8.0) z flagami -Wall -pedantic -std = C++ 0x test.cpp -o Test dostaję -Wsign-porównać ostrzeżenie dla drugiej linii pierwszego if (wyjście z g ++ - 4.8):

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

a gdy program testowy jest uruchamiany Dostaję dwa wiersze tekstu:

Something is wrong. 
Additional check went fine. 

Podczas kompilacji samego kodu w systemie Windows przy użyciu MS Visual Studio 11 Express, aktualizacji 2 z domyślnych opcjach projektu dla obu architekturze x64 lub x86 nie dostanę ani ostrzeżenia, ani to wyjście, a nie wyjście jest:

Everything is fine. 

Czy to problem w kodzie? Jeśli tak, czy mógłbyś to wskazać? Czy jest to problem z używanym kompilatorem?

Dodanie dodatkowego typu rzutowania dla drugiej stałej w pierwszej instrukcji if usuwa ostrzeżenie w g ++.

+1

To ładne pierwsze pytanie, w tym pełny przykład i wszystkie istotne informacje. –

+2

'0xFFF0000000000000', jako wartość dodatnia, nie pasuje długo. Jednak pasuje on do unsigned long long, więc jest to typ gcc. –

+1

Sufiks 'LL' może być użyty do wymuszenia na kompilatorze wybrania dłuższego typu (' 'LL' jest długi), ale nie do wybrania mniejszego typu, który ma być rzutowany ('(long long) 0xFFF0000000000000' (mówiąc ściśle, jest to implementacja zdefiniowana)). –

Odpowiedz

10

Zgodnie [lex.icon] w normie szesnastkowym-dosłownym 0xFFF0000000000000LL jest typu unsigned long long, ponieważ wartość ta nie mieści się w long long (patrz Unsigned hexadecimal constant in C? i C interpretation of hexadecimal long integer literal "L" więcej danych na ten temat.)

Oznacza to, że ostrzeżenie G ++ jest poprawne, porównujesz literaturę long long result z literą unsigned long long.

Oczywiście jako wartość bez znaku, 123456LL jest mniejsza niż 0xFFF0000000000000LL, więc wynik G ++ jest również poprawny.

MSVC wydaje się mieć błąd [Edit: albo zachowuje się inaczej ze względu na kompatybilność, zobaczyć komentarze], ponieważ to twierdzenie nie:

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

MSVC daje dosłowne 0xFFF0000000000000LL typu long long, jak wynika to nieważne Kod, który jest akceptowany przez MSVC:

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

C++ 03 przykład, który powinien skompilować bez błędów to:

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

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

GCC, Clang, Intel i Solaris CC wszystkie mają ten przykład, VC++ robi to źle.

+0

+1 za dobre sposoby sprawdzania poprawności błędu za pomocą 'static_assert' i automatycznego dedukcji typu :) – legends2k

+0

Czy VC++ obsługuje' long long' jako funkcję C++ 11, czy jako rozszerzenie C++ 03? 'long long' nie był częścią standardowego C++ przed C++ 11, czyż nie? – hvd

+0

Pod flagą '-fms-extensions' klangu' 0xFFF0000000000000LL' zostaje podpisana. Wierzę, że dzieje się tak, ponieważ, jak wskazuje na to @hvd, VC++ ma "long long" przed C++ 11 i to zachowanie jest potrzebne dla kompatybilności. – bames53

Powiązane problemy