Неявні правила перетворення типу в операторах C ++


167

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

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

і так далі ...

Чи завжди вираз буде оцінюватися як більш точний тип? Чи відрізняються правила для Java? Виправте мене, якщо я сформулював це запитання неточно.


15
Майте на увазі, ^це XOR.
GManNickG

15
@int ^ float = помилка компіляції :)
Серж Дундич

Відповіді:


223

У 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перетворюється на a 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>

1
"Мінімальний розмір операцій - int." - Це було б дуже дивно (що з архітектурами, які ефективно підтримують char / short операції?) Це справді в специфікації C ++?
Rafał Dowgird

3
@Rafal: Так. int повинен бути найбільш ефективним цілим числом для роботи на певній платформі. char завжди повинен бути 1, але короткий може бути такого ж розміру, як int.
Мартін Йорк

1
@ Rafał: так, це дуже дивно, і це є в стандарті. У багатьох випадках архітектура, яку ви описуєте, може використовувати її надефективний charтип. Якщо значення char + chara присвоєне a char, воно може просто виконати арифметику charі, наприклад, обернутись. Але якщо результат присвоєний, intто він повинен виконувати арифметику в такому великому типі, щоб отримати правильний результат, коли він більше CHAR_MAX.
Стів Джессоп

2
Я просто хочу наголосити на тому, що int отримує статус непідписаного int !!! Я боровся з помилками цілими днями, тому що в мене склалося враження, що обидва будуть просунуті до int або long, щоб можливий негативний результат не спричинив переповнення / обгортання.
nitsas

10
Приклад проблеми " int стає підвищеним до непідписаного int ": ((int) 4) - ((unsigned int) 5)це призведе 4294967295до 32-бітових int та 32-бітових неподписаних int .
nitsas

33

Арифметичні операції з 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, інший повинен бути перетворений у long double.

- В іншому випадку, якщо будь-який операнд подвійний, інший перетворюється на подвійний.

- В іншому випадку, якщо будь-який операнд плаває, інший повинен бути перетворений на плаваючий.

- В іншому випадку інтегральні промоції (4.5) повинні виконуватися на обох операндах.54)

- Тоді, якщо будь-який операнд довго не підписаний, то інший повинен бути перетворений у ненаписаний довгий.

- В іншому випадку, якщо один операнд - це довгий int, а інший - непідписаний int, тоді, якщо довгий int може представляти всі значення неподписаного int, неподписаний int перетворюється на довгий int; інакше обидва операнди перетворюються на неподписані довгі int.

- В іншому випадку, якщо будь-який операнд довгий, інший перетворюється на довгий.

- В іншому випадку, якщо будь-який операнд не підписаний, інший перетворюється на непідписаний.

[Примітка: в іншому випадку єдиний залишився випадок - це те, що обидва операнди є int]


3
... до тих пір, поки інший тип не є ні doubleні long double.
CB Bailey

1
@Charles: Правильно. Я процитував відповідний розділ Стандарту для подальшого уточнення.
Наваз

То чи може ціле число завжди перетворюватися на плаваюче без втрати даних? (наприклад, за допомогою нульового показника та використання всього для мантіси)?
Марко А.

1
Ця відповідь застаріла. Запропонувати оновлення. Зокрема, long longі тут unsigned longне звертаються.
chux

@MarcoA. 32-бітний floatне має достатньої кількості бітів у мантісі (24 біта для IEEE-754 ) для 32-розрядних int, тому можливі певні втрати даних. 64-бітний doubleповинен бути просто чудовим.
Марк Викуп

17

Оскільки в інших відповідях не йдеться про правила в C ++ 11, ось один. З C ++ 11 стандарту (проект n3337) § 5/9 (підкреслена різниця):

Ця закономірність називається звичайними арифметичними перетвореннями , які визначаються наступним чином:

- Якщо будь-який операнд має тип перерахунку, то перетворення не виконуються; якщо інший операнд не має того ж типу, вираз є неправильним.

