Skip to content

Latest commit

 

History

History
75 lines (55 loc) · 4.45 KB

integer_promotion.md

File metadata and controls

75 lines (55 loc) · 4.45 KB

Integer promotion

C++ от C досталось тяжелое наследство. Одна его часть была исправлена и беспощадно зарезана для большей надежности — так, например, поступили с неявными const преобразованиями. Другая же часть, доставляющая не меньше проблем, перешла в первозданном виде.

В C и C++ много различных типов целых чисел разных размеров. И над ними определены операции. Правда, операции определены не для каждого типа чисел.

Например, здесь нет +, -, *, / для uint16_t. Но применить мы их можем.

uint16_t x = 1;
uint16_t y = 2;
auto a = x - y;
auto b = x + y;
auto c = x * y;
auto d = x / y;

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

Что происходит?

Происходят две неявные операции:

  1. Типы, меньшие int, приводятся к int (integer promotion). Знаковому! Независимо от знаковости исходного типа!
  2. Когда в операции участвуют аргументы разных типов целых чисел, они приводятся к общему типу (usual arithmetic conversion):
    • Меньший тип приводится к большему
    • Если размеры одинаковы, то знаковый приводится к беззнаковому

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

К чему это приводит?

  1. К ошибкам в логике: Неявные преобразования вовлекаются в любую операцию. Вы выполняете сравнение знакового и беззнакового числа и забыли явно привести типы? Готовьтесь к тому, что -1 < 1 может вернуть false:
    std::vector<int> v = {1};
    auto idx = -1;
    if (idx < v.size()) {
        std::cout << "less!\n";
    } else {
        std::cout << "oops!\n";
    }
  2. К неопределенному поведению:
    unsigned short x=0xFFFF;
    unsigned short y=0xFFFF;
    auto z=x*y;
    Integer promotion неявно приводит x и y к int, в котором происходит переполнение. Переполнение int — неопределенное поведение.
  3. К трудностями в переносе программ с одной платформы на другую. Если меняется размер int/long применение правил неявных конверсий к вашему коду также меняется:
    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