Fra*_* D. 3 language-agnostic math floating-point bit-manipulation integer-arithmetic
是否有任何无分支或类似的黑客用于将整数钳位到0到255的间隔,或者是一个双倍到0.0到1.0的间隔?(两个范围都是封闭的,即端点是包含的.)
我正在使用明显的最小 - 最大检查:
int value = (value < 0? 0 : value > 255? 255 : value);
Run Code Online (Sandbox Code Playgroud)
但有没有办法让它更快 - 类似于"模"钳value & 255?有没有办法用浮点做类似的事情?
我正在寻找便携式解决方案,所以最好不要使用CPU/GPU特定的东西.
请注意,如果您编写代码,您的编译器可能已经为您提供了所需的内容value = min (value, 255)。如果存在的话,可以将MIN其转换为指令,或者转换为比较,然后进行条件移动,例如CMOVccx86 上的指令。
以下代码假定整数的二进制补码表示形式,这在今天通常是给定的。从布尔值到整数的转换不应涉及幕后分支,因为现代架构要么提供可直接用于形成掩码的指令(例如SETcc在 x86 和ISETccNVIDIA GPU 上),要么可以应用预测或条件移动。如果缺少所有这些,编译器可能会根据算术右移发出无分支指令序列来构造掩码,就像 Boann 的答案一样。但是,存在一些编译器可能做错事情的残余风险,因此当有疑问时,最好反汇编生成的二进制文件进行检查。
int value, mask;
mask = 0 - (value > 255); // mask = all 1s if value > 255, all 0s otherwise
value = (255 & mask) | (value & ~mask);
Run Code Online (Sandbox Code Playgroud)
在许多体系结构上,三元运算符的使用?:也可能导致无分支指令序列。硬件可能支持选择类型指令,这些指令本质上相当于三元运算符的硬件,例如ICMP在 NVIDIA GPU 上。或者它提供了CMOVx86 中的(条件移动)或 ARM 上的预测,这两者都可用于实现三元运算符的无分支代码。与前一种情况一样,人们希望检查反汇编的二进制代码,以绝对确定生成的代码没有分支。
int value;
value = (value > 255) ? 255 : value;
Run Code Online (Sandbox Code Playgroud)
对于浮点操作数,现代浮点单元通常提供直接映射到 C/C++ 标准数学函数FMIN和的指令。或者, 和可以转换为比较,然后是条件移动。同样,谨慎的做法是检查生成的代码以确保它是无分支的。FMAXfmin()fmax()fmin()fmax()
double value;
value = fmax (fmin (value, 1.0), 0.0);
Run Code Online (Sandbox Code Playgroud)
这是我用来将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;
}
Run Code Online (Sandbox Code Playgroud)
它仍然有一个分支,但是一个方便的事情是你可以通过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);
}
Run Code Online (Sandbox Code Playgroud)