Regole di conversione del tipo implicite negli operatori C ++


167

Voglio essere migliore nel sapere quando dovrei lanciare. Quali sono le regole di conversione del tipo implicito in C ++ quando si aggiungono, si moltiplicano, ecc. Ad esempio,

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

eccetera ...

L'espressione verrà sempre valutata come il tipo più preciso? Le regole differiscono per Java? Per favore, correggimi se ho formulato questa domanda in modo impreciso.


16
Tieni presente che ^è XOR.
GManNickG

16
@int ^ float = errore di compilazione :)
Serge Dundich,

Risposte:


223

In C ++ gli operatori (per i tipi POD) agiscono sempre su oggetti dello stesso tipo.
Pertanto, se non sono uguali, verrà promosso in modo che corrisponda all'altro.
Il tipo di risultato dell'operazione è lo stesso degli operandi (dopo la conversione).

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

Nota. La dimensione minima delle operazioni è int. Quindi short/ charvengono promossi intprima che l'operazione venga eseguita.

In tutte le tue espressioni intviene promosso a floatprima che l'operazione venga eseguita. Il risultato dell'operazione è a float.

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>

1
"La dimensione minima delle operazioni è int." - Sarebbe molto strano (che dire delle architetture che supportano in modo efficiente operazioni char / short?) È davvero nelle specifiche C ++?
Rafał Dowgird,

3
@Rafal: Sì. int dovrebbe essere il tipo intero più efficiente per il funzionamento su una particolare piattaforma. char deve sempre essere 1 ma short può avere le stesse dimensioni di int.
Martin York,

1
@ Rafał: sì, è molto strano ed è nello standard. In molti casi, l'architettura che descrivi potrebbe usare il suo tipo super efficiente char. Se il valore di char + charè assegnato a char, allora può semplicemente eseguire l'aritmetica chare, ad esempio, concludere. Ma se il risultato è assegnato, intallora deve fare l'aritmetica in un tipo abbastanza grande da ottenere il risultato corretto quando è più di CHAR_MAX.
Steve Jessop,

2
Voglio solo sottolineare il fatto che int viene promosso a unsigned int !!! Ho lottato con i bug per giorni perché avevo l'impressione che entrambi sarebbero stati promossi a int o long in modo che un possibile risultato negativo non causasse un underflow / avvolgimento.
nitsas,

10
Esempio del problema " int viene promosso a unsigned int ": ((int) 4) - ((unsigned int) 5)risulterà 4294967295per ints a 32 bit e ints a 32 bit.
nitsas,

33

Operazioni aritmetiche che comportano floatrisultati in float.

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

Per una risposta più dettagliata. Guarda cosa dice la sezione §5 / 9 dello standard C ++

Molti operatori binari che prevedono operandi di tipo aritmetico o di enumerazione causano conversioni e generano tipi di risultati in modo simile. Lo scopo è quello di produrre un tipo comune, che è anche il tipo del risultato .

Questo modello è chiamato le solite conversioni aritmetiche, che sono definite come segue:

- Se uno degli operandi è di tipo long double, l'altro deve essere convertito in long double.

- Altrimenti, se uno degli operandi è doppio, l'altro deve essere convertito in doppio.

- In caso contrario, se uno degli operandi è float, l'altro deve essere convertito in float.

- In caso contrario, le promozioni integrali (4.5) devono essere eseguite su entrambi gli operandi.54)

- Quindi, se uno degli operandi è unsigned long, l'altro deve essere convertito in unsigned long.

- Altrimenti, se un operando è un long int e l'altro unsigned int, quindi se un long int può rappresentare tutti i valori di un unsigned int, l'int unsigned deve essere convertito in un long int; in caso contrario entrambi gli operandi saranno convertiti in unsigned long int.

- Altrimenti, se uno degli operandi è lungo, l'altro deve essere convertito in lungo.

- In caso contrario, se uno degli operandi è senza segno, l'altro deve essere convertito in senza segno.

