Умножение двух целых чисел в С++
У меня довольно простой вопрос, но я не уверен, понимаю ли я эту концепцию или нет. Предположим, у нас есть:
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 бита без причины
(я проверил: это не тот случай, когда код оптимизирован)