【问题标题】:How can I correctly check that integer underflow and overflow will not happen before subtraction?如何正确检查在减法之前不会发生整数下溢和上溢?
【发布时间】:2020-01-29 17:45:58
【问题描述】:

我目前正在为 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;
}

假设没有特定大小的整数。不要依赖未定义的行为。

【问题讨论】:

  • 有一些特殊的指令(加法、减法、乘法)可以告知溢出 - 但我怀疑是否有任何可移植的指令。您可以尝试寻找它们。
  • @ALX23z 如您所知,我没有使用程序集,也没有在检查溢出之前添加。我打算让它完全便携。
  • 我知道这不是重点,但你检查过boost.org/doc/libs/develop/libs/safe_numerics 吗?
  • @Tarc 我不使用 Boost。安装一个巨大的库是一件很痛苦的事情,否则会很简单。
  • 是的,但至少它是经过策划的,而且我认为 Safe Numerics 旨在便于携带。也许值得一读,看看作者使用的技术。

标签: c++ integer subtraction integer-overflow


【解决方案1】:
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;
}

请注意,这两个函数基本上都在问同一个问题。首先我们问“rhs 会将我们的结果从lhs 移动到什么方向”。然后我们检查lhs 是否与minmax 在那个方向上“足够远”。

在您的代码中简单地注入:

if (!valid_add(val, rhs))
   throw "whatever";

if (!valid_subtract(val, rhs))
   throw "whatever";

如果不在整数类型上使用,则需要更改此代码。在浮点类型上,有更多的复杂性。

在您检查操作是否有效后,只需执行此操作。不要调用其他函数。

【讨论】:

  • 第一个是下溢,第二个是上溢对吗?
  • @S.S.Anne 我在上面的代码中有 4 个不同的边界检查,我不确定你所说的第一个或第二个是什么意思。从某种意义上说,通过max是上溢,通过min是下溢。
  • 我的意思是第二个函数,检查减法。
  • @S.S.Anne 然后没有。通过最大值是溢出,而不是下溢。将 lhs &gt; max + rhs 重新排列为 lhs - rhs &gt; max 以查看(不安全,但在数学上等效)版本的测试。
猜你喜欢
  • 2019-05-17
  • 2011-08-12
  • 2018-02-28
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多