是否有任何无分支或类似的黑客用于将整数钳位到0到255的间隔,或者是一个两倍到0.0到1.0的间隔? (两个范围都是封闭的,即端点是包容性的。)
我使用明显的最小 - 最大检查:
int value = (value < 0? 0 : value > 255? 255 : value);
但有没有办法让它更快 - 类似于&#34; modulo&#34;钳value & 255
?有没有办法用浮点做类似的事情?
我正在寻找便携式解决方案,所以最好不要使用CPU / GPU特定的东西。
答案 0 :(得分:5)
这是我用来将int钳位到0到255范围的技巧:
/**
* Clamps the input to a 0 to 255 range.
* @param v any int value
* @return {@code v < 0 ? 0 : v > 255 ? 255 : v}
*/
public static int clampTo8Bit(int v) {
// if out of range
if ((v & ~0xFF) != 0) {
// invert sign bit, shift to fill, then mask (generates 0 or 255)
v = ((~v) >> 31) & 0xFF;
}
return v;
}
它仍然有一个分支,但是一个方便的事情是你可以通过ORing将它们组合在一起测试几个int中的任何一个是否超出范围,这使得在所有它们都是常见的情况下更快在范围内。例如:
/** Packs four 8-bit values into a 32-bit value, with clamping. */
public static int ARGBclamped(int a, int r, int g, int b) {
if (((a | r | g | b) & ~0xFF) != 0) {
a = clampTo8Bit(a);
r = clampTo8Bit(r);
g = clampTo8Bit(g);
b = clampTo8Bit(b);
}
return (a << 24) + (r << 16) + (g << 8) + (b << 0);
}
答案 1 :(得分:3)
请注意,如果编码value = min (value, 255)
,您的编译器可能已经为您提供了所需的内容。这可以转换为MIN
指令(如果存在),或转换为比较后跟条件移动,例如x86上的CMOVcc
指令。
以下代码假定整数的两个补码表示,这通常是今天给出的。从布尔到整数的转换不应涉及引擎盖下的分支,因为现代架构要么提供可直接用于形成掩码的指令(例如x86上的SETcc
和NVIDIA GPU上的ISETcc
,或者可以应用预测或有条件的移动。如果缺少所有这些,编译器可以发出基于算术右移的无分支指令序列,以构建一个掩码,沿着Boann的答案。但是,编译器可能存在一些错误的遗留风险,因此如果有疑问,最好将生成的二进制文件反汇编以进行检查。
int value, mask;
mask = 0 - (value > 255); // mask = all 1s if value > 255, all 0s otherwise
value = (255 & mask) | (value & ~mask);
在许多体系结构中,使用三元运算符?:
也可以生成无分支指令序列。硬件可以支持选择类型的指令,这些指令本质上是三元运算符的硬件等价物,例如NVIDIA GPU上的ICMP
。或者它在x86中提供CMOV
(条件移动),或在ARM上提供预测,这两者都可用于为三元运算符实现无分支代码。与前一种情况一样,人们希望检查反汇编的二进制代码,以确保结果代码没有分支。
int value;
value = (value > 255) ? 255 : value;
对于浮点操作数,现代浮点单元通常提供FMIN
和FMAX
指令,这些指令直接映射到C / C ++标准数学函数fmin()
和{{1 }}。或者,可以将fmax()
和fmin()
转换为比较,然后进行条件移动。同样,检查生成的代码以确保它是无分支的是明智的。
fmax()
答案 2 :(得分:0)
我用这个东西,100%无分支。
int clampU8(int val)
{
val &= (val<0)-1; // clamp < 0
val |= -(val>255); // clamp > 255
return val & 0xFF; // mask out
}