- Якщо будь-який операнд має тип long double, інший повинен бути перетворений у long double.

- В іншому випадку, якщо будь-який операнд подвійний, інший перетворюється на подвійний.

- В іншому випадку, якщо будь-який операнд плаває, інший повинен бути перетворений на плаваючий.

- В іншому випадку інтегральні промоції повинні виконуватися на обох операндах. Тоді до рекламованих операндів застосовуються такі правила:

- Якщо обидва операнди мають один і той же тип, подальше перетворення не потрібно.

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

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

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

- В іншому випадку обидва операнди повинні бути перетворені на цілий цілий число без підпису, що відповідає типу операнда з підписаним цілим числом.

Дивіться тут список, який часто оновлюється.


1
Ці правила були однаковими для всіх версій C ++, за винятком перелічених масштабів, які додані звичайно в C ++ 11
MM

6

Ця відповідь направлена ​​значною мірою на коментар @ RafałDowgird:

"Мінімальний розмір операцій - int." - Це було б дуже дивно (що з архітектурами, які ефективно підтримують char / short операції?) Це справді в специфікації C ++?

Майте на увазі, що стандарт C ++ має все важливе правило "як-ніби". Див. Розділ 1.8: Виконання програми:

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

Компілятор не може встановити intрозмір 8 біт, навіть якщо це був найшвидший, оскільки стандарт мандатує 16-бітовий мінімум int.

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

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

Це справедливо в цілому на додавання, віднімання та множення. Це взагалі не вірно для поділу чи модуля.


4

Якщо ви виключаєте неподписані типи, існує впорядкована ієрархія: підписаний знак char, короткий, int, long, long long, float, double, long double. По-перше, все, що надходить до int у вище, перетвориться на int. Тоді в двійковій операції тип нижчого ранжу буде перетворений на вищий, а результати - тип вищого. (Ви зауважите, що з ієрархії, щоразу, коли бере участь плаваюча точка і інтегральний тип, інтегральний тип буде перетворений на тип з плаваючою точкою.)

Непідписаний трохи ускладнює речі: це порушує рейтинг, і частини рейтингу стають визначеними для реалізації. Через це краще не змішувати підписані та непідписані в одному виразі. (Більшість експертів C ++, схоже, уникають безпідписаних, якщо не будуть залучені побітні операції. Це, принаймні, те, що рекомендує Stroustrup.)


3
Stroustrup може порекомендувати те, що йому подобається, але використання знаку, здатного intдо числа, яке ніколи не повинно бути негативним, - це повна витрата всього 50% наявного асортименту. У мене, звичайно, немає Stroustrup, але я використовую unsignedза замовчуванням і signedлише тоді, коли у мене є причина.
підкреслити_

1
Це все добре і добре, підкреслюйте, до дня, коли вам доведеться відняти. Основна проблема непідписаних чисел у C ++ полягає в тому, що під час віднімання вони залишаються без підпису. Тож припустимо, що ви пишете функцію, щоб перевірити, чи std :: vector в порядку. Ви можете написати, bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;і тоді ви будете роздратовані, виявивши, що він виходить з ладу для порожніх векторів, тому що розмір () - 1 повертає
18446744073709551615.

3

Моє рішення до проблеми отримав WA (неправильну відповідь), то я змінив одну з них, intщоб long long intі він дав AC (прийняти) . Раніше я намагався це зробити long long int += int * int, і після того, як я випрямив це long long int += long long int * int. Гуглінг, який я придумав,

1. Арифметичні перетворення

Умови перетворення типів:

