безопасность кода С++ с неявным преобразованием между знаком и без знака

В соответствии с правилами о неявных преобразованиях между подписанных и неподписанных целочисленных типов, обсуждаемых здесь и здесь, при суммировании unsigned int с int, подписанная int сначала преобразуется в unsigned int.

Рассмотрим, например, следующую минимальную программу

#include <iostream>

int main()
{
   unsigned int n = 2;
   int x = -1;

   std::cout << n + x << std::endl;

   return 0;
}

Вывод программы, тем не менее, равен 1, как и ожидалось: x сначала преобразуется в целое число unsigned int, а сумма с n приводит к переполнению целого числа, что дает "правильный" ответ.

В коде, подобном предыдущему, если я точно знаю, что n + x положительно, могу ли я предположить, что сумма unsigned int n и int x дает ожидаемое значение?

Ответ 1

В коде, подобном предыдущему, если я точно знаю, что n + x положительно, могу ли я предположить, что сумма беззнаковых int n и int x дает ожидаемое значение?

Да.

Во-первых, значение со знаком преобразуется в без знака, используя арифметику по модулю:

Если тип назначения является беззнаковым, полученное значение является наименьшим целым числом без знака, соответствующим исходному целому числу (по модулю 2 n, где n - число битов, используемых для представления типа без знака).

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

Целые числа без знака должны подчиняться законам арифметики по модулю 2 n, где n - количество битов в представлении значения этого конкретного размера целого числа.

Это означает, что вы получите ожидаемый ответ.

Даже если результат будет отрицательным в математическом смысле, результатом в C++ будет число, которое по модулю равно отрицательному числу.

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

Ответ 2

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

Во-первых, позвольте изложить ясность: интегралы без знака не переполняются, а принимают значение по модулю 2 ^ nrOfBits (см. Этот стандартный черновик C++ онлайн):

6.7.1 Основные типы

(7) Целые числа без знака должны подчиняться законам арифметики по модулю 2n, где n - количество битов в представлении значения этого конкретного размера целого числа.

Поэтому вопрос только в том, правильно ли преобразовано отрицательное значение nv в целочисленную битовую комбинацию без знака nv(conv), так что x + nv(conv) всегда будет таким же, как x - nv. В случае системы, использующей два дополнения, все ясно, поскольку два дополнения фактически спроектированы так, что эта арифметика работает немедленно.

Для систем, использующих другие представления отрицательных значений, нам придется внимательно прочитать стандарт:

7.8 Интегральные преобразования

(2) Если тип назначения является беззнаковым, результирующее значение является наименьшим целым числом без знака, соответствующим исходному целому числу (по модулю 2n, где n - число битов, используемых для представления типа без знака). [Примечание: в представлении с дополнением до двух это преобразование является концептуальным, и в битовой комбинации нет изменений (если не происходит усечение). -endnote]

Поскольку в сноске явно сказано, что в представлении с двумя дополнениями нет изменений в битовой комбинации, мы можем предположить, что в системах, отличных от 2s, будет выполнено реальное преобразование, такое что x + nv(conv) == x - nv.

Поэтому из-за 7.8 (2) я бы сказал, что ваше предположение верно.