如何正确检查减法前不会发生整数下溢和上溢?
How can I correctly check that integer underflow and overflow will not happen before subtraction?
我目前正在为 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
的任何东西都不起作用(e.x。-1 - -0x80000000
应该是 0x7fffffff
但报告溢出)。
然后我尝试了这个:
safe_int &operator-=(I rhs)
{
if( rhs < -max && val > 0 )
throw std::overflow_error("");
return operator+=(-rhs);
}
但现在即使它正确地捕获了会发生溢出的情况,它本身也会在有效情况下导致溢出(e.x。-1 - -0x80000000
,其中 - -0x80000000
溢出)。
在这一点上,我认为没有办法在捕获所有极端情况的同时重用添加的代码。因此,我可能应该为减法编写不同的代码。
如何正确检查减法前不会发生整数溢出?
这是一个小测试程序:
int main(void)
{
safe_int<int> i = -1;
i -= -2147483648;
return 0;
}
假设没有特定大小的整数。不要依赖未定义的行为。
template<class I>
bool valid_add( I lhs, I rhs ) {
static constexpr I max = std::numeric_limits<I>::max();
static constexpr I min = std::numeric_limits<I>::min();
if( rhs > 0 && lhs > max - rhs ) return false;
if( rhs < 0 && lhs < min - rhs ) return false;
return true;
}
template<class I>
bool valid_subtract( I lhs, I rhs ) {
static constexpr I max = std::numeric_limits<I>::max();
static constexpr I min = std::numeric_limits<I>::min();
if ((rhs < 0) && (lhs > max + rhs)) return false;
if ((rhs > 0) && (lhs < min + rhs)) return false;
return true;
}
请注意,这两个函数基本上是在问同一个问题。首先我们问"what direction will rhs
move our result from lhs
"。然后我们检查 lhs
是否是 "far enough away" 从 min
或 max
在那个方向。
在您的代码中只需注入:
if (!valid_add(val, rhs))
throw "whatever";
和
if (!valid_subtract(val, rhs))
throw "whatever";
如果不在整数类型上使用它,您将需要更改此代码。在浮点类型上,有更多的复杂性。
检查操作是否有效后,直接执行。不要调用其他函数。
我目前正在为 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
的任何东西都不起作用(e.x。-1 - -0x80000000
应该是 0x7fffffff
但报告溢出)。
然后我尝试了这个:
safe_int &operator-=(I rhs)
{
if( rhs < -max && val > 0 )
throw std::overflow_error("");
return operator+=(-rhs);
}
但现在即使它正确地捕获了会发生溢出的情况,它本身也会在有效情况下导致溢出(e.x。-1 - -0x80000000
,其中 - -0x80000000
溢出)。
在这一点上,我认为没有办法在捕获所有极端情况的同时重用添加的代码。因此,我可能应该为减法编写不同的代码。
如何正确检查减法前不会发生整数溢出?
这是一个小测试程序:
int main(void)
{
safe_int<int> i = -1;
i -= -2147483648;
return 0;
}
假设没有特定大小的整数。不要依赖未定义的行为。
template<class I>
bool valid_add( I lhs, I rhs ) {
static constexpr I max = std::numeric_limits<I>::max();
static constexpr I min = std::numeric_limits<I>::min();
if( rhs > 0 && lhs > max - rhs ) return false;
if( rhs < 0 && lhs < min - rhs ) return false;
return true;
}
template<class I>
bool valid_subtract( I lhs, I rhs ) {
static constexpr I max = std::numeric_limits<I>::max();
static constexpr I min = std::numeric_limits<I>::min();
if ((rhs < 0) && (lhs > max + rhs)) return false;
if ((rhs > 0) && (lhs < min + rhs)) return false;
return true;
}
请注意,这两个函数基本上是在问同一个问题。首先我们问"what direction will rhs
move our result from lhs
"。然后我们检查 lhs
是否是 "far enough away" 从 min
或 max
在那个方向。
在您的代码中只需注入:
if (!valid_add(val, rhs))
throw "whatever";
和
if (!valid_subtract(val, rhs))
throw "whatever";
如果不在整数类型上使用它,您将需要更改此代码。在浮点类型上,有更多的复杂性。
检查操作是否有效后,直接执行。不要调用其他函数。