(-2147483648> 0) restituisce true in C ++?


241

-2147483648 è il numero intero più piccolo per il tipo intero con 32 bit, ma sembra che trabocchi nella if(...)frase:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

Questo verrà stampato truenei miei test. Tuttavia, se eseguiamo il cast di -2147483648 in numero intero, il risultato sarà diverso:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

Questo stamperà false.

Non ho capito bene. Qualcuno può dare una spiegazione al riguardo?


Aggiornamento 02-05-2012:

Grazie per i tuoi commenti, nel mio compilatore, la dimensione di int è di 4 byte. Sto usando VC per alcuni semplici test. Ho cambiato la descrizione nella mia domanda.

Sono molte le risposte molto buone in questo post, AndreyT ha fornito una spiegazione molto dettagliata su come il compilatore si comporterà su tale input e su come questo intero minimo è stato implementato. qPCR4vir d'altra parte ha dato alcune "curiosità" correlate e come sono rappresentati i numeri interi. Così impressionante!


48
"sappiamo tutti che -2147483648 è il numero più piccolo di numero intero" Dipende dalla dimensione del numero intero.
orlp

14
"sappiamo tutti che -2147483648 è il numero più piccolo di numero intero" - Pensavo che non ci fosse un numero intero più piccolo, dal momento che ce ne sono molti di loro ... Qualunque cosa.

