Почему unsigned short (multiply) unsigned short преобразуется в подписанный int?
Почему unsigned short * unsigned short
преобразован в int
в С++ 11?
int
слишком мал, чтобы обрабатывать максимальные значения, как показано в этой строке кода.
cout << USHRT_MAX * USHRT_MAX << endl;
переполняется на MinGW 4.9.2
-131071
потому что (источник)
USHRT_MAX = 65535 (2 ^ 16-1) или больше *
INT_MAX = 32767 (2 ^ 15-1) или более *
и (2^16-1)*(2^16-1) = ~2^32
.
Должен ли я ожидать каких-либо проблем с этим решением?
unsigned u = static_cast<unsigned>(t*t);
Эта программа
unsigned short t;
cout<<typeid(t).name()<<endl;
cout<<typeid(t*t).name()<<endl;
выводит вывод
t
i
о
gcc version 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
gcc version 4.8.2 (GCC)
MinGW 4.9.2
с обоими
g++ p.cpp
g++ -std=c++11 p.cpp
что доказывает, что t*t
преобразуется в int
в этих компиляторах.
Полезные ресурсы:
Подписано на неподписанное преобразование в C - всегда ли оно безопасно?
Целочисленное умножение с подписью и без знака
https://bytes.com/topic/c-sharp/answers/223883-multiplication-types-smaller-than-int-yields-int
http://www.cplusplus.com/reference/climits
http://en.cppreference.com/w/cpp/language/types
Изменить: Я продемонстрировал проблему на следующем изображении.
![введите описание изображения здесь]()
Ответы
Ответ 1
Вы можете прочитать о неявных преобразованиях, особенно раздел числовых рекламных акций, где говорится
Пределы малых интегральных типов (например, char)
могут быть преобразованы в prvalues более крупных интегральных типов (например, int
). В частности, арифметические операторы не допускайте типы меньше int
в качестве аргументов
В приведенном выше сказании говорится, что если вы используете нечто меньшее, чем int
(например, unsigned short
) в выражении, которое включает в себя арифметические операторы (что, конечно, включает умножение), то значения будут повышены до int
.
Ответ 2
Это обычные арифметические преобразования в действии.
Обычно называется продвижением аргументов, хотя стандарт использует этот термин более ограниченным образом (вечный конфликт между разумными описательными терминами и стандартными).
С++ 11 §5/9:
" Многие двоичные операторы, ожидающие операндов арифметики или типа перечисления, вызывают конверсии и доходность аналогичным образом. Цель состоит в том, чтобы дать общий тип, который также является типом результата. Этот шаблон называется обычными арифметическими преобразованиями [& hellip;]
Далее в параграфе описываются детали, которые преобразуются в лестницу более общих типов, пока не будут представлены все аргументы. Самая низкая ступень на этой лестнице - интегральное продвижение обоих операндов двоичной операции, поэтому, по крайней мере, это выполняется (но преобразование может начинаться с более высокой ступени). И интегральное продвижение начинается с этого:
С++ 11 §4.5/1:
" Значение целочисленного типа, отличного от bool
, char16_t
, char32_t
или wchar_t
, целочисленное преобразование ранг (4.13) меньше ранга int
может быть преобразован в prvalue типа int
, если int
может представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в prvalue типа unsigned int
Реально, речь идет о типах, а не арифметических выражениях. В вашем случае аргументы оператора умножения *
преобразуются в int
. Затем умножение выполняется как умножение int
, что дает результат int
.
Ответ 3
Как отметил Паоло М в комментариях, USHRT_MAX
имеет тип int
(это указано в 5.2.4.2.1/1: все такие макросы имеют тип, по крайней мере такой же большой, как int
).
Итак, USHRT_MAX * USHRT_MAX
уже есть int
x int
, никаких рекламных акций не происходит.
Это вызывает вычитаемое целочисленное переполнение в вашей системе, вызывая поведение undefined.
Относительно предлагаемого решения:
unsigned u = static_cast<unsigned>(t*t);
Это не помогает, потому что t*t
сам вызывает поведение undefined из-за целочисленного переполнения со знаком. Как объясняется другими ответами, t
продвигается до int
до того, как происходит умножение по историческим причинам.
Вместо этого вы можете использовать:
auto u = static_cast<unsigned int>(t) * t;
который после цельного продвижения является unsigned int
, умноженным на int
; а затем, согласно остальным обычным арифметическим преобразованиям, int
продвигается до unsigned int
, и происходит четкое модульное умножение.
Ответ 4
С целыми правилами продвижения
Значение USHRT_MAX
повышается до int
.
то мы делаем умножение на 2 int (с возможным переполнением).
Ответ 5
Кажется, что никто еще не ответил на эту часть вопроса:
Должен ли я ожидать каких-либо проблем с этим решением?
u = static_cast<unsigned>(t*t);
Да, здесь есть проблема: он сначала вычисляет t*t
и позволяет ему переполняться, а затем преобразует результат в unsigned
. Целочисленное переполнение вызывает поведение undefined в соответствии со стандартом С++ (хотя на практике это может работать нормально). Правильное решение:
u = static_cast<unsigned>(t)*t;
Обратите внимание, что второй t
продвигается до unsigned
перед умножением, потому что первый операнд unsigned
.
Ответ 6
Как было указано другими ответами, это происходит из-за целых правил продвижения.
Самый простой способ избежать преобразования беззнакового типа с меньшим рангом, чем подписанный тип с большим рангом, состоит в том, чтобы убедиться, что преобразование выполнено в unsigned int
, а не int
.
Это делается путем умножения на значение 1, которое имеет тип unsigned int. Из-за того, что 1 является мультипликативной идентичностью, результат останется неизменным:
unsigned short c = t * 1U * t;
Сначала обрабатываются операнды t и 1U. Левый операнд подписан и имеет меньший ранг, чем беззнаковый правый операнд, поэтому он преобразуется в тип правильного операнда. Затем операнды умножаются, и то же самое происходит с результатом и оставшимся правым операндом. Последний абзац в стандарте, приведенный ниже, используется для этой акции.
В противном случае целые рекламные акции выполняются в обоих операндах. Затем для продвинутых операндов применяются следующие правила:
-Если оба операнда имеют один и тот же тип, то дальнейшее преобразование не требуется.
-В противном случае, если оба операнда имеют целочисленные типы, или оба имеют unsigned целочисленных типов, операнд с типом меньшего целочисленного ранга преобразования преобразуется в тип операнда с большим рангом.
- В противном случае, если операнд, который имеет целочисленный тип без знака, имеет ранг больше или равный рангам типа другого операнда, то операнд с целочисленный тип со знаком преобразуется в тип операнда без знака целочисленный тип.