Подтвердить что ты не робот

Почему 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


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

введите описание изображения здесь

4b9b3361

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

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