@Inisheer Con numeri interi a 4 byte potresti avere un valore INT_MINdi -9223372036854775808, se CHAR_BITè 16. E anche con CHAR_BIT == 8e sizeof(int== 4) `potresti ottenere -9223372036854775807perché C non richiede numeri a 2 complementi.
12431234123412341234123

Risposte:


391

-2147483648non è un "numero". Il linguaggio C ++ non supporta valori letterali negativi.

-2147483648è in realtà un'espressione: un valore letterale positivo 2147483648con un -operatore unario di fronte. Il valore 2147483648è apparentemente troppo grande per il lato positivo intdell'intervallo sulla tua piattaforma. Se il tipo long intavesse un raggio maggiore sulla tua piattaforma, il compilatore dovrebbe presumere automaticamente che 2147483648abbia long inttipo. (In C ++ 11 anche il compilatore dovrebbe considerare il long long inttipo.) Questo renderebbe il compilatore da valutare-2147483648 nel dominio di tipo più grande e il risultato sarebbe negativo, come ci si aspetterebbe.

Tuttavia, apparentemente nel tuo caso l'intervallo di long intè uguale all'intervallo di int, e in generale non esiste un tipo intero con un intervallo maggiore rispetto intalla tua piattaforma. Ciò significa formalmente quella costante positiva2147483648 trabocca di tutti i tipi di numeri interi con segno disponibili, il che a sua volta significa che il comportamento del programma non è definito. (È un po 'strano che la specifica del linguaggio opti per un comportamento indefinito in questi casi, invece di richiedere un messaggio diagnostico, ma è così.)

In pratica, tenendo conto del fatto che il comportamento non è definito, 2147483648potrebbe essere interpretato come un valore negativo dipendente dall'implementazione che diventa positivo dopo averlo -applicato unario . In alternativa, alcune implementazioni potrebbero decidere di provare a utilizzare tipi non firmati per rappresentare il valore (ad esempio, in compilatori C89 / 90 dovevano essere utilizzatiunsigned long int , ma non in C99 o C ++). Le implementazioni sono autorizzate a fare qualsiasi cosa, poiché il comportamento è comunque indefinito.

Come nota a margine, questo è il motivo per cui costanti come INT_MINsono in genere definite come

#define INT_MIN (-2147483647 - 1)

invece che apparentemente più semplice

#define INT_MIN -2147483648

Quest'ultimo non funzionerebbe come previsto.


78
Questo è anche il motivo per questo è fatto: #define INT_MIN (-2147483647 - 1).
orlp

5
@ RichardJ.RossIII - con clang probabilmente stai ottenendo un valore letterale a 64 bit, dato che era troppo grande per adattarsi a un int. L'implementazione di OP potrebbe non avere un tipo a 64 bit.
Carl Norum

1
@ RichardJ.RossIII: credo che questo comportamento sia definito / non definito dall'implementazione.
Oliver Charlesworth,

3
Non ho mai pensato che un "numero negativo" non sia analizzato in quanto tale. Non vedo un motivo. Spero che -1.0sia analizzato come doppio valore negativo, no?
saluta il

6
@ qPCR4vir: No. Come ho scritto nel mio commento alla tua risposta, in questo caso né il C né il C ++ moderno consentono di utilizzare tipi senza segno (con una costante decimale non corretta ). Solo il primo standard C (C89 / 90) consentito unsigned long intin questo contesto, ma in C99 questa autorizzazione è stata rimossa. I letterali non aggiunti in C e C ++ devono avere tipi firmati . Se vedi un tipo senza segno qui quando uno con segno funzionerebbe, significa che il tuo compilatore è rotto. Se vedi un tipo senza segno qui quando nessun tipo con segno funzionerebbe, allora questa è solo una manifestazione specifica di comportamento indefinito.
AnT

43

Il compilatore (VC2012) promuove numeri interi "minimi" che possono contenere i valori. Nel primo caso, signed int(e long int) non possono (prima dell'applicazione del segno), ma unsigned intpossono: 2147483648hasunsigned int ???? genere. Nel secondo forza intdal unsigned.

const bool i= (-2147483648 > 0) ;  //   --> true

avviso C4146: operatore meno unario applicato a tipo senza segno , risultato ancora senza segno

Ecco le "curiosità" correlate:

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

Standard C ++ 11 :

2.14.2 Letterali interi [lex.icon]

...

Un letterale intero è una sequenza di cifre che non ha periodo o parte esponente. Un valore letterale intero può avere un prefisso che specifica la sua base e un suffisso che specifica il suo tipo.

...

Il tipo di un numero intero letterale è il primo dell'elenco corrispondente in cui può essere rappresentato il suo valore.

inserisci qui la descrizione dell'immagine

Se un valore intero letterale non può essere rappresentato da alcun tipo nel suo elenco e un tipo intero esteso (3.9.1) può rappresentare il suo valore, può avere quel tipo intero esteso. Se tutti i tipi nell'elenco per il valore letterale sono firmati, deve essere firmato il tipo intero esteso. Se tutti i tipi nell'elenco per il valore letterale sono senza segno, il tipo intero esteso deve essere senza segno. Se l'elenco contiene sia tipi firmati che non firmati, il tipo intero esteso può essere firmato o non firmato. Un programma è mal formato se una delle sue unità di traduzione contiene un valore letterale intero che non può essere rappresentato da nessuno dei tipi consentiti.

E queste sono le regole delle promozioni per gli interi nello standard.

4.5 Promozioni integrali [conv.prom]

Una prvalue di un tipo intero diverso bool, char16_t, char32_t, o wchar_til cui intero conversione rango (4.13) è inferiore alla posizione di int può essere convertito in un prvalue di tipo intse intpuò rappresentare tutti i valori del tipo di sorgente; in caso contrario, il valore di origine può essere convertito in un valore di tipo unsigned int.


3
@ qPCR4vir: In C89 / 90 i compilatori avrebbero dovuto tipi di utilizzo int, long int, unsigned long intper rappresentare le costanti decimali senza suffisso. Quella era l'unica lingua che consentiva l'uso di tipi senza segno per le costanti decimali non corrette. In C ++ 98 era into long int. Non sono consentiti tipi non firmati. Né C (a partire da C99) né C ++ consentono al compilatore di utilizzare tipi senza segno in questo contesto. Il compilatore è, ovviamente, libero di usare tipi non firmati se nessuno di quelli firmati funziona, ma questa è ancora solo una manifestazione specifica di comportamento indefinito.
AnT

@AndreyT. Grande! Di couse, il tuo rigth. VC2012 è rotto?
qPCR4vir

@ qPCR4vir: AFAIK, VC2012 non è ancora un compilatore C ++ 11 (vero?), il che significa che deve usare into long intper rappresentare 2147483648. Inoltre, AFAIK, in VC2012 entrambi inte long intsono tipi a 32 bit. Ciò significa che in VC2012 il valore letterale 2147483648dovrebbe comportare un comportamento indefinito . Quando il comportamento non è definito, al compilatore è consentito fare qualsiasi cosa. Ciò significherebbe che VC2012 non è rotto. Ha semplicemente emesso un messaggio diagnostico fuorviante. Invece di dirti che il comportamento è completamente indefinito, ha deciso di utilizzare un tipo senza segno.
An

@AndreyT: Stai dicendo che i compilatori sono liberi di emettere demoni nasali se il codice sorgente contiene un valore decimale letterale non corretto che supera il valore massimo di un segno longe non è necessario emettere una diagnostica? Sembrerebbe rotto.
supercat

Stesso "avvertimento C4146" in VS2008 e "questa costante decimale non è firmata solo in ISO C90" in G ++
spyder

6

In breve, 2147483648trabocca a -2147483648ed (-(-2147483648) > 0)è true.

Ecco come 2147483648appare in binario.

Inoltre, nel caso di calcoli binari con segno, il bit più significativo ("MSB") è il bit di segno. Questa domanda può aiutare a spiegare il perché.


4

Poiché in -2147483648realtà è 2147483648negato ( -) applicato ad esso, il numero non è quello che ti aspetteresti. In realtà è l'equivalente di questo pseudocodice:operator -(2147483648)

Ora, supponendo che il tuo compilatore sia sizeof(int)uguale 4e CHAR_BITdefinito come 8, ciò farebbe 2147483648overflow il valore con segno massimo di un numero intero (2147483647 ). Quindi qual è il massimo più uno? Consente di risolverlo con un intero di complimento a 4 bit, 2s.

Aspettare! 8 trabocca l'intero! Cosa facciamo? Usa la sua rappresentazione senza segno 1000e interpreta i bit come un intero con segno . Questa rappresentazione ci lascia con -8l'applicazione della negazione del complemento 2s risultante 8, che, come tutti sappiamo, è maggiore di 0.

Questo è il motivo per cui <limits.h>(e <climits>) si definiscono comunemente INT_MINcome ((-2147483647) - 1)- in modo che il numero intero con 0x7FFFFFFFsegno massimo ( ) sia negato ( 0x80000001), quindi decrementato ( 0x80000000).


Per un numero di 4 bit, la negazione del complemento a due di -8è ferma -8.
Ben Voigt,

Tranne che -8 è interpretato come 0-8, non negativo 8. E 8 trabocca un 4 bit con segno int
Cole Johnson

Considera -(8)quale in C ++ è uguale a -8- è la negazione applicata a un letterale, non a un letterale negativo. Il valore letterale è 8, che non rientra in un numero intero a 4 bit con segno, quindi deve essere senza segno. Lo schema è 1000. Finora la tua risposta è corretta. La negazione del complemento a due di 1000in 4 bit è 1000, non importa se è firmata o non firmata. La tua risposta dice "interpretare i bit come un intero con segno" che rende il valore -8dopo la negazione del complemento dei due, proprio come era prima della negazione.
Ben Voigt,

Naturalmente, in "C ++ a 4 bit" non esiste "interpretare i bit come un passo intero con segno". Il letterale diventa il tipo più piccolo che può esprimerlo, che è un intero a 4 bit senza segno . Il valore del letterale è 8. Viene applicata la negazione (modulo 16), con conseguente risposta finale di 8. La codifica è ancora 1000 ma il valore è diverso perché è stato scelto un tipo senza segno.
Ben Voigt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.