C语言中整数的快速符号

C语言中整数的快速符号,c,C,C中有一个符号函数: int sign(int x) { if(x > 0) return 1; if(x < 0) return -1; return 0; } 在这种情况下,我只得到一个比较 有没有办法避免比较呢 < > > > >编辑< /强>不回答问题,因为所有答案都是C++,使用比较(我应该避免)或不返回 -> 1 /代码>, + 1 < /代码>, 0 .< /P> int i=-10; int i = -10; if((i & 1

C中有一个符号函数:

int sign(int x)
{
    if(x > 0) return 1;
    if(x < 0) return -1;
    return 0;
}
在这种情况下,我只得到一个比较

有没有办法避免比较呢

< > > > >编辑< /强>不回答问题,因为所有答案都是C++,使用比较(我应该避免)或不返回<代码> -> 1 /代码>,<代码> + 1 < /代码>,<代码> 0 .< /P> <代码> int i=-10;
int i = -10;
if((i & 1 << 31) == 0x80000000)sign = 0;else sign = 1;
//sign 1 = -ve, sign 0 = -ve 
如果((i&1if
s(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)
第一部分(
x>>32
)给出-1表示负数,0表示0或正数。 如果x>0或等于INT_MIN,则第二部分给出1,否则为0。或者给出正确的最终答案

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

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

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

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

我已经使用GCC4.7.2在Sandy Bridge box上对您的函数进行了基准测试,每次调用大约需要1.2ns

以下速度大约快25%,每次通话速度约为0.9ns:

int sign(int x) {
    return (x > 0) - (x < 0);
}
有两点值得指出:

  • 基本性能水平非常高
  • 消除分支确实可以提高性能,但不会显著提高性能

  • 请看或更具体地说,这个答案:我认为编译后,原始版本将更快,只需一次测试和两次条件跳跃。一个好的编译器应该优化原始版本,使其成为无分支的指令流。“比较成本非常高”:这是怎么回事?与零比较通常是便宜或免费的,因为在我所知道的每种体系结构上,符号和零状态寄存器位都是自动设置的。这非常好。你可以通过在返回前放置
    x |=x>>1
    来摆脱INT|MIN错误。INT|MIN是10000…INT|MIN | INT|MIN>>1是11000…可以安全地求反。他是u使用按位移位运算符。在某些语言中,它是一个“符号传播右移位”(如JavaScript),这意味着符号不会改变。因此,使用
    x>>31
    移位32位浮点的所有位只留下符号。
    !!x
    转换为true或false(1或0),然后按位“或”将它们组合在一起(组合位)。右移有符号整数是未定义的行为!您需要将其转换为无符号整数类型:
    返回((无符号)x>>31)|(!!x)
    不仅如此,而且假设
    int
    为32位是完全不可移植的-您可能期望它是
    sizeof(int)*字符位
    位。有一件事你不能假设负数有一个特定的编码;符号大小、1的补码和2的补码都是众所周知的可能性,但C并没有将实现限制为其中之一。@很抱歉,你的变体错误地返回了
    x的所有负值
    1
    e> .这不是原海报想要的。
    int sign(int x)
    {
        // assumes 32-bit int/unsigned
        return ((unsigned)-x >> 31) - ((unsigned)x >> 31);
    }
    
    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) {    
        return (x>>31)|(!!x);
    }