Почему unsigned short (multiply) unsigned short преобразуется в signed int? [дубликат]

этот вопрос уже есть ответ здесь:

почему unsigned short * unsigned short преобразовано в int в C++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

on

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


Edit: я продемонстрировал проблему на следующем изображении.

enter image description here

6 ответов


вы можете прочитать о неявные преобразования, особенно в разделе о числовые продвижения где написано

Prvalues малых интегральных типов (например,char) может быть преобразован в значения больших интегральных типов (например,int). В частности, арифметические операторы не принимать типы меньше, чем int в качестве аргументов

что выше говорит, что если вы используете что-то меньше, чем int (как unsigned short) в выражении, которое включает в себя арифметические операторы (что, конечно, включает в себя умножение), то значения будут повышены до int.


это обычные арифметические преобразования в действии.

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

C++11 §5/9:

" многие двоичные операторы, ожидающие операндов арифметического или перечислительного типа, вызывают преобразования и дают типы результатов аналогично. Этот цель состоит в том, чтобы дать общий тип, который также является типом результата. Этот шаблон называется обычным арифметические преобразования [...]

далее в параграфе описываются детали, которые сводятся к преобразованиям вверх по лестнице более общих типов, пока не будут представлены все аргументы. Самая низкая ступенька этой лестницы -комплексное продвижение обоих операндов двоичной операции, поэтому, по крайней мере, это выполняется (но преобразование может начните с более высокой ступени). И интегральное продвижение начинается с этого:

C++11 §4.5 / 1:

" в prvalue типа integer, кроме bool, char16_t, char32_t или wchar_t преобразования, целочисленное ранг (4.13) меньше, чем ранг int можно преобразовать в prvalue типа int если int могут представлять все значения типа источника; в противном случае исходное значение prvalue может быть преобразовано в значение типа prvalue unsigned int

важно то, что речь идет о типах, а не об арифметических выражениях. В вашем случае аргументы оператора умножения * превращается в int. Тогда умножение выполняется как int умножение, приводя к int результат.


как отметил Паоло м в комментарии USHRT_MAX типа int (это указано в 5.2.4.2.1 / 1: все такие макросы имеют тип не менее int).

так USHRT_MAX * USHRT_MAX уже int x int, никаких рекламных акций не происходит.

это вызывает переполнение целого числа со знаком в вашей системе, вызывая неопределенное поведение.


относительно предлагаемого решения:

unsigned u = static_cast<unsigned>(t*t);

это не помогает, потому что вызывает неопределенное поведение из-за переполнения целого числа со знаком. Как объясняют другие ответы,t превращается в int перед умножением происходит, по историческим причинам.

вместо этого вы можете использовать:

auto u = static_cast<unsigned int>(t) * t;

, который, после продвижение целого числа, это unsigned int умножить на int; а затем по остальным обычные арифметические преобразования на int превращается в unsigned int, и четко определенный модульное умножение.


с целочисленными правилами продвижения

USHRT_MAX значение повышается до int. затем мы делаем умножение 2 int (с возможным переполнением).


кажется, что никто еще не ответил на эту часть вопроса:

следует ли ожидать каких-либо проблем с этим решением?

u = static_cast<unsigned>(t*t);

Да, здесь есть проблема: он сначала вычисляет t*t и позволяет ему переполняться, а затем преобразует результат в unsigned. Переполнение целого числа вызывает неопределенное поведение в соответствии со стандартом C++ (хотя на практике оно всегда может работать нормально). Правильное решение:

u = static_cast<unsigned>(t)*t;

Примечание. что второе t превращается в unsigned перед умножением, потому что первый операнд unsigned.


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

самый простой способ избежать преобразования из беззнакового типа с меньшим рангом, чем подписанный тип с большим рангом, - убедиться, что преобразование выполняется в unsigned int, а не int.

это делается путем умножения на значение 1, которое имеет тип unsigned int. Из-за 1 будучи мультипликативная идентичность, результат останется без изменений:

unsigned short c = t * 1U * t;

сначала вычисляются операнды t и 1U. Левый операнд подписан и имеет меньший ранг, чем беззнаковый правый операнд, поэтому он преобразуется в тип правого операнда. Затем операнды умножаются, и то же самое происходит с результатом и оставшимся правым операндом. Для этой акции используется последний абзац В приведенном ниже стандарте.

В противном случае целочисленные промо-акции выполняются для обоих операндов. Затем к продвигаемым операндам применяются следующие правила:

-Если оба операнда имеют один и тот же тип, дальнейшее преобразование не требуется.

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

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