Неявные правила преобразования типов в операторах C++

Я хочу лучше знать, когда я должен бросить. Каковы неявные правила преобразования типов в C++ при добавлении, умножения и т. д. Например,

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

и так далее...

всегда ли выражение будет оцениваться как более точный тип? Отличаются ли правила для Java? Пожалуйста, поправьте меня, если я сформулировал этот вопрос неточно.

9 ответов


в операторах C++ (для типов POD) всегда действуют на объекты одного типа.
Таким образом, если они не совпадают, один будет повышен, чтобы соответствовать другому.
Тип результата операции совпадает с типом операндов (после преобразования).

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

Примечание. Минимальный размер операций -int. Так что short/char звание int перед операцией проводится.

во всех ваших выражений int превращается в float перед операция выполнена. Результатом операции является float.

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>

арифметические операции с участием float результаты float.

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

для более подробного ответа. Посмотрите, что говорится в разделе §5/9 стандарта C++

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

этот шаблон называется обычный арифметические преобразования, которые определяется следующим образом:

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

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

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

- в противном случае Интеграл акциях (4.5) должны выполняться на обоих операнды.54)

- тогда, если любой из операндов без подписи долго другой должен быть преобразовано в unsigned long.

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

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

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

[Примечание: В противном случае единственным оставшимся случаем является что оба операнда имеют тип int ]


поскольку другие ответы не говорят о правилах в C++11, Вот один. Из стандарта C++11 (проект n3337) §5/9:

этот шаблон называется обычные арифметические преобразования, которые определяются следующим образом:

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

- Если операнд имеет тип long двойник, другое будет преобразовано к длиннему двойнику.

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

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

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

- Если оба операнда имеют одинаковый тип, нет необходимо дальнейшее преобразование.

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

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

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

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

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


этот ответ направлен в значительной степени на комментарий, сделанный @RafałDowgird:

" минимальный размер операций-int."- Это было бы очень странно (как насчет архитектур, которые эффективно поддерживают char / short операции?) Это действительно в спецификации C++?

имейте в виду, что стандарт C++ имеет важнейшее правило "как если бы". См. раздел 1.8: выполнение программы:

3) это положение иногда называется правилом "как будто", потому что реализация может игнорировать любые требования Стандарта до тех пор, пока результат будет таким, как если бы требование было выполнено, как можно определить из наблюдаемого поведения программы.

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

поэтому в случае теоретического компьютера с супер-быстрые 8-битные операции, неявное продвижение к int для арифметики может иметь значение. Однако для многих операций вы не можете сказать, действительно ли компилятор выполнял операции с точностью int а затем преобразуется в char для хранения в переменной, или если операции были сделаны в char все время.

рассмотрим, например,unsigned char = unsigned char + unsigned char + unsigned char, где добавление будет переполняться (предположим, значение 200 для каждого). Если вас повысили до int, вы получите 600, который затем будет неявно брошен в unsigned char, который будет обертывать по модулю 256, таким образом, давая окончательный результат 88. Если бы вы не делали таких рекламных акций, вам пришлось бы обернуть между первыми двумя дополнениями, что уменьшило бы проблему с 200 + 200 + 200 to 144 + 200, что составляет 344, что сокращается до 88. Другими словами, программа не знает разницы, поэтому компилятор может игнорировать мандат на выполнение промежуточных операций в int Если операнды имеют более низкий ранг, чем int.

это верно в целом для сложения, вычитания и умножения. Это не верно в целом для деления или модуля.


Если исключить неподписанные типы, то существует упорядоченный иерархия: подписанный char, short, int, long, long long, float, двойной, двойной. Во-первых, все, что происходит перед int в выше будет преобразовано в int. Затем в двоичной операции, более низкий ранжированный тип будет преобразован в более высокий, а результаты будут типа выше. (Вы заметите, что из иерархия, в любое время, когда плавающая точка и интегральный тип участвует, интегральный тип будет преобразован в плавающий тип точки.)

Unsigned немного усложняет ситуацию: это нарушает рейтинг, и части рейтинга становятся реализацией. Потому что это, лучше не смешивать подписанные и неподписанные в одном и том же выражение. (Большинство экспертов C++, похоже, избегают неподписанных, если задействованы побитовые операции. Это, по крайней мере, то, что Страуструп рекомендует.)


