Проверка переполнения/переполнения в С++?

Есть ли общий способ проверки переполнения или потери значимости данного типа данных (uint32, int и т. д.)?

Я делаю что-то вроде этого:

uint32 a,b,c;
... //initialize a,b,c
if(b < c) {
   a -= (c - b)
}

Когда я печатаю после некоторых итераций, он отображает большое число, например: 4294963846.


person Legend    schedule 08.03.2010    source источник


Ответы (5)


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

uint32 a,b;
//assign values
uint32 result = a + b;
if (result < a) {
    //Overflow
}

Для вашего конкретного чека будет:

if (a > (c-b)) {
    //Underflow
}
person Stephen L    schedule 08.03.2010
comment
это тот факт, что в случае переполнения ответ всегда будет подписанным (отрицательное целое число)? - person Faizan; 24.02.2013
comment
Переполнение целых чисел без знака никогда не будет знаковым, скорее это будет меньшее целое число без знака, чем любое из исходных значений. - person Stephen L; 28.02.2013
comment
Будьте осторожны, подписанное переполнение является неопределенным поведением в C. - person Alexei Sholik; 05.09.2013

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

class MyInt
{
     int val;
     MyInt(const int&nval){ val = nval;} // cast from int
     operator int(){return val;} // cast to int

    // then just overload ALL the operators... putting your check in
};

//typedef int sint32;
typedef MyInt sint32;

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

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

person matt    schedule 08.03.2010
comment
Существует версия этого SafeInt, о которой я узнал сегодня вечером. Вероятно, неплохая идея использовать что-то подобное большую часть времени, просто не в коде, критичном для производительности. - person HostileFork says dont trust SE; 17.06.2012

В Cert есть хороший справочник по переполнение целочисленного значения со знаком, что является неопределенным поведением и unsigned wrapping, которого нет, и они охватывают все операторы.

В документе приведен следующий код проверки беззнакового переноса при вычитании с использованием предварительных условий:

void func(unsigned int ui_a, unsigned int ui_b) {
  unsigned int udiff;
  if (ui_a < ui_b){
    /* Handle error */
  } else {
    udiff = ui_a - ui_b;
  }
  /* ... */
}

и с постусловиями:

void func(unsigned int ui_a, unsigned int ui_b) {
  unsigned int udiff = ui_a - ui_b;
  if (udiff > ui_a) {
    /* Handle error */
  }
  /* ... */
}

Если вы gcc 5, вы можете использовать __builtin_sub_overflow:

__builtin_sub_overflow( ui_a, ui_b, &udiff ) 
person Shafik Yaghmour    schedule 24.04.2014

У Boost есть аккуратная библиотека под названием Safe Numerics. В зависимости от того, как вы создаете экземпляр безопасного шаблона, библиотека выдаст исключение, когда произойдет переполнение или потеря памяти. См. https://www.boost.org/doc/libs/1_74_0/libs/safe_numerics/doc/html/index.html.

person Angel E.    schedule 03.05.2021

Я приведу здесь еще один возможный подход на случай, если доступен целочисленный тип большего (размер x2). В этом случае можно предотвратить переполнение за счет немного большего количества вычислений.

// https://gcc.godbolt.org/z/fh9G6Eeah
#include <exception>
#include <limits>
#include <iostream>

using integer_t = uint32_t; // The desired type
using bigger_t = uint64_t; // Bigger type

constexpr integer_t add(const integer_t a, const integer_t b)
   {
    static_assert(sizeof(bigger_t)>=2*sizeof(integer_t));
    constexpr bigger_t SUP = std::numeric_limits<integer_t>::max();
    constexpr bigger_t INF = std::numeric_limits<integer_t>::min();
    // Using larger type for operation
    bigger_t res = static_cast<bigger_t>(a) + static_cast<bigger_t>(b);
    // Check overflows
    if(res>SUP) throw std::overflow_error("res too big");
    else if(res<INF) throw std::overflow_error("res too small");
    // Back to the original type
    return static_cast<integer_t>(res); // No danger of narrowing here
   }


//---------------------------------------------------------------------------
int main()
{
    std::cout << add(100,1) << '\n';
    std::cout << add(std::numeric_limits<integer_t>::max(),1) << '\n';
}
person MatG    schedule 04.05.2021