将整数钳位到0-255并加倍到0.0-1.0的黑客?

时间:2015-07-25 21:17:29

标签: math language-agnostic floating-point bit-manipulation integer-arithmetic

是否有任何无分支或类似的黑客用于将整数钳位到0到25​​5的间隔,或者是一个两倍到0.0到1.0的间隔? (两个范围都是封闭的,即端点是包容性的。)

我使用明显的最小 - 最大检查:

int value = (value < 0? 0 : value > 255? 255 : value);

但有没有办法让它更快 - 类似于&#34; modulo&#34;钳value & 255?有没有办法用浮点做类似的事情?

我正在寻找便携式解决方案,所以最好不要使用CPU / GPU特定的东西。

3 个答案:

答案 0 :(得分:5)

这是我用来将int钳位到0到25​​5范围的技巧:

/**
 * 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;

对于浮点操作数,现代浮点单元通常提供FMINFMAX指令,这些指令直接映射到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
}