Como o Java lida com estoques e estouros inteiros?
A partir disso, como você verificaria / testaria se isso está ocorrendo?
java
integer
integer-overflow
KushalP
fonte
fonte
checked
onde eu sei. Não o vejo muito usado, e digitarchecked { code; }
é tão trabalho quanto chamar um método.csc /checked ...
ou defina a propriedade no painel de propriedades do projeto no Visual Studio.Respostas:
Se estourar, retornará ao valor mínimo e continuará a partir daí. Se o fluxo for insuficiente, ele retornará ao valor máximo e continuará a partir daí.
Você pode verificar isso da seguinte maneira:
(você pode substituir
int
porlong
para executar as mesmas verificaçõeslong
)Se você acha que isso pode ocorrer com mais frequência, considere usar um tipo de dados ou objeto que possa armazenar valores maiores, por exemplo,
long
ou talvezjava.math.BigInteger
. O último não transborda, praticamente, a memória JVM disponível é o limite.Se você já estiver no Java8, poderá usar os métodos new
Math#addExact()
e newMath#subtractExact()
que lançarão umArithmeticException
overflow.O código fonte pode ser encontrado aqui e aqui, respectivamente.
Obviamente, você também pode usá-los imediatamente, em vez de ocultá-los em um
boolean
método utilitário.fonte
+100, -100
, respectivamente. Se você estivesse adicionando um a um número inteiro Java, o processo ficaria assim quando estourasse.98, 99, 100, -100, -99, -98, ...
. Isso faz mais sentido?Math#addExact
é a sintaxe normalmente utilizado quando se escreve javadocs - enquanto normalmente que seria convertido paraMath.addExact
, por vezes, a outra forma apenas fura ao redorIf it underflows, it goes back to the maximum value and continues from there.
- você parece ter confundido o estouro com o estouro negativo. underflow em números inteiros acontece o tempo todo (quando o resultado é uma fração).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:
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:
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):
(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:
(~ 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.
fonte
Por padrão, a matemática int e long do Java envolve silenciosamente o estouro e o estouro. (Operações de número inteiro em outros tipos de número inteiro são executadas promovendo primeiro os operandos para int ou long, conforme JLS 4.2.2 .)
A partir de Java 8,
java.lang.Math
forneceaddExact
,subtractExact
,multiplyExact
,incrementExact
,decrementExact
enegateExact
métodos estáticos para ambos int e longos argumentos que realizam a operação denominada, jogando ArithmeticException no estouro. (Não existe um método divideExact - você deverá verificar o caso especial (MIN_VALUE / -1
).)No Java 8, o java.lang.Math também fornece
toIntExact
a conversão de um long para um int, lançando ArithmeticException se o valor do long não se encaixar em um int. Isso pode ser útil para, por exemplo, calcular a soma de entradas usando matemática longa não verificada e depois usartoIntExact
para converter para int no final (mas tome cuidado para não deixar sua soma transbordar).Se você ainda estiver usando uma versão mais antiga do Java, o Google Guava fornecerá métodos estáticos IntMath e LongMath para adição, subtração, multiplicação e exponenciação verificadas (jogando em excesso). Essas classes também fornecem métodos para calcular fatoriais e coeficientes binomiais que retornam
MAX_VALUE
ao estouro (o que é menos conveniente para verificar). Classes utilitárias primitivos de goiaba,SignedBytes
,UnsignedBytes
,Shorts
eInts
, proporcionarcheckedCast
métodos para estreitar tipos maiores (jogando sobre IllegalArgumentException sob / descarga, não ArithmeticException), bem comosaturatingCast
métodos que retornoMIN_VALUE
ouMAX_VALUE
em excesso.fonte
Java não faz nada com excesso de número inteiro para tipos primitivos int ou long e ignora o excesso com números inteiros positivos e negativos.
Esta resposta descreve primeiro o excesso de número inteiro, fornece um exemplo de como isso pode acontecer, mesmo com valores intermediários na avaliação da expressão e, em seguida, fornece links para recursos que fornecem técnicas detalhadas para prevenir e detectar o excesso de número inteiro.
Aritmética inteira e expressões que resultam em estouro inesperado ou não detectado são um erro de programação comum. O excesso inesperado ou não detectado de números inteiros também é um problema de segurança explorável bem conhecido, especialmente porque afeta objetos de matriz, pilha e lista.
O estouro pode ocorrer em uma direção positiva ou negativa, onde o valor positivo ou negativo estaria além dos valores máximos ou mínimos do tipo primitivo em questão. O estouro pode ocorrer em um valor intermediário durante a avaliação da expressão ou operação e afetar o resultado de uma expressão ou operação em que o valor final deveria estar dentro do intervalo.
Às vezes, o estouro negativo é chamado erroneamente de estouro. Subfluxo é o que acontece quando um valor está mais próximo de zero do que a representação permite. O subfluxo ocorre na aritmética inteira e é esperado. O fluxo insuficiente inteiro acontece quando uma avaliação inteira está entre -1 e 0 ou 0 e 1. O que seria um resultado fracionário trunca para 0. Isso é normal e esperado com aritmética inteira e não é considerado um erro. No entanto, isso pode levar ao código que lança uma exceção. Um exemplo é uma exceção "ArithmeticException: / by zero" se o resultado do underflow inteiro for usado como um divisor em uma expressão.
Considere o seguinte código:
que resulta na atribuição de x a 0 e na avaliação subsequente de bigValue / x lança uma exceção, "ArithmeticException: / by zero" (ou seja, divide por zero), em vez de y receber o valor 2.
O resultado esperado para x seria 858.993.458, que é menor que o valor int máximo de 2.147.483.647. No entanto, o resultado intermediário da avaliação de Integer.MAX_Value * 2 seria 4.294.967.294, que excede o valor máximo int e é -2 de acordo com 2s, complementando representações inteiras. A avaliação subsequente de -2 / 5 é avaliada como 0 e é atribuída a x.
Reorganizando a expressão para computar x para uma expressão que, quando avaliada, divide antes da multiplicação, o seguinte código:
resulta em x sendo atribuído 858.993.458 e y sendo atribuído 2, o que é esperado.
O resultado intermediário de bigValue / 5 é 429.496.729, que não excede o valor máximo para um int. A avaliação subsequente de 429.496.729 * 2 não excede o valor máximo de um int e o resultado esperado é atribuído a x. A avaliação para y então não se divide por zero. As avaliações para x e y funcionam conforme o esperado.
Os valores inteiros Java são armazenados e se comportam de acordo com o 2s complementam representações inteiras assinadas. Quando um valor resultante for maior ou menor que o valor inteiro máximo ou mínimo, o valor inteiro do complemento de 2 resultará. Em situações não expressamente projetadas para usar o comportamento do complemento 2s, que é a maioria das situações aritméticas inteiras comuns, o valor resultante do complemento 2s causará uma lógica de programação ou erro de computação, como foi mostrado no exemplo acima. Um excelente artigo da Wikipedia descreve números inteiros binários de 2s aqui: Complemento de dois - Wikipedia
Existem técnicas para evitar o excesso não intencional de números inteiros. As técnicas podem ser categorizadas como teste de pré-condição, upcasting e BigInteger.
O teste de pré-condição compreende examinar os valores que entram em uma operação ou expressão aritmética para garantir que um estouro não ocorra com esses valores. A programação e o design precisarão criar testes que garantam que os valores de entrada não causem estouro e, em seguida, determinem o que fazer se ocorrerem valores de entrada que causarão estouro.
A upcasting compreende o uso de um tipo primitivo maior para executar a operação ou expressão aritmética e, em seguida, determinar se o valor resultante está além dos valores máximo ou mínimo para um número inteiro. Mesmo com upcasting, ainda é possível que o valor ou algum valor intermediário em uma operação ou expressão esteja além dos valores máximos ou mínimos para o tipo de upcast e cause excesso, o que também não será detectado e causará resultados inesperados e indesejados. Por meio de análise ou pré-condições, pode ser possível evitar o transbordamento com upcasting quando a prevenção sem upcasting não for possível ou prática. Se os números inteiros em questão já forem do tipo primitivo longo, não será possível fazer upcast com os tipos primitivos em Java.
A técnica BigInteger compreende o uso do BigInteger para a operação ou expressão aritmética usando métodos de biblioteca que usam o BigInteger. O BigInteger não transborda. Ele usará toda a memória disponível, se necessário. Seus métodos aritméticos são normalmente apenas um pouco menos eficientes que as operações com números inteiros. Ainda é possível que um resultado usando o BigInteger possa estar além dos valores máximo ou mínimo para um número inteiro; no entanto, o excesso não ocorrerá na aritmética que leva ao resultado. A programação e o design ainda precisam determinar o que fazer se um resultado do BigInteger estiver além dos valores máximos ou mínimos para o tipo de resultado primitivo desejado, por exemplo, int ou long.
O programa CERT do Instituto de Engenharia de Software Carnegie Mellon e a Oracle criaram um conjunto de padrões para a programação segura de Java. Incluídas nos padrões estão as técnicas para prevenir e detectar o excesso de número inteiro. O padrão é publicado como um recurso on-line acessível gratuitamente aqui: O CERT Oracle Secure Coding Standard for Java
A seção da norma que descreve e contém exemplos práticos de técnicas de codificação para impedir ou detectar o excesso de número inteiro está aqui: NUM00-J. Detectar ou impedir o estouro de números inteiros
Também estão disponíveis o formulário de livro e o formato PDF do CERT Oracle Secure Coding Standard para Java.
fonte
Tendo acabado de me deparar com esse problema, aqui está minha solução (para multiplicação e adição):
fique à vontade para corrigir se está errado ou se pode ser simplificado. Fiz alguns testes com o método de multiplicação, principalmente casos extremos, mas ainda pode estar errado.
fonte
int*int
, eu acho que simplesmente lançarlong
e ver se o resultado se encaixaint
seria a abordagem mais rápida. Poislong*long
, se alguém normaliza os operandos para serem positivos, pode-se dividir cada uma nas metades superior e inferior de 32 bits, promover cada uma delas por muito tempo (tenha cuidado com as extensões de sinal!) E depois calcular dois produtos parciais [uma das metades superiores deve seja zero].Existem bibliotecas que fornecem operações aritméticas seguras, que verificam o excesso / o excesso de números inteiros. Por exemplo, IntMath.checkedAdd (int a, int b) do Guava retorna a soma de
a
eb
, desde que não transborde, e lançaArithmeticException
sea + b
exceder naint
aritmética assinada .fonte
Math
classe contém código semelhante.Envolve.
por exemplo:
impressões
fonte
Eu acho que você deve usar algo parecido com isto e é chamado Upcasting:
Você pode ler mais aqui: Detectar ou impedir o excesso de números inteiros
É uma fonte bastante confiável.
fonte
Ele não faz nada - o under / overflow simplesmente acontece.
Um "-1" resultante de uma computação que estourou demais não é diferente do "-1" resultante de qualquer outra informação. Portanto, você não pode dizer através de algum status ou inspecionando apenas um valor se está excedendo.
Mas você pode ser inteligente em relação aos seus cálculos para evitar transbordamentos, se isso interessar, ou pelo menos saber quando isso acontecerá. Qual é a sua situação?
fonte
fonte
Eu acho que isso deve ficar bem.
fonte
Há um caso, que não é mencionado acima:
vai produzir:
Este caso foi discutido aqui: O excesso de número inteiro produz Zero.
fonte