2015-07-27 18 views
10

Ho una domanda piuttosto semplice, ma non sono sicuro di aver compreso il concetto o meno. Supponiamo di avere:Moltiplicazione di due interi in C++

int a = 1000000; 
int b = 1000000; 
long long c = a * b; 

Quando eseguo questo, c mostra il valore negativo, così ho cambiato anche A e B per lungo tempo e poi tutto è andato bene. Quindi, perché devo cambiare a e b, quando i loro valori sono nell'intervallo di int e il loro prodotto è assegnato a c (che è molto lungo)?

sto usando C/C++

+17

i int non sono promossi a lungo molto prima della moltiplicazione, loro restano intatti e anche il prodotto. Quindi il prodotto è lanciato a lungo, ma troppo tardi, il trabocco ha colpito. Avere uno o l'altro lungo dovrebbe funzionare come l'altro sarebbe promosso. –

+0

Dovresti taggare la lingua di programmazione che stai utilizzando perché lingue diverse potrebbero introdurre un comportamento diverso;) – alfasin

+0

A seconda della macchina e della versione di C che stai utilizzando, la dimensione di un 'int' potrebbe cambiare. – Jay

risposta

18

Le int s non sono promossi a long long prima della moltiplicazione, essi rimangono int s ed il prodotto così. Quindi il prodotto è trasmesso a long long, ma troppo tardi, l'overflow ha colpito.

Avere uno di a o long long dovrebbe funzionare come l'altro sarebbe promosso.

2

Per gli operatori aritmetici, il tipo di risultato non dipende da ciò a cui si sta assegnando il risultato ma dai tipi di operandi. Per gli operatori aritmetici, gli usual arithmetic conversions vengono eseguiti sugli operandi. Questo è utilizzato per portare gli operandi in un tipo comune, questo significa per i tipi più piccolo firmati/firmati int se i valori possono misura sono promossi a senza segno/firmata int, in questo caso sono già entrambi int così non è richiesta alcuna conversione. Vedi Why must a short be converted to an int before arithmetic operations in C and C++? per i dettagli sul perché.

Quello che abbiamo ora è un comportamento indefinito in quanto integer overflow firmato è un comportamento indefinito, questo è trattato nel progetto C++ sezione standard 5[Expr] che dice:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [ Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note ]

Ora, a un giorno ci hanno disinfettanti per la cattura di questi tipi di comportamento non definito e l'utilizzo -fsanitize=undefined sia con clangore e gcc prenderà questa in fase di esecuzione con il seguente errore (see it live):

runtime error: signed integer overflow: 1000000 * 1000000 cannot be represented in type 'int'

Per la sezione di riferimento 5.6[expr.mul] dice:

[...]The usual arithmetic conversions are performed on the operands and determine the type of the result.

e la sezione 5 dice:

Otherwise, the integral promotions (4.5) shall be performed on both operands.61 Then the following rules shall be applied to the promoted operands

  • If both operands have the same type, no further conversion is needed.
0

'un po' assurdo, perché l'istruzione assembler non sempre calcolare

int * int -> 64 bit lunghi

quindi se si guarda il codice della macchina, si vede: imul che memorizzano 64bits in eax edx poi CDQ che ha messo il segno po 'di EAX in edx (perdendo così il risultato pieno 64bit) e poi EAX edx vengono memorizzati nella 64 bit variabile

e se si convertire i valori 32bit in 64bit prima della moltiplicazione, si riceve una chiamata alla funzione 64bit di moltiplicazione per nessun motivo

(ho controllato: non è il caso in cui il codice è ottimizzato)