[英]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;
}
問題是a
和b
會有一天會溢出。 如果a
溢出,它仍然確定。 但是如果b
溢出,那將是一個錯誤的警報。 如何使這張支票防彈?
我知道制作a
和b
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;}
}
請注意,要完全避免溢出,您必須采取其他措施,但是如果將a
和b
增加或多或少都相同,則可能已經可以了。
注意溢出的發生。
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.