2012-09-01 17 views
24

Eventuali duplicati:
How can I check if multiplying two numbers in Java will cause an overflow?Come prevenire l'overflow dei numeri interi nel codice Java?

Supponiamo che io sono un metodo di classe Java, che utilizza * e + operazioni.

 
int foo(int a, int b) { 
    ... // some calculations with + and * 
} 

Come assicurarsi che nessuno di overflow si verifica in foo?

Credo di poter usare sia BigDecimal o sostituire tutti + e * con "wrapper" come:

 
int sum(int a, int b) { 
    int c = a + b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

int prod(int a, int b) { 
    int c = a * b; 
    if (a > 0 && b > 0 && c < 0) 
    throw new MyOverfowException(a, b) 
    return c; 
} 

ci sono modi migliori per assicurarsi che nessun int di overflow si verifica in un metodo Java?

+3

Perché si controlla se è inferiore a zero? Un int può andare ben al di sotto di 0. – 11684

+0

Per essere precisi, questo è il range di un int: -2,147,483,648 a 2,147,483,647 – 11684

+0

Vuoi bloccare i valori? –

risposta

17

È un problema difficile da un punto di vista ingegneristico.

Il sito Secure Coding raccomanda:

  • uso di precondizioni; cioè gamma-controllare gli ingressi in modo che sia impossibile troppopieno,
  • facendo ogni operazione aritmetica singola utilizzando il successivo grande tipo primitivo intero e esplicitamente controllo overflow, o
  • usando BigInteger.

Questo Dr Dobbs article suggerisce la creazione di una libreria di metodi aritmetici primitivi che fanno ogni operazione primitiva con un controllo di overflow esplicito. (Si potrebbe vedere come un'implementazione del punto 2 di cui sopra.) Ma gli autori vanno oltre suggerendo che si utilizza la riscrittura bytecode per sostituire i bytecode aritmetici con chiamate ai metodi equivalenti che incorporano i controlli di overflow.

Sfortunatamente, non è possibile abilitare il controllo di overflow in modo nativo in Java.(Ma lo stesso vale in molte altre lingue, ad es. C, C++ ...)

+0

il secondo di questi è quello che la mia risposta fa – Alnitak

+1

"È un problema difficile da un punto di vista ingegneristico." - Non è così difficile: basta generare codice macchina per controllare il flag di overflow del registro dopo ogni operazione. Questo è ciò che fa il blocco C# "controllato". Il problema è che Java non offre questa opzione come opzione, non perché sia ​​al di là dell'arguzia dell'uomo. – Rich

+0

@Rich: è difficile se non si è in grado di modificare il compilatore Java. La maggior parte delle persone non lo sono! –

5

Somma: verificare se b è maggiore della differenza del valore massimo che è possibile memorizzare in meno il valore di a. Se a e/o b possono essere negativi, è necessario (i) fare attenzione a non ottenere un overflow già per il controllo di differenza e (ii) eseguire un controllo simile per il minimo.

Prodotto: più difficile. Vorrei dividere gli interi in due interi a metà lunghezza (vale a dire se int è a 32 bit, dividerlo in due numeri a 16 bit usando il bit-masking e lo shift). Quindi fai la moltiplicazione e poi guarda se il risultato si adatta a 32 bit.

Tutto a condizione che non si desideri prendere semplicemente long per il risultato temporaneo.

20

Un modo per verificare un overflow consiste nel far promuovere gli operandi a un tipo più grande (di doppio della lunghezza del bit dell'operando originale), quindi eseguire l'operazione, quindi verificare se il valore risultante è troppo grande per il tipo originale, per esempio

int sum(int a, int b) { 
    long r = (long)a + b; 
    if (r >>> 32 != 0) { // no sign extension 
     throw new MyOverflowException(a, b); 
    } 
    return (int)r; 
} 

Se il tipo di originale è un long, dovreste usare BigInteger come quel tipo più grande.

+0

So che questa risposta è vecchia, ma questa soluzione non è garantita per funzionare in quanto il tipo più grande stesso può traboccare e finire in un intervallo che appare normale per il tipo più piccolo. – yitzih

+0

@yitzih hai torto - l'aggiunta di due interi (positivi) non può superare un valore più lungo di 1 bit rispetto all'operando più lungo. Non c'è modo dato che i vincoli della domanda finiscano con un "overflow di tipo più grande" – Alnitak

+0

@yitzihI Ho dimenticato che anche la moltiplicazione è coinvolta, ma anche in questo caso il prodotto di due interi positivi a 31 bit non può superare i 62 bit. – Alnitak

3

Supponiamo che sia a che b siano positivi o negativi, e se il segno di a + b non è uguale al segno di a e b, quindi avviene l'overflow. È possibile utilizzare questa regola per giudicare se avviene l'overflow e generare un'eccezione. Quando rilevi questa espulsione, puoi gestirla secondo il metodo indicato nelle risposte precedenti. Un altro metodo consiste nell'eseguire l'operazione utilizzando il tipo di intervallo più ampio che non verrà sottoposto a overflow. È possibile utilizzare a lungo l'operazione tra interi.

Problemi correlati