Умножение двух целых чисел в С++

У меня довольно простой вопрос, но я не уверен, понимаю ли я эту концепцию или нет. Предположим, у нас есть:

int a = 1000000;
int b = 1000000;
long long c = a * b;

Когда я запускаю это, c показывает отрицательное значение, поэтому я также изменил a и b на long long и тогда все было хорошо. Итак, почему я должен изменить a и b, когда их значения находятся в диапазоне от int и их продукт назначен c (который long long)?

Я использую C/C++

Ответ 1

int до умножения не продвигаются до long long, они остаются int и продукт. Затем продукт отбрасывается до long long, но слишком поздно, переполнение произошло.

Если один из a или b long long должен работать, как и другой, будет продвигаться.

Ответ 2

Для арифметических операторов тип результата не зависит от того, для чего вы назначаете результат, но для типов операндов. Для арифметических операторов в операндах выполняются обычные арифметические преобразования. Это используется для приведения операндов к общему типу, это означает, что для типов, меньших, чем unsigned/signed int, если значения могут соответствовать, они повышаются до unsigned/signed int, в этом случае они уже оба int, поэтому преобразование не требуется. См. Зачем нужно коротко преобразовать в int перед арифметическими операциями в C и С++? для получения подробной информации о том, почему.

Теперь у нас есть поведение undefined, так как знаковое переполнение целых чисел является undefined, это описано в черновом стандартном разделе С++ 5 [Expr], в котором говорится:

Если во время оценки выражения результат не определяется математически или нет в диапазоне представляемые значения для его типа, поведение undefined. [Примечание: большинство существующих реализаций С++ игнорировать целые переполнения. Обработка деления на ноль, формирование остатка с использованием делителя нуля и все исключения с плавающей запятой различаются между машинами и обычно регулируются библиотечной функцией. -end note]

В настоящее время у нас есть дезинфицирующие средства, чтобы поймать эти типы поведения undefined и использовать -fsanitize=undefined, и clang и gcc поймают это во время выполнения со следующей ошибкой (посмотреть его в прямом эфире):

ошибка времени выполнения: целочисленное переполнение цепочки: 1000000 * 1000000 не может быть представленный в типе 'int'

В справочном разделе 5.6 [expr.mul] говорится:

[...] Обычные арифметические преобразования выполняются в операндах и определите тип результата.

и раздел 5 говорится:

В противном случае интегральные акции (4.5) должны выполняться на обоих операндах. правила применяются к продвинутым операндам

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

Ответ 3

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

int * int → 64 бит длиной

поэтому, если вы посмотрите на машинный код, вы увидите: IMUL которые хранят 64 бит в eax edx тогда CDQ что положил бит eax в edx (таким образом, потеряв полный результат в 64 бита) а затем eax edx сохраняются в переменной 64 бит.

и если вы преобразуете значения 32 бит в 64 бит перед умножением, вы получите вызов функции умножения на 64 бита без причины

(я проверил: это не тот случай, когда код оптимизирован)