2009-02-04 14 views
8

me escribió un método para convertir un número determinado de días a milisegundos:Java comportamiento operación de multiplicación

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

Tuve un tiempo difícil averiguar lo que hice mal. Ahora mi pregunta: ¿Es ese error tan obvio?

El método corregido:

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

Si no me convierto al entero a tiempo antes de calcular, me sale un resultado erróneo completa.

+2

También podría agregar una L a las constantes. – starblue

+0

¿Por qué la gente quiere cerrar esto? Parece una pregunta legítima que podría ayudar a otras personas. Si es un duplicado exacto, dígalo. –

+1

También podría declarar constantes durante tanto tiempo: 24L * 60L ... –

Respuesta

8

¿Es obvio? Supongo que depende de cuánto tiempo llevas usando Java y cuántas veces has tenido que lidiar con milisegundos. Por supuesto, debería estar bien por hasta aproximadamente 24 días ...

Creo que la mayor pista debería ser que System.currentTimeMillis() devuelve long. Esa es una buena indicación de que una cantidad de milisegundos puede ser grande. El tipo de variable que está configurando también debería ser una buena sugerencia.

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 suficientemente obvio o no, podría debatirse, pero sería una discusión bastante inútil. En C# si activaba la verificación de desbordamiento, habría encontrado el error bastante rápido, pero no muchos desarrolladores lo hacen (de hecho, no lo hago, aunque probablemente debería hacerlo).

+0

Esperaba mucho, estoy acostumbrado a trabajar con System.currentTimeMillis(). Debido a que no es un código nuevo, no recuerdo lo que estaba pensando cuando lo escribí (probablemente esperaba algo de magia del compilador) ... –

+0

... Creo que mi gran error, como dijiste, fue haciendo operaciones aritméticas con ints, sin prestar atención al desbordamiento int.Olvidé los conceptos básicos (de hecho, no lo aprendí en Java, pero lo aprendí en C/C++) –

2

No, no es obvio.

Pero confía en mí, después de algunos años más de práctica y corregir errores de este tipo que se vuelven muy sensibles sobre desbordamientos de enteros y sólo hacer lo correcto sin siquiera pensar en ello.

Es algo que les sucedió a todos. Definitivamente no hay signos de mala práctica de código, ignorancia o algo así.

+0

Lo interesante es que he estado codificando durante más de 20 años, y esa es la primera vez que cometí ese error específico. Cuando era desarrollador de C/C++ solía prestar atención a cada pequeño detalle de mi código. Creo que ahora estoy esperando que el compilador haga algo de "magia" para mí ... –

7

Sí, es bastante obvio si lo ha hecho antes. Cada vez que ve una serie de números multiplicados, debería comenzar a pensar automáticamente en errores de desbordamiento de enteros. En este caso, está configurado para desbordarse si expireTimeInDays es más de 24. Técnicamente, debería pensar en errores de desbordamiento cada vez que trabaje con los enteros, pero multiplicar un grupo como este debería ser una gran señal de advertencia .

3

Su variable de 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 números tan grandes, el tipo de datos de desbordamientos int lleva a una respuesta aparentemente incorrecta.

En su primer ejemplo, el int solo se promociona a una asignación larga a la variable que ocurre después de el cálculo. El resultado del cálculo es un int.

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

+0

Parece que Java tiene el mismo comportamiento para el desbordamiento int que C. Creo que esperaba que el compilador convirtiera es (int a largo) para mí. Es el tipo de código que se ve bastante simple (y lo es) ... Solo tengo que prestar más atención. –

3

Puede que le interese saber que esto está cubierto en "Java Puzzlers" por Joshua Bloch y Neal Gafter.

alt text http://www.javapuzzlers.com/lg-puzzlers-cropped.jpg

Se pueden encontrar muchos otros escollos Java, trampas y casos de esquina en ese libro.

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

+0

Gracias, es un buen libro. –

+0

podría señalar el capítulo relacionado? – Pacerier

1

Solo para agregar a las otras respuestas, he encontrado que fue útil en el pasado definir constantes (public static final long) como MILLISECS_DAY o . Mucho más legible y útil.

0

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

Por cierto, solía trabajar con C/C++ y si se trataba de un programa en C, tuve el mismo problema, pero hace algunos años 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

1

Otra forma 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; 
} 
+0

De esta manera es mejor, prefiero evitar el casting innecesario. gracias –

1

Si usa FindBugs en tu código, detectará este problema exacto. "ICAST: resultado de la conversión de multiplicación entera a larga". El ejemplo de FindBugs es exactamente lo que estás haciendo; calculando días en milisegundos.

Este problema no era obvio para mí la primera vez que lo encontré.

+0

Me olvidé por completo de FindBugs ... gracias –

1

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

La matemática numérica en las computadoras puede ser difícil. Los asuntos relacionados con el orden de la operación pueden afectar la precisión y precisión de formas que no espera. La fecha matemática también puede ser sorprendentemente difícil. A menudo es mejor utilizar las rutinas de fecha/calendario en lugar de tratar de hacer las matemáticas usted mismo, pero esas rutinas no son las mejor diseñadas en la biblioteca de clases de Java.