мой решение до Преобразование

  • либо операнд имеет тип двойной. --- >Другой операнд преобразуется в тип двойной.

  • предыдущее условие не выполнено, и любой операнд имеет тип двойной. --- >Другой операнд преобразуется в тип двойной.

  • предыдущие условия не выполнены, и любой операнд имеет тип плавание. --- >Другой операнд преобразуется в тип плавание.

  • предыдущие условия не выполнены (ни один из операндов не имеет плавающих типов). --- >Интегральные акции выполняются на операндах следующим образом:

    • если любой из операндов имеет тип без знака долго, другой операнд преобразуется в тип без знака долго.
    • если предыдущее условие не выполнено, и если любой операнд имеет тип долго и другой тип unsigned int, оба операнда преобразуются в тип без знака долго.
    • если два предыдущих условия не выполнены, и если один из операндов имеет тип долго, t другой операнд преобразуется в тип долго.
    • Если предыдущие три условия не будут выполнены, и если любой из операндов имеет тип unsigned int, другой операнд преобразуется в тип без знака int.
    • если ни одно из предыдущих условий, то оба операнда преобразуются в тип int.

2 . правила преобразования целых чисел

  • Целое Число Акций:

целочисленные типы, меньшие, чем int, повышаются при выполнении над ними операции. Если все значения исходного типа могут быть представлены в виде int, значение меньшего типа преобразуется в int; в противном случае он преобразуется в unsigned int. Целочисленные промо-акции применяются как часть обычных арифметических преобразований к определенным выражениям аргументов; операндам унарных операторов +, -, и~; и операндам операторов сдвига.

  • Целочисленный Ранг Преобразования:

    • никакие два целочисленных типа со знаком не должны иметь одинаковый ранг, даже если они имеют одинаковое представление.
    • ранг целого числа со знаком тип должен быть больше ранга любого знакового целочисленного типа с меньшей точностью.
    • в звании long long int будет больше, чем ранг long int, который должен быть выше ранга int, который должен быть выше ранга short int, который должен быть выше ранга signed char.
    • ранг любого целочисленного типа без знака должен равняться рангу соответствующего целочисленного типа со знаком, если таковой имеется.
    • ранг любого стандартного целого числа тип должен быть больше ранга любого расширенного целочисленного типа с одинаковой шириной.
    • в звании char равняется рангу signed char и unsigned char.
    • ранг любого расширенного знакового целочисленного типа относительно другого расширенного знакового целочисленного типа с той же точностью определяется реализацией, но по-прежнему подчиняется другим правилам определения ранга преобразования целых чисел.
    • для всех целых типов T1, T2 и T3, если T1 имеет больший ранг чем T2 и T2 имеет больший ранг, чем T3, то T1 имеет больший ранг, чем T3.
  • Обычные Арифметические Преобразования:

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

вся Глава 4 говорит о конверсиях, но я думаю, что вы должны быть в основном заинтересованы в них:

4.5 интегральный акции [conv.пром]
Собой rvalue типа char, чар, подписанные, неподписанные символ, короткое int или unsigned короткие int может быть преобразован в rvalue типа int, если int может представлять все значения исходного типа; другое -
мудрый, исходный rvalue может быть преобразован в rvalue типа unsigned int.
Собой rvalue типа wchar_t (3.9.1) или тип перечисления (7.2) может быть преобразован в rvalue первого
из следующих типов, которые могут представлять все значения его базового типа: int, unsigned int,
длинные или без подписи.
Rvalue для интегрального битового поля (9.6) может быть преобразовано в rvalue типа int, если int может представлять все
значения битового поля; в противном случае он может быть преобразован в unsigned int, если unsigned int может rep -
возмущаться всеми значениями битового поля. Если бит-поле еще больше, к нему не применяется интегральное продвижение. Если
бит-поле имеет перечисляемый тип, оно рассматривается как любое другое значение этого типа для целей продвижения.
Rvalue типа bool может быть преобразовано в rvalue типа int, при этом false становится нулем и true
стать одним из них.
Эти преобразования называются интегральными акциями.

4.6 продвижение с плавающей запятой [conv.fpprom]
Rvalue типа float можно преобразовать в собой rvalue типа double. Значение не изменяется.
Это преобразование называется продвижением с плавающей запятой.

таким образом, все преобразования с использованием float - результат float.

только тот, который включает оба int-результат int : int / int = int


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

в выражениях, в которых участвуют действительное число и целое число, целое число будет повышено до действительного числа. Например, в int + float типом выражения является float.

другой различия связаны с возможностями данного типа. Например, выражение, включающее int и long int, приведет к типу long int.


предостережение!

преобразования происходят слева направо.

попробуйте это:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0