如何使用一些常量和运算符翻转整数值的符号而不进行乘法/分支

发布于 2024-10-15 23:03:17 字数 332 浏览 5 评论 0原文

我正在寻找一个表达式,它使我能够编写具有以下属性的表达式:

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 时它不起作用。

I am looking for an expression which would enable me to write with the following properties:

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

without multiplication/branching, as efficient as possible.

At first glance x ^ 0x80000000 seems like a candidate, but it doesn't work when x is 0.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

安静 2024-10-22 23:03:17

好吧,我终于想出了如何有效地做到这一点:

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 是 -1sgn (x)否则。

或者,如果我们只需要处理 -2^31(最小无符号 int 值)以外的每个值,并且具有保留绝对值的优点,则这是翻转符号的函数,具体取决于变量 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) 函数。

Ok, I finally figured out how to do this efficiently:

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;
}

These above functions return -sgn(x) y is -1 and sgn(x) otherwise.

Or, if we just need to work for every value other then -2^31 (minimum unsigned int value), with the benefit of preserving the absolute value, this is the function which flips the sign, depending on the variable y:

int f(int x, int y) {
    return (x ^ y) - y; // returns -x for y == -1, x otherwise
}

The derivation:
-x == ~x + 1 == (x ^ 0xFFFFFFFF) + 1 == (x ^ -1) + 1 == (x ^ -1) - (-1). Substituting -1 with y, we obtain a two-variable formula which has an interesting property that returns unchanged x if y is set to 0, because neither (x ^ 0) nor subtracting 0 changes the result. Now the corner case is if x is equal to 0x8000000 when this formula doesn't work. This can be fixed by applying the sgn(x) function, so we have (sgn(x) ^ y) - y). Finally, we replace the sgn(x) functions with the well-known formulas which do not use branching.

初吻给了烟 2024-10-22 23:03:17

这是一个相当简洁的表达式,可以解决这个问题:

return ((x < 0 ^ y) & x!=0) << 31 | (x!=0) << 31>> 31& 0x7fffffff& x| x==0x80000000 ;

这适用于 32 位 2 的补码整数,其中 x 是输入,y 是 1 或 0。1 表示返回 x 的相反符号,0 表示返回与 x 相同的符号。

这是函数 f() 中该表达式的更长版本。我添加了一些测试用例来验证。

#include <limits.h>
#include <stdio.h>

int bitsm1 = 31;
int rightbits = 0x7fffffff;


int f (x, y) {
  int sign_x = x < 0;
  int signtemp = sign_x ^ y; 
  int notzero = x!=0;
  int v = notzero << bitsm1;
  v = v >> bitsm1;
  v = v & rightbits;
  int b = v & x;
  int c = (signtemp & notzero) << bitsm1;
  int z = c | b;
  int res = z | (x==INT_MIN);
  return res;
}


int main () {
 printf("%d\n", f(0,0)); // 0
 printf("%d\n", f(0,1)); // 0
 printf("%d\n", f(1,0)); // +
 printf("%d\n", f(1,1)); // -
 printf("%d\n", f(-1,0)); // -
 printf("%d\n", f(-1,1)); // +
 printf("%d\n", f(INT_MAX,0)); // +
 printf("%d\n", f(INT_MAX,1)); // -
 printf("%d\n", f(INT_MIN,0)); // -
 printf("%d\n", f(INT_MIN,1)); // +


 return 0;
}

Here's a rather terse expression that will solve the problem:

return ((x < 0 ^ y) & x!=0) << 31 | (x!=0) << 31 >> 31 & 0x7fffffff & x | x==0x80000000 ;

This will work for 32 bit 2's complement integers, where x is the input, and y is either 1 or 0. 1 means return the opposite sign of x, 0 means return the same sign as x.

Here's a lengthier version of that expression in function f(). I've added some test cases to verify.

#include <limits.h>
#include <stdio.h>

int bitsm1 = 31;
int rightbits = 0x7fffffff;


int f (x, y) {
  int sign_x = x < 0;
  int signtemp = sign_x ^ y; 
  int notzero = x!=0;
  int v = notzero << bitsm1;
  v = v >> bitsm1;
  v = v & rightbits;
  int b = v & x;
  int c = (signtemp & notzero) << bitsm1;
  int z = c | b;
  int res = z | (x==INT_MIN);
  return res;
}


int main () {
 printf("%d\n", f(0,0)); // 0
 printf("%d\n", f(0,1)); // 0
 printf("%d\n", f(1,0)); // +
 printf("%d\n", f(1,1)); // -
 printf("%d\n", f(-1,0)); // -
 printf("%d\n", f(-1,1)); // +
 printf("%d\n", f(INT_MAX,0)); // +
 printf("%d\n", f(INT_MAX,1)); // -
 printf("%d\n", f(INT_MIN,0)); // -
 printf("%d\n", f(INT_MIN,1)); // +


 return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文