[Nota: altrimenti, l'unico caso rimanente è che entrambi gli operandi sono int]


3
... fintanto che l'altro tipo non è né doublelong double.
CB Bailey,

1
@Charles: corretto. Ho citato la sezione pertinente della norma per chiarire ulteriormente.
Nawaz,

Quindi un intero può essere sempre convertito in float senza alcuna perdita di dati? (es. azzerando l'esponente e usando tutto per la mantissa)?
Marco A.

1
Questa risposta non è aggiornata. Suggerisci aggiornamento. In particolare, long longe unsigned longnon affrontato proprio qui.
chux - Ripristina Monica

@MarcoA. un 32 bit floatnon ha abbastanza bit nella mantissa (24 bit per IEEE-754 ) per un 32 bit int, quindi potrebbe esserci una perdita di dati. Un 64 bit doubledovrebbe andare bene.
Mark Ransom il

17

Dato che le altre risposte non parlano delle regole in C ++ 11 eccone una. Dallo standard C ++ 11 (bozza n3337) §5 / 9 (sottolineato la differenza):

Questo modello è chiamato le solite conversioni aritmetiche , che sono definite come segue:

- Se uno degli operandi è del tipo di enumerazione con ambito, non viene eseguita alcuna conversione; se l'altro operando non ha lo stesso tipo, l'espressione non è corretta.

- Se uno degli operandi è di tipo long double, l'altro deve essere convertito in long double.

- Altrimenti, se uno degli operandi è doppio, l'altro deve essere convertito in doppio.

- In caso contrario, se uno degli operandi è float, l'altro deve essere convertito in float.

- In caso contrario, le promozioni integrali devono essere eseguite su entrambi gli operandi. Quindi si applicano le seguenti regole agli operandi promossi:

- Se entrambi gli operandi hanno lo stesso tipo, non è necessaria alcuna ulteriore conversione.

- In caso contrario, se entrambi gli operandi hanno tipi di numeri interi con segno o entrambi hanno tipi di numeri interi senza segno, l'operando con il tipo di rango di conversione intero inferiore deve essere convertito nel tipo di operando con rango maggiore.

- Altrimenti, se l'operando con tipo intero senza segno ha un rango maggiore o uguale al rango del tipo di altro operando, l'operando con tipo intero con segno deve essere convertito nel tipo di operando con tipo intero senza segno.

- In caso contrario, se il tipo di operando con tipo intero con segno può rappresentare tutti i valori del tipo di operando con tipo intero senza segno, l'operando con tipo intero senza segno deve essere convertito nel tipo di operando con tipo intero con segno.

- In caso contrario, entrambi gli operandi devono essere convertiti nel tipo intero senza segno corrispondente al tipo di operando con tipo intero con segno.

Vedi qui per un elenco che viene aggiornato di frequente.


1
Queste regole erano le stesse in tutte le versioni di C ++, ad eccezione delle enumerazioni con ambito aggiunte naturalmente in C ++ 11
MM

6

Questa risposta è diretta in gran parte a un commento fatto da @ RafałDowgird:

"La dimensione minima delle operazioni è int." - Sarebbe molto strano (che dire delle architetture che supportano in modo efficiente operazioni char / short?) È davvero nelle specifiche C ++?

Tieni presente che lo standard C ++ ha l'importantissima regola "come se". Vedere la sezione 1.8: Esecuzione del programma:

3) Questa disposizione è talvolta chiamata regola "come se", poiché un'implementazione è libera di ignorare qualsiasi requisito dello Standard purché il risultato sia come se il requisito fosse stato rispettato, per quanto è possibile determinare dall'osservabile comportamento del programma.

Il compilatore non può impostare una dimensione intdi 8 bit, anche se era il più veloce, poiché lo standard impone un minimo di 16 bit int.

Pertanto, nel caso di un computer teorico con operazioni a 8 bit superveloci, la promozione implicita intper l'aritmetica potrebbe essere importante. Tuttavia, per molte operazioni, non è possibile stabilire se il compilatore ha effettivamente eseguito le operazioni con la precisione di un inte quindi convertito in un chararchivio per la propria variabile, o se le operazioni sono state eseguite in caratteri tutti insieme.

Ad esempio, considera unsigned char = unsigned char + unsigned char + unsigned char, dove l'addizionale verrebbe traboccato (ipotizziamo un valore di 200 per ciascuno). Se lo avessi promosso int, otterrai 600, che verrebbero quindi implicitamente buttati giù in un unsigned char, che avvolgerebbe il modulo 256, dando così un risultato finale di 88. Se non avessi fatto tali promozioni, dovresti passare tra le prime due aggiunte, che ridurrebbe il problema da 200 + 200 + 200a 144 + 200, che è 344, che riduce a 88. In altre parole, il programma non conosce la differenza, quindi il compilatore è libero di ignorare il mandato per eseguire operazioni intermedie intse gli operandi hanno una classifica inferiore a int.

Questo è vero in generale per addizione, sottrazione e moltiplicazione. Non è vero in generale per divisione o modulo.


4

Se si escludono i tipi senza segno, esiste una gerarchia ordinata: char firmato, short, int, long, long long, float, double, long double. Innanzitutto, tutto ciò che precede int in quanto sopra verrà convertito in int. Quindi, in un'operazione binaria, il tipo con classifica inferiore verrà convertito in superiore e i risultati saranno il tipo di superiore. (Noterai che, dalla gerarchia, ogni volta che sono coinvolti un punto mobile e un tipo integrale, il tipo integrale verrà convertito nel tipo a virgola mobile.)

Unsigned complica un po 'le cose: disturba la classifica e parti della classifica diventano definite implementazione. Per questo motivo, è meglio non mescolare firmato e non firmato nella stessa espressione. (La maggior parte degli esperti di C ++ sembra evitare di non firmare a meno che non siano coinvolte operazioni bit per bit. Questo è, almeno, ciò che Stroustrup raccomanda.)


3
Stroustrup può consigliare ciò che gli piace, ma l'utilizzo di un segno intper un numero che non deve mai essere negativo è uno spreco completo di un intero 50% dell'intervallo disponibile. Non sono certamente uno Stroustrup, ma lo uso unsignedper impostazione predefinita e signedsolo quando ho un motivo.
underscore_d

1
Va tutto bene, underscore_d, fino al giorno in cui devi sottrarre. Il problema principale con i numeri senza segno in C ++ è che quando si esegue la sottrazione, rimangono senza segno. Supponiamo quindi di scrivere una funzione per vedere se uno std :: vector è in ordine. Potresti scrivere bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;e poi saresti infastidito nello scoprire che si arresta in modo anomalo per i vettori vuoti perché size () - 1 restituisce 18446744073709551615.
jorgbrown

3

La mia soluzione al problema ha WA (risposta sbagliata), poi ho cambiato uno di intal long long inte ha dato AC (accetta) . In precedenza, stavo cercando di fare long long int += int * inte dopo averlo correttolong long int += long long int * int . Googling mi è venuto in mente,

1. Conversioni aritmetiche

Condizioni per la conversione del tipo:

Condizioni soddisfatte ---> Conversione

  • Entrambi gli operandi sono di tipo long double . ---> L'altro operando viene convertito nel tipo long double .

  • Condizione precedente non soddisfatta ed entrambi gli operandi sono di tipo doppio . ---> L'altro operando viene convertito nel tipo double .

  • Condizioni precedenti non soddisfatte ed entrambi gli operandi sono di tipo float . ---> L'altro operando viene convertito in tipo float .

  • Condizioni precedenti non soddisfatte (nessuno degli operandi è di tipo mobile). ---> Le promozioni integrate vengono eseguite sugli operandi come segue:

    • Se uno degli operandi è di tipo unsigned long , l'altro operando viene convertito in type unsigned long .
    • Se la condizione precedente non è soddisfatta e se uno degli operandi è di tipo long e l'altro di tipo unsigned int , entrambi gli operandi vengono convertiti in type unsigned long .
    • Se le due condizioni precedenti non sono soddisfatte e se uno degli operandi è di tipo long , l'altro operando viene convertito in type long .
    • Se le tre condizioni precedenti non sono soddisfatte e se uno degli operandi è di tipo unsigned int , l'altro operando viene convertito in tipo unsigned int .
    • Se nessuna delle condizioni precedenti è soddisfatta, entrambi gli operandi vengono convertiti nel tipo int .

2 Regole di conversione intere

  • Promozioni intere:

I tipi interi più piccoli di int vengono promossi quando viene eseguita un'operazione su di essi. Se tutti i valori del tipo originale possono essere rappresentati come int, il valore del tipo più piccolo viene convertito in un int; in caso contrario, viene convertito in un int senza segno. Le promozioni intere vengono applicate come parte delle solite conversioni aritmetiche a determinate espressioni di argomenti; operandi degli operatori unari +, - e ~; e operandi degli operatori di turno.

  • Rango di conversione intero:

    • Nessun tipo intero con segno deve avere lo stesso rango, anche se hanno la stessa rappresentazione.
    • Il rango di un tipo intero con segno deve essere maggiore del rango di qualsiasi tipo intero con segno con minore precisione.
    • Il rango di long long intdeve essere maggiore del rango di long int, che deve essere maggiore del rango di int, che deve essere maggiore del rango di short int, che deve essere maggiore del rango disigned char .
    • Il rango di qualsiasi tipo intero senza segno deve essere uguale al rango del tipo intero con segno corrispondente, se presente.
    • Il rango di qualsiasi tipo intero standard deve essere maggiore del rango di qualsiasi tipo intero esteso con la stessa larghezza.
    • Il grado di chardeve essere uguale al grado di signed chare unsigned char.
    • Il rango di qualsiasi tipo intero con segno esteso rispetto ad un altro tipo intero con segno esteso con la stessa precisione è definito dall'implementazione ma è ancora soggetto alle altre regole per determinare il rango di conversione di numero intero.
    • Per tutti i tipi interi T1, T2 e T3, se T1 ha un rango maggiore di T2 e T2 ha un rango maggiore di T3, allora T1 ha un rango maggiore di T3.
  • Conversioni aritmetiche usuali:

    • Se entrambi gli operandi hanno lo stesso tipo, non è necessaria alcuna ulteriore conversione.
    • Se entrambi gli operandi sono dello stesso tipo intero (con o senza segno), l'operando con il tipo di rango di conversione intero inferiore viene convertito nel tipo di operando con rango maggiore.
    • Se l'operando con tipo intero senza segno ha un rango maggiore o uguale al rango del tipo dell'altro operando, l'operando con tipo intero con segno viene convertito nel tipo di operando con tipo intero senza segno.
    • Se il tipo di operando con tipo intero con segno può rappresentare tutti i valori del tipo di operando con tipo intero senza segno, l'operando con tipo intero senza segno viene convertito nel tipo di operando con tipo intero con segno.
    • Altrimenti, entrambi gli operandi vengono convertiti nel tipo intero senza segno corrispondente al tipo di operando con tipo intero con segno. Operazioni specifiche possono aggiungere o modificare la semantica delle normali operazioni aritmetiche.

1

L'intero capitolo 4 parla di conversioni, ma penso che dovresti essere principalmente interessato a queste:

4.5 Promozioni integrali [conv.prom]
Un valore di tipo char, carattere char, unsigned char, short int o unsigned short int può essere convertito in un valore di tipo int se int può rappresentare tutti i valori del tipo di origine; in caso contrario
, il valore di origine può essere convertito in un valore di tipo unsigned int.
Un valore di tipo wchar_t (3.9.1) o un tipo di enumerazione (7.2) può essere convertito in un valore del primo
dei seguenti tipi che può rappresentare tutti i valori del tipo sottostante: int, unsigned int,
long o unsigned lungo.
Un valore per un campo di bit integrale (9.6) può essere convertito in un valore di tipo int se int può rappresentare tutti
i valori del campo di bit; in caso contrario, può essere convertito in unsigned int se unsigned int può replicare
rinviare tutti i valori del campo bit. Se il campo bit è ancora più grande, non viene applicata alcuna promozione integrale. Se il
bit-field ha un tipo enumerato, viene trattato come qualsiasi altro valore di quel tipo a fini di promozione.
Un valore di tipo bool può essere convertito in un valore di tipo int, con false che diventa zero e true che
diventa uno.
Queste conversioni sono chiamate promozioni integrali.

4.6 Promozione in virgola mobile [conv.fpprom]
Un valore di tipo float può essere convertito in un valore di tipo double. Il valore è invariato.
Questa conversione si chiama promozione in virgola mobile.

Pertanto, tutte le conversioni che coinvolgono float: il risultato è float.

Solo quello che coinvolge entrambi int: il risultato è int: int / int = int


1

Il tipo di espressione, quando non entrambe le parti sono dello stesso tipo, verrà convertito nel più grande di entrambi. Il problema qui è capire quale è più grande dell'altro (non ha nulla a che fare con la dimensione in byte).

Nelle espressioni in cui sono coinvolti un numero reale e un numero intero, l'intero verrà promosso a numero reale. Ad esempio, in int + float, il tipo di espressione è float.

L'altra differenza è legata alla capacità del tipo. Ad esempio, un'espressione che coinvolge un int e un long int risulterà del tipo long int.


2
Questo non è vero. Sulle piattaforme may a longè "più grande" di a floatma qual è il tipo di long+ float?
CB Bailey,

1
-1: Cosa intendi per più grande ? Un galleggiante è più grande di un int? O viceversa ?
Paul R,

2
Grazie per i vostri commenti. Sì, la dimensione in byte qui non è affatto interessante. Come viene fuori, ovviamente mettere il più grande in corsivo non è sufficiente per spiegare la risposta. Ad ogni modo, non ha senso spiegarlo più in profondità, dato che ora ci sono altre risposte molto approfondite.
Baltasarq,

-2

Avvertimento!

Le conversioni avvengono da sinistra a destra.

Prova questo:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0

9
Ciò non è dovuto alla conversione ma alla precedenza dell'operatore. j + i * kporterebbe a 101.
gartenriese,
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.