2015-12-21 15 views
6

Ho due variabili (test1 e test2), entrambe non firmate. Ho bisogno di controllare quale di loro è più grande.sottrazione tra valori non firmati - risultato imprevisto

Sto cercando di capire cosa succede se si verifica un overflow.

Il mio primo test è stato fatto con uint8_t (char) tipo di dati:

#include <stdio.h> 
#include <stdint.h> 
#include <math.h> 

int main() 
{ 
    uint8_t test1 = 0; 
    printf("test1 = %d\n", test1); 

    uint8_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint8_t 
    printf("test2 = %d\n", test2); 

    uint8_t test3 = test1 - test2; 
    printf("test1 - test2 = %d\n", test3); 

    if ((test1 - test2) == 0) 
     printf("test1 == test2\n"); 
    if ((test1 - test2) > 0) 
     printf("test1 > test2\n"); 
    if ((test1 - test2) < 0) 
     printf("test1 < test2\n"); 

    if (test3 == 0) 
     printf("test1 == test2\n"); 
    if (test3 > 0) 
     printf("test1 > test2\n"); 
    if (test3 < 0) 
     printf("test1 < test2\n"); 

    return 0; 
} 

uscita:

test1 = 0                                      
test2 = 255                                      
test1 - test2 = 1                                    
test1 < test2                                     
test1 > test2 

Cosa? Effettuare la sottrazione e salvarla in una variabile, quindi verificarla, è diversa da controllando la sottrazione al volo?

mio secondo test è stato fatto con uint32_t (lungo) tipo di dati:

#include <stdio.h> 
#include <stdint.h> 
#include <math.h> 

int main() 
{ 
    uint32_t test1 = 0; 
    printf("test1 = %d\n", test1); 

    uint32_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint32_t 
    printf("test2 = %lu\n", test2); 

    uint32_t test3 = test1 - test2; 
    printf("test1 - test2 = %d\n", test3); 

    if ((test1 - test2) == 0) 
     printf("test1 == test2\n"); 
    if ((test1 - test2) > 0) 
     printf("test1 > test2\n"); 
    if ((test1 - test2) < 0) 
     printf("test1 < test2\n"); 

    if (test3 == 0) 
     printf("test1 == test2\n"); 
    if (test3 > 0) 
     printf("test1 > test2\n"); 
    if (test3 < 0) 
     printf("test1 < test2\n"); 

    return 0; 
} 

uscita:

test1 = 0                                      
test2 = 4294967295                                    
test1 - test2 = 1                                    
test1 > test2                                     
test1 > test2 

Cosa ??? Ora effettuare la sottrazione e salvarla in una variabile, quindi controllarla, è uguale a controllando la sottrazione al volo?

SO mi aspettavo che la sottrazione tra i valori senza segno (senza un cast esplicito) restituisce sempre un valore> = 0. Ma fare la sottrazione all'interno della IF porta a risultati inaspettati.

Ora sono confuso. Qualcuno può spiegarmi questo comportamento?

+2

Alla 3a riga nel corpo principale: 'uint8_t test2 = pow (2, 8 * sizeof (test1)) - 1; 'questo non è molto sensato, se si prende 2 alla potenza' 8 * sizeof (test1) ', questo supererà un' uint8_t' e otterrà il valore zero. Poiché non è firmato, l'overflow ha sempre valori ben definiti. Quindi, invece di scrivere questo, dovresti invece scrivere '-1', per chiarezza. –

+1

Stai facendo un lavoro molto duro - pensi che questa domanda potrebbe essere semplificata solo all'assegnazione del test 1 e del test 2, quindi esempi di espressioni che producono il risultato che ti aspetti non si aspettano? Sembra che ci sia una grande quantità di codice superfluo da attraversare qui. – Clifford

+1

@ChrisBeck: o 'uint8_t test1 = ~ 0;' piuttosto che assegnare un valore negativo a un tipo senza segno, o meglio ancora usare 'UCHAR_MAX' o' std :: numeric_limits :: max() ' – Clifford

risposta

5

A causa del tipo di promozione , il vostro uint8 sarà promosso a piattaforma basata su int, che rischia di essere superiore a 8 bit (di solito a 32 bit) nella maggior parte dei nostri giorni Computer.

