Арифметика без знака и целочисленное переполнение

Я пытаюсь понять арифметическое переполнение. Предположим, у меня есть следующее,

unsigned long long x;
unsigned int y, z;

x = y*z;

y * z может привести к переполнению целого числа. Делает ли приведение одного из операндов к беззнаковому длинному долго, облегчает эту проблему. Каков ожидаемый результат умножения 64-разрядного операнда на 32-разрядный операнд?

1

Решение

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,

4

Другие решения

Вы явно предполагаете, что unsigned int 32-битный и unsigned long long 64-битный. Они не должны быть, позвольте нам принять это.

64-битный операнд, полученный путем преобразования 32-битного операнда, по-прежнему умещается в 32 бита. Таким образом, в y*(unsigned long long)zгде каждый из операндов сначала повышен до unsigned long longрезультат вычисляется как unsigned long long и не может «переполниться», потому что это умножение двух количественных величин, которые умещаются в 32 бита каждый.

(Кроме того, в словаре стандарта C операции без знака не «переполняются». Переполнение — это неопределенное поведение при создании результата за пределами целевого типа. То, что делают операции без знака, это «обтекание»).

5

Если один операнд шире другого, компилятор должен (или вести себя так, как если бы он) преобразовывать оба операнда в один и тот же размер, поэтому приведение одного к большему размеру приведет к правильному поведению.

Это указано в стандартах C и C ++. В стандарте C ++ 11 (черновик n3337) сказано в главе 5 утверждение 9:

… если оба операнда имеют целочисленные типы со знаком или оба без знака
целочисленные типы, операнд с типом преобразования меньшего целого
ранг должен быть преобразован в тип операнда с большим рангом.

Есть пара страниц, описывающих все преобразования и прочее, что происходит, но это то, что определяет поведение этого конкретного выражения.

3
По вопросам рекламы [email protected]