Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Three-way conditional in c++ to determine sign equivalance of two numbers

I need the most efficient way (in cpu cycles) to determine if two numbers have the same/different sign. But the catch is if either number is zero I need to be able to distinguish it from numbers with same/different signs (ie. zero is treated as a "third" sign). The following code is similar to what I need, but the return values can be anything as long as there are only three distinct return values.

int foo(int x, int y) {
    if (x * y > 0) return 1;
    if (x * y < 0) return -1;
    return 0;
}

For my specific problem, the values are in the range [-6, 6] and X is guaranteed to not be 0. I found a solution to find if two numbers have the same sign, and altered it to get the following solution.

return y? (((x^y) >= 0)? 1 : -1) : 0;

There should be some bitops/comparisons that give faster results than using multiplication, branching, comparisons.

like image 814
Justin Avatar asked Jul 21 '10 08:07

Justin


2 Answers

Here is another version (with ugly, non-portable bit manipulation tricks):

int foo(int x, int y) {
    return ((x^y) >> 4) - ((x^(-y)) >> 4);
}

Some explanations:

  • ((x^y) >> 4) is -1 if exactly one of x and y is negative, otherwise it is 0.
  • ((x^(-y)) >> 4) is -1 if exactly one of x and -y is negative, otherwise it is 0.
  • If x > 0 and y > 0, the result will be 0 - (-1) = 1.
  • If x < 0 and y < 0, the result will be 0 - (-1) = 1.
  • If x > 0 and y = 0, the result will be 0 - 0 = 0.
  • If x < 0 and y = 0, the result will be (-1) - (-1) = 0.
  • If x > 0 and y < 0, the result will be (-1) - 0 = -1.
  • If x < 0 and y > 0, the result will be (-1) - 0 = -1.

Assumes two's complement arithmetic and assumes that >> shifts with sign-extension.

like image 55
Jukka Suomela Avatar answered Sep 24 '22 03:09

Jukka Suomela


How about:

int foo(int x,int y)
{
    // As suggested by Luther Blissett below in the comments.
    // Increased the size of the array to 16x16.
    // This allows for simpler optimization for the compiler
    // Also use +8 rather +6 in the hopes that compiler optimization will be easier
    // you never know (there may be some fancy trick.
    static int sign[16][16] = {
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 1, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1, -1, -1, -1, -1},
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1},
                { -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1}
            };

    return sign[x+8][y+8];
}

This should be fast as there is no branching that will stall the processor.

Using g++ -O3 -S:

__Z3fooii:
  pushl   %ebp
  movl    %esp, %ebp
  movl    8(%ebp), %eax
  movl    12(%ebp), %edx
  popl    %ebp
  sall    $4, %eax
  addl    %edx, %eax
  movl    _ZZ3fooiiE4sign+544(,%eax,4), %eax
  ret
like image 39
Martin York Avatar answered Sep 22 '22 03:09

Martin York