В настоящее время я работаю над безопасной целочисленной библиотекой для C ++. Я сталкивался с некоторыми проблемами при реализации вычитания.
Вот что я начал с:
#include <limits>
#include <stdexcept>
template<typename I>
class safe_int
{
I val;
public:
typedef I value_type;
static constexpr I max = std::numeric_limits<I>::max();
static constexpr I min = std::numeric_limits<I>::min();
safe_int(I i) : val { i } { };
safe_int &operator+=(I rhs)
{
if( val > 0 && rhs > max - val )
throw std::overflow_error("");
else if( val < 0 && rhs < min - val )
throw std::underflow_error("");
val += rhs;
return *this;
}
};
Сначала я попытался написать operator-=
так:
safe_int &operator-=(I rhs)
{
return operator+=(-rhs);
}
но очевидно, что это не удастся, если ввести -0x80000000
в системе дополнения до двух.
Затем я попытался сделать это так:
safe_int &operator-=(I rhs)
{
if(rhs < -max)
throw std::overflow_error("");
return operator+=(-rhs);
}
Но это не работает для все, что меньше 0
(например, -1 - -0x80000000
должно быть 0x7fffffff
, но вместо этого сообщается о переполнении).
Затем я попытался это сделать:
safe_int &operator-=(I rhs)
{
if( rhs < -max && val > 0 )
throw std::overflow_error("");
return operator+=(-rhs);
}
Но теперь, хотя он правильно ловит В случае, когда происходит переполнение, оно само вызывает переполнение в допустимом случае (например, -1 - -0x80000000
, где - -0x80000000
переполняется).
На данный момент я считаю, что нет способа повторно использовать код из дополнения при перехвате все угловые дела. Поэтому мне, вероятно, следует написать другой код для вычитания.
Как правильно проверить, что целочисленное переполнение не произойдет до вычитания?
Вот небольшая тестовая программа:
int main(void)
{
safe_int<int> i = -1;
i -= -2147483648;
return 0;
}
Предполагается, что нет конкретного размера целого числа. Не полагайтесь на неопределенное поведение.