questa linea:

if ((test1 - test2) < 0) 

se equivalente a

if ((int)((int)test1 - (int)test2) < 0) = -255 < 0 

che è vero.

Tuttavia, per questa linea,

uint8_t test3 = test1 - test2; 

è equivalente a

uint8_t test3 = (uint8_t)((int)test1 - (int)test2); //(uint8_t)(-255) = 1!!; 

Così sia test1 - test2 < 0 e test3 > 0 sono (Benvenuto al mondo del computer!) Corretta!

Questo spiega il risultato.

Ma per uint32, perché è di rango "superiore" rispetto nativo int, quindi non è "promosso" e rimane come uint32 tutto il tempo, o in altre parole

questa linea:

if ((test1 - test2) > 0) 

se equivalente a

if ((uint32_t)((uint32_t)test1 - (uint32_t)test2) > 0) = large positive number > 0 

e questa linea

uint32_t test3 = test1 - test2; 

è equivalente a

uint32_t test3 = (uint32_t)test1 - (uint32_t)test2; //also large positive number 

così avete sia test1 - test2 > 0 e test3 > 0.

Quindi, nei due casi, entrambi possono essere corretti solo se in esecuzione su una macchina a 8 bit. Oppure, supponiamo che in futuro, nativo int è a 64 bit, quindi entrambi i casi saranno corretti troppo ...

+0

Nel primo esempio dopo la promozione l'espressione è '-255 <0' – Clifford

+0

corretta. Grazie – Ian

+0

Mi piace la risposta @Clifford ma la scelgo perché ha un buon esempio basato sul mio codice di test. Grazie a tutti voi ragazzi! – Suxsem

9

Si applica l'un po 'arcano type promotion rules. Nel primo esempio in cui gli operandi sono uint8_t, per l'espressione:

test1 - test2 

entrambi vengono implicitamente promosso intprima sottrazione, e l'espressione stessa ha tipo int.

+0

+10 spiegazione abbastanza semplice – Ian

+0

Sfortunatamente, non è corretto. Le "regole di promozione del tipo arcano" in realtà non si applicano a 'char unsigned 'o a tipi a 8 bit' unsigned'. Questo in realtà è un caso in cui l'avvertenza nella parte inferiore del link fornito "Sfortunatamente, questa non è una dichiarazione precisa di queste regole" è rilevante. Le operazioni matematiche su tipi non firmati utilizzano l'aritmetica modulo. – Peter

+0

@Peter: come ho detto, sono arcane e consiglierei di prevedere l'inaspettato, e di non usare mai char o tipi brevi come oggetti aritmetici, e di evitare espressioni misti firmate e non firmate. Detto questo, non sono convinto che tu abbia ragione; da C99 6.3.1.1: * "Se un int può rappresentare tutti i valori del tipo originale, il valore viene convertito in un int, altrimenti viene convertito in un int unsigned." *. Quindi, mentre è vero che l'aritmetica del modulo si verifica su tipi senza segno, nel momento in cui viene valutato l'operatore '-', gli operandi non sono più senza segno. – Clifford

1

In realtà quando si sta sottraendo come

0 - 255; // It results -255 

e si sta cercando di conservarlo in int unsigned così memorizzato in questo modo:

-1 in Unsigned int (1 Byte) as 255 
-2 as 254 
-3 as 253 
. 
. 
. 
-255 as 1 

Ecco perché hai questo ans. Il motivo alla base di questo è il modo in cui memorizza le variabili in cui usa i complimenti di 1 o 2 non hanno idea ma è sicuro che sia correlato a quello

+0

Questo in realtà non spiega all'OP perché dopo 'test3 = test1 - test2',' test3! = Test1 - test2' (che è forse la domanda più semplice e succinta che avrebbe dovuto fare). – Clifford

+0

in realtà stavo spiegando che se il calcolo è stato fatto usando il byte senza segno allora avrai il risultato sopra ma quando lo proviamo direttamente in if (....) poi lo tratterà come doppio non come byte senza segno .. Mi spiace di averlo non ha detto che .. Questo è quello che so di questo. –

+0

Apprezzo quello che stavi dicendo, ma non è una risposta alla domanda in mano. E immagino tu intenda int non raddoppiare? – Clifford

Problemi correlati