如何安全地比较两个无符号整数计数器?

时间:2018-06-08 18:53:00

标签: c++ c integer-overflow unsigned-integer

我们有两个无符号计数器,我们需要比较它们以检查一些错误情况:

uint32_t a, b;
// a increased in some conditions
// b increased in some conditions
if (a/2 > b) {
   perror("Error happened!");
   return -1;
}

问题是ab有一天会溢出。如果a溢出,它仍然可以。但如果b溢出,那将是一个误报。如何使这个检查防弹?

我知道让ab uint64_t延迟这种误报。但它仍然无法完全解决这个问题。

===============

让我澄清一点:计数器用于跟踪内存分配,这个问题可以在dmalloc/chunk.c找到:

#if LOG_PNT_SEEN_COUNT
  /*
   * We divide by 2 here because realloc which returns the same
   * pointer will seen_c += 2.  However, it will never be more than
   * twice the iteration value.  We divide by two to not overflow
   * iter_c * 2.
   */
  if (slot_p->sa_seen_c / 2 > _dmalloc_iter_c) {
    dmalloc_errno = ERROR_SLOT_CORRUPT;
    return 0;
  }
#endif

6 个答案:

答案 0 :(得分:6)

我认为你误解了代码中的评论:

  

我们除以2而不是溢出iter_c * 2

无论值来自哪里,写a/2都是安全的,但写a*2是不安全的。无论您使用的是哪种无符号类型,都可以将数字除以2,而相乘可能会导致溢出。

如果条件如下:

if (slot_p->sa_seen_c > _dmalloc_iter_c * 2) {

然后大约一半的输入会导致错误的情况。话虽这么说,如果你担心计数器溢出,你可以将它们包装在一个类中:

class check {
    unsigned a = 0;
    unsigned b = 0;
    bool odd = true;
    void normalize() {
        auto m = std::min(a,b);
        a -= m;
        b -= m;
    }
public:
    void incr_a(){ 
        if (odd) ++a;
        odd = !odd;
        normalize();
    }
    void incr_b(){ 
        ++b;
        normalize();
    }
    bool check() const { return a > b;}
}

请注意,为了完全避免溢出,您必须采取其他措施,但如果ab增加或减少相同的数量,这可能已经没问题了。

答案 1 :(得分:4)

注意发生溢出。

uint32_t a, b;
bool aof = false;
bool bof = false;

if (condition_to_increase_a()) {
  a++;
  aof = a == 0;
}

if (condition_to_increase_b()) {
  b++;
  bof = b == 0;
}

if (!bof && a/2 + aof*0x80000000 > b) {
   perror("Error happened!");
   return -1;
}

每个a, b相互依赖地具有2个 32 + 1个反映值和条件增量的不同状态。不知何故,需要超过uint32_t的信息。可以使用uint64_t,变体代码路径或辅助变量,例如bool

答案 2 :(得分:4)

发布的代码实际上似乎没有使用可能包含的计数器。

代码中的评论是说比较a/2 > b而不是a > 2*b更安全,因为后者可能会溢出而前者无法溢出。 a的类型尤其如此b的类型。

答案 3 :(得分:2)

通过强制它们同时换行,在值换行时将值标准化。在它们包裹时保持两者之间的差异。

尝试这样的事情;

uint32_t a, b;
// a increased in some conditions
// b increased in some conditions
if (a or b is at the maximum value) {
   if (a > b)
   {
     a = a-b; b = 0;
   }
   else
   {
     b = b-a; a = 0;
   }
}
if (a/2 > b) {
   perror("Error happened!");
   return -1;
}

答案 4 :(得分:1)

如果即使使用64位还不够,那么你需要编写自己的代码" var increase"方法,而不是重载++运算符(如果你不小心可能会弄乱你的代码)。
该方法只需将var重置为' 0'或其他一些有意义的价值。

答案 5 :(得分:1)

如果您的目的是确保行动x的发生频率不超过行动y的两倍,我建议您采取以下措施:

uint32_t x_count = 0;
uint32_t scaled_y_count = 0;

void action_x(void)
{
  if ((uint32_t)(scaled_y_count - x_count) > 0xFFFF0000u)
    fault();
  x_count++;
}

void action_y(void)
{
  if ((uint32_t)(scaled_y_count - x_count) < 0xFFFF0000u)
    scaled_y_count+=2;
}

在许多情况下,可能需要减少增量scaled_y_count时使用的比较中的常数,以限制可以“存储”多少action_y次操作。但是,即使操作次数超出uint32_t的范围,上述操作也应该在操作保持接近2:1比例平衡的情况下正常工作。