Quero lidar com o caso especial em que a multiplicação de dois números causa um estouro. O código é parecido com este:
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
Essa é uma versão simplificada. No programa real a
e b
são obtidos em outro lugar em tempo de execução. O que eu quero alcançar é algo assim:
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
Como você sugere que eu codifique melhor isso?
Update: a
e b
são sempre não negativos no meu cenário.
java
math
long-integer
integer-overflow
Steve McLeod
fonte
fonte
Respostas:
Java 8 tem
Math.multiplyExact
,Math.addExact
etc. para ints e long. Estes lançam umArithmeticException
estouro desmarcado .fonte
Se
a
eb
forem positivos, você pode usar:Se você precisa lidar com números positivos e negativos, é mais complicado:
Aqui está uma pequena mesa que criei para verificar isso, fingindo que o estouro acontece em -10 ou +10:
fonte
n
,n > x
é o mesmo quen > floor(x)
. Para números inteiros positivos, a divisão é um piso implícito. (Para números negativos, ele arredonda para cima)a = -1
eb = 10
, veja minha resposta abaixo.Existem bibliotecas Java que fornecem operações aritméticas seguras, que verificam longo estouro / estouro negativo. Por exemplo, LongMath.checkedMultiply (long a, long b) do Guava retorna o produto de
a
eb
, desde que não estourou, e lançaArithmeticException
sea * b
estourou nalong
aritmética assinada .fonte
Você pode usar java.math.BigInteger em vez disso e verificar o tamanho do resultado (não testei o código):
fonte
Use logaritmos para verificar o tamanho do resultado.
fonte
ceil(log(a)) + ceil(log(b)) > log(Long.MAX)
:?Java tem algo como int.MaxValue? Se sim, então tente
editar: visto Long.MAX_VALUE em questão
fonte
Math.Abs(a)
não funciona sea
forLong.MIN_VALUE
.Aqui está a maneira mais simples que posso pensar
fonte
Roubado de jruby
ATUALIZAÇÃO: Este código é curto e funciona bem; no entanto, ele falha para a = -1, b = Long.MIN_VALUE.
Um possível aprimoramento:
Observe que isso irá capturar alguns overflows sem qualquer divisão.
fonte
Como foi apontado, o Java 8 possui métodos Math.xxxExact que lançam exceções no estouro.
Se você não estiver usando Java 8 para o seu projeto, ainda pode "pegar emprestado" suas implementações, que são bastante compactas.
Aqui estão alguns links para essas implementações no repositório de código-fonte JDK, nenhuma garantia se eles permanecerão válidos, mas em qualquer caso, você deve ser capaz de baixar o código-fonte JDK e ver como eles fazem sua mágica dentro da
java.lang.Math
classe.Math.multiplyExact(long, long)
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l925Math.addExact(long, long)
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l830etc etc.
ATUALIZADO: foram removidos links inválidos para sites de terceiros para links para os repositórios Mercurial do Open JDK.
fonte
Não sei por que ninguém está olhando para uma solução como:
Escolha a para ser o maior dos dois números.
fonte
a
é o maior ou o menor?Eu gostaria de desenvolver a resposta de John Kugelman sem substituí-la editando-a diretamente. Isso funciona para seu caso de teste (
MIN_VALUE = -10
,MAX_VALUE = 10
) por causa da simetria deMIN_VALUE == -MAX_VALUE
, que não é o caso para inteiros de complemento de dois. Na realidadeMIN_VALUE == -MAX_VALUE - 1
,.Quando aplicada ao verdadeiro
MIN_VALUE
eMAX_VALUE
, a resposta de John Kugelman produz um caso de estouro quandoa == -1
eb ==
qualquer outra coisa (ponto levantado pela primeira vez por Kyle). Esta é uma maneira de consertar:Não é uma solução geral para qualquer
MIN_VALUE
eMAX_VALUE
, mas é geral para Java deLong
eInteger
e qualquer valor dea
eb
.fonte
MIN_VALUE = -MAX_VALUE - 1
, não em qualquer outro caso (incluindo seu caso de teste de exemplo). Eu teria que mudar muito.Talvez:
Não tenho certeza sobre esta "solução".
Editar: Adicionado b! = 0.
Antes de fazer downvote : a * b / b não será otimizado. Isso seria um bug do compilador. Ainda não vejo um caso em que o bug de estouro possa ser mascarado.
fonte
a * b / b
provavelmente será otimizada apenasa
para muitos outros contextos.talvez isso te ajude:
fonte
long
.c / c ++ (longo * longo):
java (int * int, desculpe, não encontrei int64 em java):
1. salve o resultado em tipo grande (int * int coloque o resultado em long, long * long coloque em int64)
2.cmp result >> bits e result >> (bits - 1)
fonte