Почему 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: я продемонстрировал проблему на следующем изображении.
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 может быть преобразовано в значение типа prvalueunsigned 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. Левый операнд подписан и имеет меньший ранг, чем беззнаковый правый операнд, поэтому он преобразуется в тип правого операнда. Затем операнды умножаются, и то же самое происходит с результатом и оставшимся правым операндом. Для этой акции используется последний абзац В приведенном ниже стандарте.
В противном случае целочисленные промо-акции выполняются для обоих операндов. Затем к продвигаемым операндам применяются следующие правила:
-Если оба операнда имеют один и тот же тип, дальнейшее преобразование не требуется.
-В противном случае, если оба операнда имеют целочисленные типы со знаком или оба без знака целочисленные типы, операнд с типом меньшего целого ранга преобразования преобразуется к типу операнда с большим рангом.
-в противном случае, если операнд имеет беззнаковый тип имеет ранг больше или равный ранг типу другого операнда, то операнд с знаковый целочисленный тип преобразуется к типу операнда с беззнаковый целочисленный тип.