Pregunta

Escribí un método para convertir un número determinado de días a milisegundos:

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

Me costó mucho darme cuenta de qué hice mal.Ahora mi pregunta:¿Es tan obvio ese error?

El método corregido:

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

Si no convierto el número entero en mucho antes de calcularlo, obtengo un resultado completamente incorrecto.

¿Fue útil?

Solución

es obvio? Supongo que depende de cuánto tiempo usted ha estado usando Java y cuántas veces ha tenido que hacer frente a milisegundos. Por supuesto, debería estar bien para un máximo de unos 24 días ...

Creo que el mayor indicio de que debe ser System.currentTimeMillis() devuelve un long. Esa es una buena indicación de que un número de milisegundos puede ser grande. El tipo de la variable que se está configurando debe ser un buen toque también.

Por supuesto, usted tiene también tiene que saber que si lo hace operaciones aritméticas con enteros, el resultado será int con envolvente en caso de desbordamiento. Si eso es lo suficientemente obvia o no podían ser objeto de debate, pero sería una discusión bastante inútil. En C # Si apagó la comprobación de desbordamiento, habrías encontrado el error con bastante rapidez -. Pero no muchos desarrolladores hacer eso (de hecho, no lo hago aunque probablemente debería)

Otros consejos

Sí, es bastante obvio si lo has hecho antes. Cada vez que vea una cadena de números multiplica a cabo usted debe comenzar automáticamente a pensar en errores de desbordamiento de enteros. En este caso ya está listo para desbordar si expireTimeInDays es más de 24. Técnicamente usted debe pensar acerca de los errores de desbordamiento cualquier momento se trabaja con números enteros , pero la multiplicación de un grupo de ellos de esta manera debe ser una muy gran bandera roja.

La variable operando y los números literales son de tipo int. El tipo de datos int tiene un valor máximo de 2 ^ 31 -1. Por lo tanto con tan gran número, el tipo de datos int desborda que conduce a una respuesta incorrecta aparente.

En su primer ejemplo, el int sólo está promovido a un largo en la asignación a la variable que se produce después el cálculo. El resultado del cálculo es un int.

El segundo ejemplo, arroja el primer operando a una larga, haciendo que la promoción del cálculo a un tiempo. En este caso, el resultado del cálculo es mucho, debido a la promoción. El tipo de datos a largo es más que suficiente grande para su cálculo.

Quizás le interese saber que esto se trata en "Java Puzzlers" de Joshua Bloch y Neal Gafter.

alt text
(fuente: javapuzzlers.com)

En ese libro encontrará muchos otros errores, trampas y casos extremos de Java.

Estoy de acuerdo con el starblue que dejó un comentario.Agregue una L al número.

No, no es obvio.

Pero confía en mí, después de algunos años más de práctica y reparación de errores como éste se convierte en muy sensible sobre desbordamientos de enteros y acaba de hacer lo correcto sin siquiera pensar en ello.

Es algo que sucedió a todo el mundo. Sin duda, no hay señales de código de prácticas mal, la ignorancia o menos.

Sólo para añadir a las otras respuestas, he encontrado que es útil en el pasado para definir constantes (public static final long) como MILLISECS_DAY o MILLISECS_HOUR. Mucho más legible y útil.

Otra manera de escribir esto es

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

o

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

Si utiliza FindBugs en su código que detectará este problema exacto. "ICAST: El resultado de la multiplicación de enteros fundido a tiempo". ejemplo FindBugs' es exactamente lo que está haciendo; calcular los días en milisegundos.

Este problema no era obvio para mí la primera vez que me encontré con él.

Hay alguna herramienta de análisis estático (findbugs) que encontrarán este tipo de errores.

matemáticas numérica sobre los ordenadores puede ser difícil. Orden de los asuntos de operación puede afectar a la precisión y la exactitud de las maneras que usted no espera. Fecha de matemáticas también puede ser sorprendentemente difícil. A menudo es mejor utilizar las rutinas Fecha / Calendario en lugar de tratar de hacer lo mismo matemáticas pero esas rutinas no son los mejor diseñados que están en la biblioteca de clases de Java.

No estoy tratando de justificar mi error, pero sería genial si el compilador de Java fue lo suficientemente inteligente como para promover el int a un tiempo antes de que el cálculo (una vez que el cálculo se asigna a una variable de tipo long)

Por cierto, yo solía trabajar con C / C ++ y si se trataba de un programa en C, que había tenido el mismo problema, pero hace unos años que había sido más cuidadoso con este tipo de operación.

Voy a pagar más atención la próxima vez (o cambiar a Python) ...: D

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top