Вопрос

Я написал метод для преобразования заданного числа из дней в миллисекунды:

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

Мне было трудно понять, что я сделал не так.Теперь мой вопрос:Неужели эта ошибка настолько очевидна?

Исправленный метод:

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

Если я не преобразую целое число в long перед вычислением, я получу совершенно неверный результат.

Это было полезно?

Решение

Это очевидно?Я думаю, это зависит от того, как долго вы используете Java и сколько раз вам приходилось иметь дело с миллисекундами.Конечно, все должно быть в порядке примерно до 24 дней...

Я думаю, что самый большой намек должен заключаться в том, что System.currentTimeMillis() возвращает long.Это хороший признак того, что количество миллисекунд может стать большим.Тип устанавливаемой вами переменной также должен служить подсказкой.

Конечно, у вас есть также узнал, что если производить арифметические операции с целыми числами, результат будет int с обтеканием при переполнении.Можно спорить о том, достаточно ли это очевидно или нет, но это была бы довольно бессмысленная дискуссия.В C#, если бы вы включили проверку переполнения, вы бы нашли ошибку довольно быстро, но тогда не многие разработчики так делают (да, я и не делаю этого, хотя, вероятно, мне следовало бы).

Другие советы

Да, это довольно очевидно, если вы делали это раньше.Каждый раз, когда вы видите умноженную строку чисел, вы должны автоматически начать думать об ошибках целочисленного переполнения.В этом случае вы настроены на переполнение, если expireTimeInDays больше 24.Технически вам следует подумать об ошибках переполнения. каждый раз, когда вы работаете с целыми числами, но такое увеличение группы из них должно быть очень большим тревожным сигналом.

Ваша переменная операнда и литеральные числа имеют тип int.Тип данных int имеет максимальное значение 2^31 -1.Поэтому при таких больших числах тип данных int переполняется, что приводит к кажущемуся неправильному ответу.

В вашем первом примере int повышается до long только при присвоении переменной, которая возникает после расчет.Результатом вычисления является int.

Во втором примере первый операнд преобразуется в длинное значение, что приводит к преобразованию вычисления в длинное значение.В этом случае результат расчета длинный, из-за промоакции.Тип данных long более чем достаточен для ваших вычислений.

Возможно, вам будет интересно узнать, что об этом говорится в книге Джошуа Блоха и Нила Гафтера «Java Puzzles».

alt text
(источник: javapuzzlers.com)

В этой книге вы найдете множество других ловушек, ловушек и нестандартных ситуаций Java.

Я согласен со звездочкой, оставившей комментарий.Добавьте букву L к числу.

Нет, это не очевидно.

Но поверьте мне, после нескольких лет практики и исправления подобных ошибок вы станете очень разумно относиться к целочисленным переполнениям и просто будете поступать правильно, даже не задумываясь об этом.

Это то, что случилось с каждым.Определенно нет никаких признаков плохой практики написания кода, невежества или чего-то подобного.

Чтобы добавить к другим ответам, в прошлом мне было полезно определять константы (public static final long) такой как MILLISECS_DAY или MILLISECS_HOUR.Гораздо читабельнее и полезнее.

Другой способ написать это

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

или

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

Если вы используете FindBugs в своем коде, он обнаружит именно эту проблему.«ИКАСТ:Результат целочисленного умножения, приведенный к длинному». Пример FindBugs — это именно то, что вы делаете;расчет дней в миллисекундах.

Эта проблема не была для меня очевидной, когда я впервые столкнулся с ней.

Существуют инструменты статического анализа (findbugs), которые находят ошибки такого типа.

Численная математика на компьютере может быть сложной.Вопросы порядка операций могут влиять на точность и достоверность так, как вы этого не ожидаете.Математика дат также может быть на удивление сложной.Часто лучше использовать подпрограммы Date/Calendar, а не пытаться выполнять математические вычисления самостоятельно, но эти подпрограммы не являются лучшими в библиотеке классов Java.

Я не пытаюсь оправдать свою ошибку, но было бы здорово, если бы компилятор Java был достаточно умен, чтобы перевести int в значение long перед вычислением (как только вычисление присваивается переменной типа long)

Кстати, раньше я работал с C/C++, и если бы это была программа на C, у меня была бы та же проблема, но несколько лет назад я был более осторожен с такого рода операциями.

В следующий раз я уделю больше внимания (или перейду на Python)...:D

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top