C中整数的快速符号

C中有一个符号函数:

int sign(int x) { if(x > 0) return 1; if(x < 0) return -1; return 0; } 

不幸的是,比较成本非常高,所以我需要修改function以减少比较次数。

我尝试了以下方法:

 int sign(int x) { int result; result = (-1)*(((unsigned int)x)>>31); if (x > 0) return 1; return result; } 

在这种情况下,我只得到一个比较。

有什么方法可以避免比较吗?

编辑 可能重复没有给出问题的答案,因为所有答案都是C ++,使用比较(我应该避免)或不返回-1+1 0

首先,整数比较非常便宜。 它的分支可能很昂贵(由于分支错误预测的风险)。

我使用gcc 4.7.2在Sandy Bridge盒子上对你的function进行了基准测试,每次调用大约需要1.2ns。

以下约快25%,每次通话约0.9ns:

 int sign(int x) { return (x > 0) - (x < 0); } 

上面的机器代码是完全无分支的:

 _sign: xorl %eax, %eax testl %edi, %edi setg %al shrl $31, %edi subl %edi, %eax ret 

有两件事值得指出:

  1. 基本级别的性能非常高。
  2. 消除分支确实提高了性能,但并不显着。
 int sign(int x) { // assumes 32-bit int and 2s complement signed shifts work (implementation defined by C spec) return (x>>31) | ((unsigned)-x >> 31); } 

第一部分( x>>32 )给出-1为负数,0给0为0或正数。 如果x> 0或等于INT_MIN,则第二部分给出1,否则为0。 或者给你正确的答案。

还有规范的return (x > 0) - (x < 0); 但遗憾的是,大多数编译器都会使用分支来为其生成代码,即使没有可见的分支。 您可以尝试手动将其转换为无分支代码:

 int sign(int x) { // assumes 32-bit int/unsigned return ((unsigned)-x >> 31) - ((unsigned)x >> 31); } 

这可以说比上面更好,因为它不依赖于实现定义的行为,但有一个微妙的错误,因为它将为INT_MIN返回0。

 int sign(int x) { return (x>>31)|(!!x); } 

如果s(x)是返回x的符号位的x (通过((unsigned int)x)>>31实现它,则可以以某种方式组合s(x)s(-x) 。 这是一个“真值表”:

x> 0:s(x)= 0; s(-x)= 1; 你的函数必须返回1

x <0:s(x)= 1; s(-x)= 0; 你的函数必须返回-1

x = 0:s(x)= 0; s(-x)= 0; 你的函数必须返回0

所以你可以用以下方式组合它们:

 s(-x) - s(x) 
 int i = -10; if((i & 1 << 31) == 0x80000000)sign = 0;else sign = 1; //sign 1 = -ve, sign 0 = -ve