Fast sign of integer in C

前端 未结 5 1097
轮回少年
轮回少年 2020-12-06 11:57

There is a sign function in C:

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

Unfortunately, comp

相关标签:
5条回答
  • 2020-12-06 12:16
    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);
    }
    

    The first part (x>>32) gives you -1 for negative numbers and 0 for 0 or positive numbers. The second part gives you 1 if x > 0 or equal to INT_MIN, and 0 otherwise. Or gives you the right final answer.

    There's also the canonical return (x > 0) - (x < 0);, but unfortunately most compilers will use branches to generate code for that, even though there are no visible branches. You can try to manually turn it into branchless code as:

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

    which is arguably better than the above as it doesn't depend on implementation defined behavior, but has a subtle bug in that it will return 0 for INT_MIN.

    0 讨论(0)
  • 2020-12-06 12:18
    int i = -10;
    if((i & 1 << 31) == 0x80000000)sign = 0;else sign = 1;
    //sign 1 = -ve, sign 0 = -ve 
    
    0 讨论(0)
  • 2020-12-06 12:24

    First of all, integer comparison is very cheap. It's branching that can be expensive (due to the risk of branch mispredictions).

    I have benchmarked your function on a Sandy Bridge box using gcc 4.7.2, and it takes about 1.2ns per call.

    The following is about 25% faster, at about 0.9ns per call:

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

    The machine code for the above is completely branchless:

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

    Two things are worth pointing out:

    1. The base level of performance is very high.
    2. Eliminating branching does improve performance here, but not dramatically.
    0 讨论(0)
  • 2020-12-06 12:25

    If s(x) is a function that returns the sign-bit of x (you implemented it by ((unsigned int)x)>>31), you can combine s(x) and s(-x) in some way. Here is a "truth table":

    x > 0: s(x) = 0; s(-x) = 1; your function must return 1

    x < 0: s(x) = 1; s(-x) = 0; your function must return -1

    x = 0: s(x) = 0; s(-x) = 0; your function must return 0

    So you can combine them in the following way:

    s(-x) - s(x)
    
    0 讨论(0)
  • 2020-12-06 12:26
    int sign(int x) {    
        return (x>>31)|(!!x);
    }  
    
    0 讨论(0)
提交回复
热议问题