|
|
严肃的双杠 · 华为nova9开发者选项在哪里?-百度经验· 4 月前 · |
|
|
成熟的鼠标垫 · 纽卡斯尔联足球俱乐部 - Saudipedia· 5 月前 · |
|
|
无聊的皮带 · List<T>.AddRange(IEnum ...· 6 月前 · |
|
|
憨厚的黄瓜 · 嘉兴港区工业污水处理有限公司_中华人民共和国 ...· 6 月前 · |
|
|
高大的消防车 · 如何解决无法安装itunes_iPhone玩 ...· 7 月前 · |
假设我有两个无符号字节
b
和
x
。我需要将
bsub
计算为
b - x
,将
badd
计算为
b + x
。但是,我不希望在这些操作过程中发生下溢/溢出。例如(伪代码):
b = 3; x = 5;
bsub = b - x; // bsub must be 0, not 254
和
b = 250; x = 10;
badd = b + x; // badd must be 255, not 4
最明显的方法包括分支:
bsub = b - min(b, x);
badd = b + min(255 - b, x);
我只是想知道是否有更好的方法来做到这一点,即通过一些老生常谈的比特操作?
这样如何:
bsum = a + b;
bsum = (bsum < a || bsum < b) ? 255 : bsum;
bsub = a - b;
bsub = (bsub > a || bsub > b) ? 0 : bsub;
一种简单的方法是检测溢出并相应地重置该值,如下所示
bsub = b - x;
if (bsub > b)
bsub = 0;
badd = b + x;
if (badd < b)
badd = 255;
}
在使用-O2编译时,GCC可以将溢出检查优化为条件赋值。
我测量了与其他解决方案相比的优化程度。在我的PC上进行1000000000+操作时,这个解决方案和@ShafikYaghmour的平均时间为4.2秒,而@chux的平均时间为4.8秒。这种解决方案也更具可读性。
您还可以使用 Boost Library Incubator 上的安全数值库。它提供了int、long等的临时替代。这保证你永远不会得到未检测到的溢出、下溢等。
文章 Branchfree Saturating Arithmetic 提供了实现这一点的策略:
他们的加法解决方案如下:
u32b sat_addu32b(u32b x, u32b y)
u32b res = x + y;
res |= -(res < x);
return res;
}
针对uint8_t进行了修改:
uint8_t sat_addu8b(uint8_t x, uint8_t y)
uint8_t res = x + y;
res |= -(res < x);
return res;
}
他们的减法解决方案是:
u32b sat_subu32b(u32b x, u32b y)
u32b res = x - y;
res &= -(res <= x);
return res;
}
针对uint8_t进行了修改:
uint8_t sat_subu8b(uint8_t x, uint8_t y)
uint8_t res = x - y;
res &= -(res <= x);
return res;
}
所有这些都可以在无符号字节算术中完成
// Addition without overflow
return (b > 255 - a) ? 255 : a + b
// Subtraction without underflow
return (b > a) ? 0 : a - b;
如果你要大量调用这些方法,最快的方法不是位操作,而可能是一个查找表。为每个操作定义一个长度为511的数组。减号(减法)示例
static unsigned char maxTable[511];
memset(maxTable, 0, 255); // If smaller, emulates cutoff at zero
maxTable[255]=0; // If equal - return zero
for (int i=0; i<256; i++)
maxTable[255+i] = i; // If greater - return the difference
该数组是静态的,并且只初始化一次。现在您的减法可以定义为内联方法或使用预编译器:
#define MINUS(A,B) maxTable[A-B+255];
它是如何工作的?嗯,你想要预先计算无符号字符的所有可能的减法。结果从-255到+255不等,总共有511个不同的结果。我们定义了一个包含所有可能结果的数组,但因为在C中我们不能从负索引访问它,所以我们使用+255 (在A-B+255中)。您可以通过定义指向数组中心的指针来删除此操作。
const unsigned char *result = maxTable+255;
#define MINUS(A,B) result[A-B];
像这样使用它:
bsub = MINUS(13,15); // i.e 13-15 with zero cutoff as requested
请注意,执行速度非常快。只需一次减法和一次指针引用即可得到结果。没有分支。静态数组非常短,因此它们将被完全加载到CPU的缓存中,以进一步加快计算速度
同样适用于加法,但有一点不同的表(前256个元素将是索引,最后255个元素将等于255,以模拟超过255的截止值。
如果你坚持bits运算,使用(a>b)的答案是错误的。这仍然可以作为分支来实现。使用符号位技术
// (num1>num2) ? 1 : 0
#define is_int_biggerNotEqual( num1,num2) ((((__int32)((num2)-(num1)))&0x80000000)>>31)
现在你可以使用它来计算减法和加法。
如果要模拟函数max()、min()而不进行分支,请使用:
inline __int32 MIN_INT(__int32 x, __int32 y){ __int32 d=x-y; return y+(d&(d>>31)); }
|
|
严肃的双杠 · 华为nova9开发者选项在哪里?-百度经验 4 月前 |
|
|
成熟的鼠标垫 · 纽卡斯尔联足球俱乐部 - Saudipedia 5 月前 |
|
|
憨厚的黄瓜 · 嘉兴港区工业污水处理有限公司_中华人民共和国生态环境部 6 月前 |