我正在寻找一个表达式,使我能够使用以下属性进行编写:
f(x, SOME_CONSTANT) -> returns -x (or any negative value)
f(x, SOME_CONSTANT2) -> returns x (or any positive value)
f(0, SOME_CONSTANT) -> returns 0
f(0, SOME_CONSTANT2) -> returns 0
没有乘法/分支,尽可能高效。
乍一看x^0x80000000好像是候选,但是当x为0时就不行了。
最佳答案
好吧,我终于想出了如何有效地做到这一点:
Java:
int f(int x, int y) {
return (((x >> 31) | ((unsigned)-x >> 31)) ^ y) - y;
}
C/C++:
int f(int x, int y) {
return (((x > 0) - (x < 0)) ^ y) - y;
}
以上这些函数返回 -sgn(x)
y 是 -1 和 sgn(x)
否则。
或者,如果我们只需要为除 -2^31(最小无符号整数值)之外的每个值工作,为了保留绝对值,这是翻转符号的函数,具体取决于变量 y :
int f(int x, int y) {
return (x ^ y) - y; // returns -x for y == -1, x otherwise
}
推导:
-x == ~x + 1 == (x ^ 0xFFFFFFFF) + 1 == (x ^ -1) + 1 == (x ^ -1) - (-1)。用 y 替换 -1,我们得到一个双变量公式,它有一个有趣的属性,如果 y 设置为 0,则返回不变的 x,因为 (x ^ 0) 和减去 0 都不会改变结果。现在极端情况是当此公式不起作用时 x 等于 0x8000000。这可以通过应用 sgn(x) 函数来解决,所以我们有 (sgn(x) ^ y) - y)
。最后,我们用不使用分支的众所周知的公式替换 sgn(x) 函数。
关于performance - 如何在没有乘法/分支的情况下使用一些常量和运算符翻转整数值的符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4866187/