Pergunta

Eu escrevi um método para converter um determinado número de dias para milissegundos:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000;
}

Eu tive um tempo difícil descobrir o que eu fiz de errado. Agora a minha pergunta: é que o erro tão óbvio?

O método corrigido:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000;
}

Se eu não converter o inteiro para muito tempo antes de calcular, eu recebo um resultado errado completa.

Foi útil?

Solução

é óbvio? Eu acho que depende de quanto tempo você estiver usando Java e quantas vezes você já teve que lidar com milissegundos. Claro, ele deve estar bem para até cerca de 24 dias ...

Eu acho que a maior dica deve ser que System.currentTimeMillis() retornos uma long. Essa é uma boa indicação de que um número de milissegundos pode ficar grande. O tipo da variável que está definindo deve ser uma boa dica também.

É claro, você tem também tem que saber que se você fizer operações aritméticas com ints, o resultado será int com wrap-around no estouro. Se isso é suficientemente óbvio ou não pôde ser debatido, mas seria uma discussão bastante inútil. Em C # se você virou verificação de estouro, você teria encontrado o bug muito rapidamente -. Mas então não muitos desenvolvedores de fazer isso (na verdade, eu não embora eu provavelmente deveria)

Outras dicas

Sim, é bastante óbvio se você já fez isso antes. Toda vez que você ver uma seqüência de números multiplicados para fora você deve iniciar automaticamente pensar em erros integer overflow. Neste caso, você está pronto para estouro se expireTimeInDays é mais do que 24. Tecnicamente, você deve estar pensando em erros de estouro qualquer momento você está trabalhando com números inteiros , mas multiplicando um grupo de-los como este deve ser uma grande bandeira vermelha.

Seu variável operando e os números literais são do tipo int. O tipo de dados int tem um valor máximo de 2 ^ 31 -1. Portanto, com tão grande número, o tipo de dados int transborda levando a uma resposta incorreta aparente.

Em seu primeiro exemplo, o int só é promovido a um longo na atribuição para a variável que ocorre depois o cálculo. O resultado do cálculo é um int.

O segundo exemplo, lança o primeiro operando a uma longa, fazendo com que a promoção do cálculo para um longo. Neste caso, o resultado do cálculo é um longo, devido à promoção. O tipo de dados longo é mais do que suficiente para o seu cálculo.

Você pode estar interessado em saber que este é coberto em "Java Puzzlers" por Joshua Bloch e Neal Gafter.

text alt
(fonte: javapuzzlers.com )

Você vai encontrar muitas outras armadilhas Java, armadilhas e casos de canto em que o livro.

Eu concordo com o starblue que deixou um comentário. Anexar um L com o número.

Não, não é óbvio.

Mas confie em mim, depois de mais alguns anos de prática e correção de bugs como este que você se tornar muito sensível sobre estouros de inteiros e apenas fazer a coisa certa sem pensar mesmo sobre ele.

É algo que aconteceu a todos. Definitivamente nenhum sinal de prática de código ruim, ignorância ou assim.

Só para acrescentar às outras respostas, eu tê-lo encontrado útil no passado para definir constantes (public static final long) como MILLISECS_DAY ou MILLISECS_HOUR. Muito mais legível e útil.

Outra maneira de escrever este é

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000;
}

ou

public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000;
}

Se você usar FindBugs no seu código ele irá detectar este problema exato. "ICAST: Resultado da multiplicação inteiro elenco muito tempo." exemplo FindBugs' é exatamente o que você está fazendo; cálculo dias em milissegundos.

Este problema não era óbvio para mim a primeira vez que eu corri para ele.

Há alguma ferramenta de análise estática (findbugs) que vai encontrar este tipo de erros.

matemática numérica em computadores pode ser difícil. Ordem de assuntos operação pode afetar a precisão e exatidão nas maneiras que você não espera. Data de matemática também pode ser surpreendentemente complicado. Muitas vezes é melhor usar as rotinas de Data / Calendário em vez de tentar fazer as contas você mesmo, mas essas rotinas não são os melhores projetados na biblioteca de classes Java.

Eu não estou tentando justificar meu erro, mas seria ótimo se o compilador java foi inteligente o suficiente para promover o int para uma longa antes do cálculo (uma vez que o cálculo está sendo atribuído a uma variável do tipo long)

A propósito, eu costumava trabalhar com C / C ++ e se fosse um programa C, eu tive o mesmo problema, mas alguns anos atrás eu tinha tido mais cuidado com este tipo de operação.

Eu vou prestar mais atenção na próxima vez (ou interruptor para python) ...: D

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top