簡體   English   中英

如何安全地比較兩個無符號整數計數器?

[英]How to safely compare two unsigned integer counters?

我們有兩個未簽名的計數器,我們需要對其進行比較以檢查某些錯誤情況:

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

我認為您誤解了代碼中的注釋:

我們除以2不會溢出iter_c * 2

無論值來自何處,寫a/2都是安全的,但是寫a*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增加或多或少都相同,則可能已經可以了。

注意溢出的發生。

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

實際上,發布的代碼似乎並沒有使用可能會回繞的計數器。

代碼中的注釋是說,比較a/2 > b而不是a > 2*b是更安全的,因為后者可能潛在地溢出而前者則不能。 a的類型比b的類型特別大。

通過強制將兩個值同時包裝,可以在包裝后立即標准化這些值。 包裹時要保持兩者之間的差異。

嘗試這樣的事情;

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;
}

如果僅使用64位是不夠的,那么您需要編寫自己的“變量增加”方法,而不是重載++運算符(如果不小心,可能會使您的代碼弄亂)。
該方法只會將var重置為“ 0”或其他有意義的值。

如果您要確保操作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比例的情況下精確地起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM