2016-04-18 26 views
8

Come posso convertire in modo ottimale uno uint32_t in uno int32_t rapidamente con il wrapping, in C++?Converti un uint32_t in un int32_t senza rischio di overflow o eccessiva complessità

Alcuni tentativi:

uint32_t y = UINT32_MAX; 
int32_t x = (int32_t)y; // UB on overflow 
int32_t x = *(int32_t*)&y; // does this violate strict aliasing? 
+2

Non è teoricamente impossibile convertirlo senza rischio di overflow? – pingul

+0

Cosa dovrebbe fare quando si verifica un overflow? Completa il complemento a due (che è il "comportamento indefinito" che in realtà diventa il 99% di tutti i compilatori/CPU del mondo)? – ShadowRanger

+0

Presumo che 'y' sia dichiarato come' uint32_t y; '(sarebbe opportuno aggiungerlo alla domanda) –

risposta

5

int32_t x = (int32_t)y; non è troppo pieno e non UB. Overflow è quando un'operazione aritmetica produce un risultato al di fuori dell'intervallo di valori rappresentabili. Tuttavia, una conversione non è un'operazione aritmetica.

Questa situazione è comportamento definito dall'implementazione. Tutte le implementazioni di cui sono a conoscenza definiscono il comportamento come non apportare alcun cambiamento nella rappresentazione.

Si noti che non è necessario eseguire il cast qui. Puoi scrivere int32_t x = y;. In termini pratici, questo è più semplice e funzionerà sempre. Così tanto codice si basa su questo che nessun venditore definirà mai nessun altro comportamento (non che abbia comunque ragione di farlo).


int32_t x = *(int32_t*)&y non è UB. Non viola l'aliasing rigoroso perché la versione firmata di un tipo è autorizzata ad aliasare la versione senza firma. Questo codice è garantito per produrre il int32_t con la stessa rappresentazione del corrispondente uint32_t (ad esempio "involucro", poiché questi tipi sono garantiti come complemento a 2).

5
union { 
    int32_t i; 
    uint32_t u; 
} u; 
u.i = ...; 
printf("%" PRIu32 "\n", u.u); 

Questo e memcpy(&uint_var, &int_var, sizeof uint_var) sono i due modi standard per fare una tale conversione senza invocare un comportamento indefinito.

Consulta anche:

+0

Che dire delle rappresentazioni di trap per l'int firmato? –

+0

@RolandIllig Gli interi di larghezza esatta non hanno bit di riempimento e pertanto non possono avere rappresentazioni di trap. – a3f

+0

La lettura del punto 6.2.6.2p2 del C99 sembra dire diversamente. Mentre potrebbe non esserci un bit di riempimento, può comunque esserci una rappresentazione di trap. –

1
#include <assert.h> 
#include <limits.h> 
#include <stdlib.h> 

int makesigned(unsigned x) { 
    if (x <= (unsigned) INT_MAX) { 
    return (int) x; 
    } 

    /* assume 2's complement */ 
    if (x >= (unsigned) INT_MIN) { 
    return 0 - (int)(-x); 
    } 

    abort(); 
    return 0; 
} 

int main(void) { 
    assert(makesigned(0) == 0); 
    assert(makesigned(INT_MAX) == INT_MAX); 
    assert(makesigned(UINT_MAX) == -1); 
    assert(makesigned(INT_MIN) == INT_MIN); 
    return 0; 
}