безопасность кода С++ с неявным преобразованием между знаком и без знака
В соответствии с правилами о неявных преобразованиях между подписанных и неподписанных целочисленных типов, обсуждаемых здесь и здесь, при суммировании 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) я бы сказал, что ваше предположение верно.