2013-06-26 12 views
10

consideri una tipica funzione di valore assoluto (dove per amor di discussione del tipo integrale di dimensione massima è lunga):Esiste un modo sicuro per ottenere il valore assoluto senza segno di un intero con segno, senza attivare l'overflow?

unsigned long abs(long input); 

Un'implementazione ingenuo di questo potrebbe essere simile:

unsigned long abs(long input) 
{ 
    if (input >= 0) 
    { 
     // input is positive 
     // We know this is safe, because the maximum positive signed 
     // integer is always less than the maximum positive unsigned one 
     return static_cast<unsigned long>(input); 
    } 
    else 
    { 
     return static_cast<unsigned long>(-input); // ut oh... 
    } 
} 

Questo il codice attiva un comportamento indefinito, in quanto la negazione di input può essere eccessiva e l'attivazione dell'overflow dei numeri interi con segno è un comportamento indefinito. Ad esempio, su macchine con complemento a 2 secondi, il valore assoluto di std::numeric_limits<long>::min() sarà 1 maggiore di std::numeric_limits<long>::max().

Cosa può fare un autore di librerie per risolvere questo problema?

risposta

16

È possibile eseguire prima il cast della variante senza segno. Ciò fornisce un comportamento ben definito. Se invece il codice è simile al seguente:

unsigned long abs(long input) 
{ 
    if (input >= 0) 
    { 
     // input is positive 
     return static_cast<unsigned long>(input); 
    } 
    else 
    { 
     return -static_cast<unsigned long>(input); // read on... 
    } 
} 

invochiamo due operazioni ben definite. Conversione del numero intero con segno a quello non firmato è ben definita dalla N3485 4,7 [conv.integral]/2:

Se il tipo di destinazione è senza segno, il valore risultante è il minimo intero senza segno congruente al numero intero sorgente (modulo 2^n dove n è il numero di bit usati per rappresentare il tipo senza segno). [Nota: nella rappresentazione a complemento a due, questa conversione è concettuale e non vi è alcun cambiamento nel modello di bit (se non vi è alcun troncamento). - end note]

Questo in pratica dice che quando si effettua la conversione specifica di passare da firmata a non firmata, si può assumere wraparound in stile senza segno.

La negazione del numero intero senza segno è ben definita dalla 5.3.1 [expr.unary.op]/8:

Il negativo di una quantità senza segno viene calcolato sottraendo il valore da 2^n, dove n è il numero di bit nell'operando promosso.

Questi due requisiti costringono efficacemente le implementazioni a funzionare come una macchina con complemento a 2, anche se la macchina sottostante è un complemento a 1 o una macchina di grandezza firmata.

+1

Bella risposta, anche se alla tua stessa domanda, ma +1. – Bathsheba