Подтвердить что ты не робот

Переполнение происходит с умножением

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?
4b9b3361

Ответ 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 оценивается слева направо.