Виконані умови ---> Перетворення

  • Будь-який операнд має подвійний тип . ---> Інший операнд перетворений у тип long double .

  • Попередня умова не виконується, і будь-який операнд типу подвійний . ---> Інший операнд перетворений у тип double .

  • Попередні умови не дотримані, і будь-який операнд має тип float . ---> Інший операнд перетворений у тип float .

  • Попередні умови не виконані (жоден операнд не має плаваючого типу). ---> Інтегральні промоції виконуються на операндах наступним чином:

    • Якщо один з операндів має тип непідписаних довгий , то інший операнд перетвориться в тип без знака довго .
    • Якщо попередня умова не виконується, і якщо будь-який операнд має тип long, а інший типу не підписаний int , обидва операнди перетворюються на тип без підпису .
    • Якщо попередні дві умови не виконані, і якщо будь-який операнд має тип long , t інший операнд перетворюється на тип long .
    • Якщо попередні три умови не виконуються, і якщо будь-який операнд не підписаний int , інший операнд перетворюється на тип без підпису .
    • Якщо жодна з попередніх умов не виконується, обидва операнди перетворюються на тип int .

2. Правила перетворення цілого числа

  • Цілі акції:

Типи цілих чисел, менші за int, рекламуються, коли над ними виконується операція. Якщо всі значення вихідного типу можуть бути представлені як int, значення меншого типу перетворюється в int; в іншому випадку він перетворюється на непідписаний int. Промоції цілих чисел застосовуються як частина звичайних арифметичних перетворень до певних виразів аргументів; операнди операторів unary +, - і ~; і операнди операторів зміни.

  • Ранг конверсії цілого числа:

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

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

1

У всьому розділі 4 йдеться про конверсії, але я думаю, що вас найбільше цікавлять:

4.5 Інтегральні акції [conv.prom]
Rvalue типу char, підписаний char, неподписаний char, короткий int або неподписаний короткий int може бути перетворений у rvalue типу int, якщо int може представляти всі значення типу джерела; в іншому
мудрий, джерело Rvalue може бути перетворений в RValue типу без знака міжнар.
Рівень типу wchar_t (3.9.1) або тип перерахунку (7.2) може бути перетворений у рецензію першого
з наступних типів, який може представляти всі значення його базового типу: int, непідписаний int,
long або неподписаний довго.
Rvalue для інтегрального бітового поля (9.6) може бути перетворена в rvalue типу int, якщо int може представляти всі
значення бітового поля; в іншому випадку він може бути перетворений в unsigned int, якщо unsigned int може повторити-
обурювати всі значення бітового поля. Якщо бітове поле ще більше, до нього не застосовується інтегральне просування. Якщо
бітове поле має перелічений тип, воно розглядається як будь-яке інше значення цього типу для цілей просування.
Рівень bool типу може бути перетворений в rvalue типу int, при цьому false стає нулем, а true
стає єдиним.
Ці перетворення називаються цілісними акціями.

4.6 Просування з плаваючою комою [conv.fpprom]
Rvalue типу float може бути перетворена в rvalue типу double. Значення не змінюється.
Це перетворення називається просуванням з плаваючою комою.

Тому всі перетворення з участю float - результат float.

Тільки той, що включає обидва int - результат int: int / int = int


1

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

У виразах, в яких бере участь дійсне число і ціле число, ціле число буде переведено на дійсне число. Наприклад, у int + float тип виразу float.

Інша відмінність пов'язана з можливостями типу. Наприклад, вираз, що включає int та long int, призведе до типу long int.


2
Це не правда. На майоних платформах a long"більший" ніж a, floatале який тип long+ float?
CB Bailey

1
-1: Що ви маєте на увазі під найбільшим ? Чи поплавок більший за int? Або навпаки ?
Пол Р

2
Дякую за ваші коментарі. Так, розмір у байтах тут взагалі не цікавить. Як виходить, очевидно, що розміщення найбільшого курсиву недостатньо для пояснення відповіді. У всякому разі, це не має сенсу пояснювати це більш глибоко, оскільки зараз є й інші, дуже ґрунтовні відповіді.
Балтасарк

-2

Обливайся!

Перетворення відбуваються зліва направо.

Спробуйте це:

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

8
Це не через конверсію, а через перевагу оператора. j + i * kпризведе до 101.
gartenriese
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.