Я понимаю, что в отношении неявных преобразований, если у нас есть операнд типа без знака и операнд типа со знаком, а тип операнда без знака совпадает (или больше) с типом операнда со знаком, знаковый операнд будет преобразован без подписи.
Так:
unsigned int u = 10;
signed int s = -8;
std::cout << s + u << std::endl;
//prints 2 because it will convert `s` to `unsigned int`, now `s` has the value
//4294967288, then it will add `u` to it, which is an out-of-range value, so,
//in my machine, `4294967298 % 4294967296 = 2`
Что я не понимаю — я прочитал это, если подписанный операнд имеет больший тип, чем беззнаковый операнд:
если все значения в типе без знака соответствуют большему типу, то беззнаковый операнд преобразуется в тип со знаком
если значения в типе без знака не вписываются в больший тип, тогда операнд со знаком будет преобразован в тип без знака
так в следующем коде:
signed long long s = -8;
unsigned int u = 10;
std::cout << s + u << std::endl;
u
будет преобразован в long long со знаком, потому что значения int могут уместиться в long long со знаком ??
Если это так, то в каком сценарии меньшие значения типов не будут соответствовать большему?
Соответствующая цитата из Стандарта:
5 выражений [expr]
[2 пункта о равных типах или типах знака равенства опущены]10 Многие бинарные операторы, которые ожидают операнды арифметики или
Тип перечисления вызывает преобразования и приводит к типам результата в аналогичном
путь. Цель состоит в том, чтобы получить общий тип, который также является типом
результат. Эта модель называется обычными арифметическими преобразованиями,
которые определены следующим образом:
— В противном случае, если операнд с целым типом без знака имеет ранг
больше или равно рангу типа другого операнда,
операнд со знаком целочисленного типа должен быть преобразован в тип
операнд с целым типом без знака.— В противном случае, если тип
операнд со знаком целочисленного типа может представлять все значения
типа операнда с целым типом без знака, операнд
с целым беззнаковым типом преобразуется в тип
операнд со знаком целочисленного типа.— В противном случае оба операнда должны быть
преобразуется в целое число без знака, соответствующее типу
операнд со знаком целочисленного типа.
Давайте рассмотрим следующие 3 примера для каждого из 3 пунктов выше в системе, где sizeof(int) < sizeof(long) == sizeof(long long)
(легко адаптируется к другим случаям)
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
signed long int s2 = -4;
unsigned int u2 = 2;
signed long long int s3 = -4;
unsigned long int u3 = 2;
int main()
{
std::cout << (s1 + u1) << "\n"; // 4294967294
std::cout << (s2 + u2) << "\n"; // -2
std::cout << (s3 + u3) << "\n"; // 18446744073709551614
}
Живой пример с выходом.
Первый пункт: типы одинакового ранга, поэтому signed int
операнд преобразуется в unsigned int
, Это влечет за собой преобразование значения, которое (с использованием дополнения до двух) дает напечатанное значение.
Второе предложение: тип со знаком имеет более высокий ранг и (на этой платформе!) Может представлять все значения типа без знака, поэтому беззнаковый операнд преобразуется в тип со знаком, и вы получаете -2
Третье предложение: подписанный тип снова имеет более высокий ранг, но (на этой платформе!) Не может представлять все значения беззнакового типа, поэтому оба операнда преобразуются в unsigned long long
и после преобразования значения в подписанном операнде вы получите напечатанное значение.
Обратите внимание, что когда беззнаковый операнд будет достаточно большим (например, 6 в этих примерах), тогда конечный результат даст 2 для всех 3 примеров из-за переполнения целого числа без знака.
(Добавлено) Обратите внимание, что вы получаете еще более неожиданные результаты при сравнении этих типов. Рассмотрим приведенный выше пример 1 с <
:
#include <iostream>
signed int s1 = -4;
unsigned int u1 = 2;
int main()
{
std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "\n"; // "s1 !< u1"std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "\n"; // "-4 !< 2u"}
поскольку 2u
сделан unsigned
явно u
Суффикс применяются те же правила. И результат, вероятно, не то, что вы ожидаете при сравнении -4 < 2 при написании в C ++ -4 < 2u
…
signed int
не вписывается в unsigned long long
, Так что у вас будет это преобразование:
signed int
-> unsigned long long
,
Обратите внимание, что стандарт C ++ 11 здесь не говорит о больших или меньших типах, он говорит о типах с более низким или более высоким рангом.
Рассмотрим случай long int
а также unsigned int
где оба 32-битные. long int
имеет больший ранг, чем unsigned int
, но с тех пор long int
а также unsigned int
оба 32-битные, long int
не может представлять все значения unsigned int
,
Поэтому мы переходим к последнему случаю (C ++ 11: 5.6p9):
- В противном случае оба операнда должны быть преобразованы в целочисленный тип без знака, соответствующий
тип операнда со знаком целочисленного типа.
Это означает, что оба long int
и unsigned int
будет преобразован в unsigned long int
,