Bem, no que diz respeito aos tipos inteiros primitivos, o Java não suporta Over / Underflow (para float e double o comportamento é diferente, ele será liberado para +/- infinito, como exige o IEEE-754).
Ao adicionar dois int's, você não receberá nenhuma indicação quando ocorrer um estouro. Um método simples para verificar se há estouro é usar o próximo tipo maior para realmente executar a operação e verificar se o resultado ainda está dentro do intervalo para o tipo de fonte:
public int addWithOverflowCheck(int a, int b) {
// the cast of a is required, to make the + work with long precision,
// if we just added (a + b) the addition would use int precision and
// the result would be cast to long afterwards!
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
throw new RuntimeException("Overflow occured");
} else if (result < Integer.MIN_VALUE) {
throw new RuntimeException("Underflow occured");
}
// at this point we can safely cast back to int, we checked before
// that the value will be withing int's limits
return (int) result;
}
O que você faria no lugar das cláusulas de lançamento depende dos requisitos de seus aplicativos (lançamento, descarga para min / max ou apenas faça o log). Se você deseja detectar estouro em operações longas, não tem sorte com os primitivos, use o BigInteger.
Edit (21/05/2014): Como essa pergunta parece ser referida com bastante frequência e eu tive que resolver o mesmo problema, é muito fácil avaliar a condição de estouro pelo mesmo método que uma CPU calcula seu sinalizador V.
É basicamente uma expressão booleana que envolve o sinal de ambos os operandos e o resultado:
/**
* Add two int's with overflow detection (r = s + d)
*/
public static int add(final int s, final int d) throws ArithmeticException {
int r = s + d;
if (((s & d & ~r) | (~s & ~d & r)) < 0)
throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");
return r;
}
Em java, é mais simples aplicar a expressão (no if) aos 32 bits inteiros e verificar o resultado usando <0 (isso testará efetivamente o bit de sinal). O princípio funciona exatamente da mesma forma para todos os tipos primitivos inteiros , alterando todas as declarações no método acima para long faz com que funcione por muito tempo.
Para tipos menores, devido à conversão implícita em int (consulte o JLS para operações bit a bit para obter detalhes), em vez de marcar <0, a verificação precisa mascarar explicitamente o bit de sinal (0x8000 para operandos curtos, 0x80 para operandos de byte, ajustar as conversões e declaração de parâmetro adequadamente):
/**
* Subtract two short's with overflow detection (r = d - s)
*/
public static short sub(final short d, final short s) throws ArithmeticException {
int r = d - s;
if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
return (short) r;
}
(Observe que o exemplo acima usa a expressão necessidade de subtrair a detecção de estouro)
Então, como / por que essas expressões booleanas funcionam? Primeiro, algum pensamento lógico revela que um estouro só pode ocorrer se os sinais dos dois argumentos forem os mesmos. Porque, se um argumento é negativo e um positivo, o resultado (de adição) deve estar mais próximo de zero ou, no caso extremo, um argumento é zero, o mesmo que o outro argumento. Como os argumentos por si só não podem criar uma condição de estouro, sua soma também não pode criar um estouro.
Então, o que acontece se ambos os argumentos tiverem o mesmo sinal? Vamos dar uma olhada no caso em que ambos são positivos: adicionar dois argumentos que criam uma soma maior que os tipos MAX_VALUE, sempre produzirá um valor negativo; portanto, um estouro ocorrerá se arg1 + arg2> MAX_VALUE. Agora, o valor máximo que poderia resultar seria MAX_VALUE + MAX_VALUE (o caso extremo dos dois argumentos é MAX_VALUE). Para um byte (exemplo) que significaria 127 + 127 = 254. Observando as representações de bits de todos os valores que podem resultar da adição de dois valores positivos, verifica-se que aqueles que excedem (128 a 254) possuem o bit 7 definido, enquanto tudo o que não transborda (0 a 127) tem o bit 7 (mais alto, sinal) limpo. É exatamente isso que a primeira parte (direita) da expressão verifica:
if (((s & d & ~r) | (~s & ~d & r)) < 0)
(~ s & ~ d & r) se torna verdadeiro, somente se os dois operandos (s, d) forem positivos e o resultado (r) for negativo (a expressão funciona em todos os 32 bits, mas é o único bit em que estamos interessados) é o bit mais alto (sinal), que é verificado pelo <0).
Agora, se ambos os argumentos são negativos, sua soma nunca pode estar mais próxima de zero do que qualquer um dos argumentos, a soma deve estar mais próxima de menos infinito. O valor mais extremo que podemos produzir é MIN_VALUE + MIN_VALUE, que (novamente para exemplo de byte) mostra que, para qualquer valor no intervalo (-1 a -128), o bit de sinal é definido, enquanto qualquer valor transbordante possível (-129 a -256 ) tem o sinal limpo. Portanto, o sinal do resultado novamente revela a condição de estouro. É o que a metade esquerda (s & d & r) verifica para o caso em que ambos os argumentos (s, d) são negativos e um resultado positivo. A lógica é amplamente equivalente ao caso positivo; todos os padrões de bits que podem resultar da adição de dois valores negativos terão o bit de sinal limpo, se e somente se ocorrer um estouro.