﻿# V1083\. Signed integer overflow in arithmetic expression\. This leads to undefined behavior\.

Анализатор обнаружил арифметическое выражение, в котором может произойти переполнение знакового числа\.

Рассмотрим пример:

```cpp
long long foo()
{
  long longOperand = 0x7FFF'FFFF;
  long long y = longOperand * 0xFFFF;
  return y;
}
```

По правилам C и C\+\+ результирующим типом выражения `longOperand * 0xFFFF` будет `long`\. При использовании компилятора MSVC на Windows тип `long` [имеет размер 4 байта](https://pvs-studio.ru/ru/blog/terms/0012/)\. Максимальное значение, которое может быть представлено этим типом, равно 2'147'483'647 в десятичной системе или 0x7FFF'FFFF в шестнадцатеричной\. При умножении переменной `longOperand` на 0xFFFF \(65 535\) ожидается результат 0x7FFF'7FFF'0001\. Однако согласно стандарту C \(см\. стандарт [С18 пункт 6\.5](https://web.archive.org/web/20181230041359if_/http:/www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#page=74) параграф 5\) и C\+\+ \(см\. стандарт С\+\+20 [пункт 7\.1](https://timsong-cpp.github.io/cppwp/n4861/expr#pre-4) параграф 4\) [переполнение знаковых чисел](https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow) приводит к неопределённому поведению\.

Исправить этот код можно несколькими способами\.

Если требуется произвести корректные вычисления, необходимо использовать типы, размеры которых будут достаточны для отображения чисел\. Если число не помещается в машинное слово, то можно воспользоваться одной из библиотек для работы с длинной арифметикой\. Например, [GMP](https://gmplib.org/), [MPRF](https://www.mpfr.org/), [cnl](https://github.com/johnmcfarlane/cnl)\.

Пример выше можно исправить следующим образом:

```cpp
long long foo()
{
  long longOperand = 0x7FFF'FFFF;
  long long y = static_cast<long long>(longOperand) * 0xFFFF;
  return y;
}
```

Если переполнение знаковых чисел является неожидаемым поведением и требует обработки, то можно воспользоваться специальными библиотеками для безопасной работы с числами\. Например, [boost::safe\_numerics](https://github.com/boostorg/safe_numerics) или [Google Integers](https://github.com/google/integers)\.

Если требуется реализовать циклическую арифметику для знаковых чисел с определённым по стандарту поведением, то для расчётов можно воспользоваться беззнаковыми числами\. В случае их переполнения происходит "оборачивание" числа по модулю `2 ^ n`, где `n` — количество бит в числе\.

Рассмотрим одно из возможных решений на основе [`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast) \(C\+\+20\):

```cpp
#include <concepts>
#include <type_traits>
#include <bit>
#include <functional>

namespace detail
{
  template <std::signed_integral R,
            std::signed_integral T1,
            std::signed_integral T2,
            std::invocable<std::make_unsigned_t<T1>,
                           std::make_unsigned_t<T2>> Fn>
  R safe_signed_wrapper(T1 lhs, T2 rhs, Fn &&op)
    noexcept(std::is_nothrow_invocable_v<Fn,
                                         std::make_unsigned_t<T1>,
                                         std::make_unsigned_t<T2>>)
  {
    auto uLhs = std::bit_cast<std::make_unsigned_t<T1>>(lhs);
    auto uRhs = std::bit_cast<std::make_unsigned_t<T2>>(rhs);

    auto res = std::invoke(std::forward<Fn>(op), uLhs, uRhs);

    using UR = std::make_unsigned_t<R>;
    return std::bit_cast<R>(static_cast<UR>(res));
  }
}
```

Функция `std::bit_cast` приводит `lhs' и 'rhs` к соответствующим беззнаковым представлениям\. Далее на двух преобразованных операндах выполняется некоторая арифметическая операция\. Затем результат расширяется или сужается до нужного результирующего типа и превращается в знаковый\.

При таком подходе знаковые числа будут повторять семантику беззнаковых в арифметических операциях, что исключает неопределённое поведение\. 

Например, по этой [ссылке](https://godbolt.org/z/vqP5bjYWz) можно увидеть, что компилятор вправе оптимизировать код, если существует вероятность переполнения знакового числа\. 

Рассмотрим пример подробнее:

```cpp
bool is_max_int(int32_t a)
{
  return a + 1 < a;
}
```

Если `a` равно `MAX_INT`, то условие `a + 1 < a` будет равно `false`\. Таким образом часто проверяют не произошло ли переполнение\. 

Однако компилятор генерирует такой код:

```cpp
is_max_int(int):                        # @is_max_int(int)
        xor     eax, eax
        ret
```

Инструкция ассемблера `xor eax, eax` обнуляет результат выполнения функции `is_max_int`\. В результате последняя всегда возвращает `true` вне зависимости от значения `a`\. В данном случае это результат неопределённого поведения при переполнении\.

В случае применения беззнакового представления такого не происходит:

```cpp
is_max_int(int):                        # @is_max_int(int)
        cmp     edi, 2147483647
        sete    al
        ret
```

Компилятор сгенерировал код, который проверяет условие\.

## Дополнительная настройка диагностического правила

Диагностическое правило поддерживает дополнительные настройки\. Для их активации [добавьте](https://pvs-studio.ru/ru/docs/manual/0040/) соответствующий комментарий в файл с исходным кодом или в файл конфигурации диагностических правил [`.pvsconfig`](https://pvs-studio.ru/ru/docs/manual/6630/?ysclid=mauu5h7en219076378)\.

### Обнаружение беззнакового переполнения

Настройка позволяет анализатору дополнительно обнаруживать переполнение беззнаковых числовых типов\. Чтобы включить настройку, добавьте комментарий:

```cpp
//+V1083 CHECK_UNSIGNED_OVERFLOW:YES
```

Для её отключения используйте комментарий:

```cpp
//+V1083 CHECK_UNSIGNED_OVERFLOW:NO
```

### Чувствительный режим

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

```cpp
//+V1083 PEDANTIC_MODE
//+V1083 PEDANTIC_MODE:YES
```

Для её отключения используйте комментарий:

```cpp
//+V1083 PEDANTIC_MODE:NO
```