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
有两件事值得指出:
- 基本级别的性能非常高。
- 消除分支确实提高了性能,但并不显着。
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