Files
ubbook/numeric/integer_promotion.md
2022-03-20 12:49:46 +03:00

76 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Integer promotion
C++ от C досталось тяжелое наследство. Одна его часть была исправлена и беспощадно зарезана для большей надежности — так, например, поступили с неявными `const` преобразованиями. Другая же часть, доставляющая не меньше проблем, перешла в первозданном виде.
В C и C++ много различных типов целых чисел разных размеров. И над ними определены операции. Правда, операции определены не для каждого типа чисел.
Например, здесь нет +, -, *, / для `uint16_t`. Но применить мы их можем.
```C++
uint16_t x = 1;
uint16_t y = 2;
auto a = x - y;
auto b = x + y;
auto c = x * y;
auto d = x / y;
```
и [результатом](https://godbolt.org/z/bdKjsor9T) операций над беззнаковыми числами станет число со знаком.
Но стоит тип хотя бы одного аргумента поменять на `uint32_t`, как результат сразу же [теряет знак](https://godbolt.org/z/aY1nhdr37).
## Что происходит?
Происходят две неявные операции:
1. Типы, меньшие `int`, приводятся к `int` (integer promotion). Знаковому! Независимо от знаковости исходного типа!
2. Когда в операции участвуют аргументы разных типов целых чисел, они приводятся к общему типу (usual arithmetic conversion):
- Меньший тип приводится к большему
- Если размеры одинаковы, то знаковый приводится к беззнаковому
Аналогичные операции проводятся и над числами с плавающей точкой.
За полной таблицей и цепочкой, что и в кого неявно превращается, стоит обратиться к тексту [стандарта](https://eel.is/c++draft/conv.rank).
## К чему это приводит?
1. К ошибкам в логике:
Неявные преобразования вовлекаются в любую операцию. Вы выполняете сравнение знакового и беззнакового числа и забыли явно привести типы? Готовьтесь к тому, что `-1 < 1` может [вернуть](https://godbolt.org/z/sqvrasjE4) `false`:
```C++
std::vector<int> v = {1};
auto idx = -1;
if (idx < v.size()) {
std::cout << "less!\n";
} else {
std::cout << "oops!\n";
}
```
2. К [неопределенному поведению](https://godbolt.org/z/M3Kx3e3q6):
```C++
unsigned short x=0xFFFF;
unsigned short y=0xFFFF;
auto z=x*y;
```
Integer promotion неявно приводит `x` и `y` к `int`, в котором происходит переполнение. Переполнение `int` — неопределенное поведение.
3. К трудностями в переносе программ с одной платформы на другую. Если меняется размер `int`/`long` применение правил неявных конверсий к вашему коду также [меняется](https://godbolt.org/z/hs59o3zca):
```C++
std::cout << (-1L < 1U);
```
Выводит разные значения в зависимости от размера типа `long`.
## Что делать?
1. Не смешивать в одном выражении знаковые и беззнаковые типы
2. Уделять особое внимание коду, работающему с типами, меньшими `int`.
3. Включать предупреждения от компилятора (`-Wconversion`, не всегда работает)
## Полезные ссылки
1. https://eel.is/c++draft/conv.prom
2. https://eel.is/c++draft/expr.arith.conv
3. https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules
4. https://shafik.github.io/c++/2021/12/30/usual_arithmetic_confusions.html