Я пытаюсь понять арифметическое переполнение. Предположим, у меня есть следующее,
unsigned long long x;
unsigned int y, z;
x = y*z;
y * z может привести к переполнению целого числа. Делает ли приведение одного из операндов к беззнаковому длинному долго, облегчает эту проблему. Каков ожидаемый результат умножения 64-разрядного операнда на 32-разрядный операнд?
unsigned long long x;
unsigned int y, z;
x = y*z;
Оценка выражения y*z
не зависит от контекста, в котором он появляется. Это умножает два unsigned int
значения, дающие unsigned int
результат. Если математический результат не может быть представлен как unsigned int
значение, результат будет вокруг. Затем присваивание неявно преобразует (возможно, усеченный) результат из unsigned int
в unsigned long long
,
Если вы хотите умножение, которое дает unsigned long long
В результате вам нужно явно преобразовать один или оба операнда:
x = (unsigned long long)y * z;
или, чтобы быть более явным:
x = (unsigned long long)y * (unsigned long long)z;
C о *
применяется оператор умножения только до двух операндов одного типа. Из-за этого, когда вы передаете ему операнды разных типов, они преобразуются в какой-то общий тип перед выполнением умножения. Правила могут быть немного сложными, когда вы смешиваете типы со знаком и без знака, но в этом случае, если вы умножаете unsigned long long
по unsigned int
, unsigned int
операнд повышен до unsigned long long
,
Если unsigned long long
как минимум в два раза шире unsigned int
, как и в большинстве систем, результат не будет ни переполнен, ни обернут, поскольку, например, 64-разрядный unsigned long long
может содержать результат умножения любых двух 32-битных unsigned int
ценности. Но если вы находитесь в системе, где, например, int
а также long long
оба имеют ширину 64 бита, вы все равно можете иметь переполнение обернуть, давая вам результат в x
это не равно математическому произведению y
а также z
,
Вы явно предполагаете, что unsigned int
32-битный и unsigned long long
64-битный. Они не должны быть, позвольте нам принять это.
64-битный операнд, полученный путем преобразования 32-битного операнда, по-прежнему умещается в 32 бита. Таким образом, в y*(unsigned long long)z
где каждый из операндов сначала повышен до unsigned long long
результат вычисляется как unsigned long long
и не может «переполниться», потому что это умножение двух количественных величин, которые умещаются в 32 бита каждый.
(Кроме того, в словаре стандарта C операции без знака не «переполняются». Переполнение — это неопределенное поведение при создании результата за пределами целевого типа. То, что делают операции без знака, это «обтекание»).
Если один операнд шире другого, компилятор должен (или вести себя так, как если бы он) преобразовывать оба операнда в один и тот же размер, поэтому приведение одного к большему размеру приведет к правильному поведению.
Это указано в стандартах C и C ++. В стандарте C ++ 11 (черновик n3337) сказано в главе 5 утверждение 9:
… если оба операнда имеют целочисленные типы со знаком или оба без знака
целочисленные типы, операнд с типом преобразования меньшего целого
ранг должен быть преобразован в тип операнда с большим рангом.
Есть пара страниц, описывающих все преобразования и прочее, что происходит, но это то, что определяет поведение этого конкретного выражения.