Переполнение происходит с умножением
long m = 24 * 60 * 60 * 1000 * 1000;
Приведенный выше код создает переполнение и не печатает правильный результат.
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
Вышеуказанные 2 строки печатают правильный результат.
Мои вопросы -
- Имеет ли значение для компилятора, который я использую,
m2
или m3
?
- Как java начинает умножаться? Слева направо или справа налево? Вычисляет ли 24 * 60 сначала или 1000 * 1000?
Ответы
Ответ 1
Я использовал бы строку m2
вместо строки m3
.
Java оценивает оператор умножения *
от слева направо, поэтому сначала оценивается 24 * 60
.
Так получилось, что 24 * 60 * 60 * 1000
(один 1000
) не переполняется, так что к тому времени, когда вы умножаетесь на 1000L
(второй 1000
), продукт продвигается до long
до умножение, так что переполнение не происходит.
Но, как вы упомянули в своих комментариях, большее количество факторов может вызвать переполнение в типе данных int
до умножения последнего номера long
, что даст неправильный ответ. Лучше использовать литерал long
для первого (самого левого) числа, как в m2
, чтобы избежать переполнения с самого начала. Кроме того, вы можете записать первый литерал как long
, например. (long) 24 * 60 * ...
.
Ответ 2
В этом случае -
long m = 24 * 60 * 60 * 1000 * 1000;
Сначала оценивается право присваивания. Справа нет данных типа long
. Все int
. Таким образом, JVM
попытается совместить результат с int
, тогда произошло переполнение.
А во втором случае -
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
Здесь один операнд умножения long
. Поэтому другие запрашиваются long
автоматически. Результат пытается соответствовать long
. Наконец, назначение выполняется с помощью m2
и m3
.
И да ассоциативность умножения слева направо - означает, что первый операнд берется первым. И исходя из этого факта, я думаю, что в этом сценарии мы должны использовать -
long m2 = 24L * 60 * 60 * 1000 * 1000;
это утверждение, так как в этом заявлении ранее продвигается продвижение до long
, что снижает риск переполнения.
Ответ 3
Поскольку выражения оцениваются слева направо, я предпочел бы ваше первое решение (m2 = ...
).
Рассуждение: давайте рассмотрим несколько другой пример.
long l = Integer.MAX_VALUE * 2 * 2L;
Это выражение будет оцениваться как -4
, так как только последнее умножение передает первое выражение в long
(которое является -2
в этот момент времени, потому что оба операнда int
). Если вы пишете
long l = Integer.MAX_VALUE * 2L * 2;
вместо этого l
будет удерживать ожидаемое значение 8589934588
, поскольку первое умножение дает результат типа long
.
Ответ 4
Умножение выполняется слева направо, а int * int
- int
. Так
24 * 60 * 60 * 1000 * 1000
совпадает с
(((24 * 60)* 60) * 1000) * 1000
что дает нам
(((1440)* 60) * 1000) * 1000
(( 86400 ) * 1000) * 1000
( 86400000 ) * 1000
и, наконец, из-за целочисленного переполнения (поскольку 86400000000
слишком велико для целого числа, максимальное значение которого 2147483647
), результат будет
500654080
Вы можете исключить целочисленное переполнение, используя long
как один из аргументов (int * long
и long * int
производит long
).
В этом случае вы можете сделать это при запуске, как в случае m2
: 24L * 60
, который будет производить long
1440L
, который снова будет умножен на int 60
, производя новый long
, и т.д., производя только значения long
.
m3
здесь работает, потому что вы умножаете 86400000
на 1000L
, что означает, что вы избегаете целочисленного переполнения, так как результат будет long
.
Ответ 5
Позвольте умножить больше чисел, эта строка будет переполняться даже там 1000L
:
long m3 = 24 * 60 * 60 * 1000 * 1000 * 1000 * 1000L;
Пока это даст правильный результат:
long m3 = 24L * 60 * 60 * 1000 * 1000 * 1000 * 1000;
Итак, мы уверены, что java начинает умножаться слева направо, и мы должны начинать с Long
слева, чтобы предотвратить переполнение.
Ответ 6
Это потому, что, когда мы используем long как один операнд, все остальные операнды типа int
получают запрос на long
.
Выражение в java оценивается слева направо.