3

I often need this kind of function, for example, for understand the direction of the touches on iPhone and the only way to solve this problem it's by using logic, like this:

int dir,distY;
distY = newY-oldY;

if (distY > 0) 
{
    dir = 1;
}
else if (distY < 0) 
{
    dir = -1;
}

I would like to know if there is an way to do it in one shoot mybey by using a mathematical method or a fashion old-school way.

Clarification, a similar example of what I'm looking for is:

i = ++i % max;

instead of:

i++;
if ( i > max ) { i = 0; }
nixeagle
  • 1,002
  • 8
  • 17
Cesar
  • 4,418
  • 2
  • 31
  • 37
  • 4
    Why? Your cyclical increment example is actually slower and (more importantly) less readable than the second. Why do you need a slower, less readable version of the first? Oh and `i = ++i % max` isn't technically identical to the second unless you also assume there is no other manipulation to `i`. – cletus Jul 05 '10 at 00:51
  • 1
    "i = ++i % max;" has undefined behaviour. So a compiler can make it faster than everything else if it wants. – Windows programmer Jul 05 '10 at 01:01
  • 2
    By the way, what does "genetic-algorithm" have to do with this question? – Windows programmer Jul 05 '10 at 01:02
  • @tag: bit-manipulation. @untag: best-practices @untag: genetic-algorithm – rwong Jul 05 '10 at 01:16
  • Nothing to do with "genetic-algorithm". I'd question "best-practices" as well. – Don Roby Jul 05 '10 at 01:18
  • 1
    Bit-manipulation is not a negative connotation. There are people working on bit-manipulation to bring us blazing fast video, etc. If it is tagged correctly you will get an answer much faster. – rwong Jul 05 '10 at 01:23
  • @cletus: I'm used to choose the compact versions because the code for me it's more readable to me if is not prolix. Thanks for the clarifications about the performances, I think I will change my way to do it. Probably the best way to do it in a line it's write a function. – Cesar Jul 05 '10 at 01:49
  • 1
    @Cesar: SO auto-suggests this as a very similar question, with very good answers: http://stackoverflow.com/questions/1610836/branchless-code-that-maps-zero-negative-and-positive-to-0-1-2 – rwong Jul 05 '10 at 04:33

8 Answers8

2

If you know the value will be nonzero, you could just divide by the absolute value:

dir = distY / abs(distY);

If it could possibly be zero, and you still want to set the flag to something, you could do something like this (in C/C++):

dir = distY >= 0 ? 1 : -1;

This will set dir to 1 when distY is zero as well.

Amber
  • 507,862
  • 82
  • 626
  • 550
  • 4
    Keep in mind that if this is an attempt at optimization, smaller source code does not necessarily mean better, faster, smaller, or more efficient compiled code. As an example, a call to `abs()` and a divide is most likely slower than a pair of conditional-move instructions, and trying to be "tricky" can often end up just confusing the optimizer and producing less-optimized compiled code. – Anon. Jul 05 '10 at 00:53
  • 2
    So the solution is a convoluate method that can either have a divide by zero problem or requires the ternary operator, which is really just another if statement? – cletus Jul 05 '10 at 00:53
  • @cletus - really it's more of a "here are the options", because in reality, an `if`/`else` combo is pretty much the simplest way of doing it, if wordier. – Amber Jul 05 '10 at 01:35
2

Assuming you're using something like C or C++ that will convert true to 1 and false to 0, you could use: direction = (distY > 0) - (distY < 0);. You didn't say what you wanted when distY=0 -- this gives 0 (which seems like the obvious choice to me, but who knows).

Of course, there's no guarantee it'll do any good -- that will depend on a combination of compiler, compiler flags, CPU, and maybe even the phase of the moon. OTOH, I'd guess it has more chance of doing good than harm anyway.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

direction = distY / abs(distY)

You will still need to check to make sure distY is not 0, though.

Dave Markle
  • 95,573
  • 20
  • 147
  • 170
  • "You will still need to check to make sure distY is not 0, though" -- no, the problem statement doesn't define any result when distY is 0 so a crash is perfectly acceptable :-) – Windows programmer Jul 05 '10 at 01:00
1

It seems you are looking for the signum function. If your programming language/libraries do not have it it's pretty easy to write: just wrap your if/else statements in a function so it will be easier and nicer to use.

With mathematical notations:

sign(n) =  
|-1 if n < 0  
| 0 if n = 0  
| 1 if n > 0

If it is slower or faster then bit manipulation depends on the language, the target platform and on the library (if you use one). Involving abs in any way (as recommended in some answers) is probably an overkill, as it will internally contain pretty much the same logic, and you have some one more call and a division + you have to deal with potential division by zero.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Sandor Murakozi
  • 4,372
  • 25
  • 27
0

In any language that supports basic bit operations (shifts) this works - even if the language doesn't explicitly use 0 and 1 for FALSE and TRUE.

C# sample:

    // Explanatory version:
    static int Sign(int val)
    {
        val = -(int)((uint)val >> 31); // int is 32 bit, so shift by n - 1.
        // uval now contains -1 for negative and 0 for positive.
        return val * 2 + 1;
        // -1 * 2 + 1 = -1
        //  0 * 2 + 1 = +1
    }

    // Terse form:
    static int Sign(int val)
    {
        return 1 - (int)((uint)val >> 31) * 2;
    }

This is because of how negative numbers are represented on little-endian two's complement hardware (like x86/x64). Basically the first bit in the number will be '1' for negative values (see the article for the example).

  1. First we cast it to an unsigned format. This is to eliminate any special treatment the language might have for shifting operations on signed formats (for example .Net).
  2. At this stage we might and done an AND operation with it and 0x80000000. This would have eliminated the bits unrelated to the sign from the value. We don't need to do this because of how a shift works (if your language only has ROL and ROR you will need to do this AND first).
  3. We shift the value right 31 bits. The shift operator will 'destroy' any bits that 'fall' off the end - so we are left with either a 1 or a 0 left at the bit 2^0. This means that if the value is negative the value will be 1, and if it is positive it will be 0.
  4. We cast it back to an int and then use simple math to map 0 and 1 to 1 and -1 respectively.

If you are targeting big-endian I think just changing the shift should yield the correct result.

Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60
0

If you insist on being terse, try

(i>0)?1:((i<0)?-1:0)

(assuming you want to cover the zero case in the most sensible way).

There may be hacks using shifts of the sign bit, but I doubt they'll be either elegant or efficient.

Personally, I'd use the if-else construct.

Don Roby
  • 40,677
  • 6
  • 91
  • 113
0

If you're using a language where 0 is false and 1 is true (or offers that type conversion), then in pseudo code:

i = abs(distY) == distY; // 0 or 1
i = i*2 - 1; // -1 or 1
Lèse majesté
  • 7,923
  • 2
  • 33
  • 44
0

You could probably tell me if there is something similar in C, but Perl has just the equality operator for the job.

The <=> operator would be used like so:

$dir = $distY <=> 0;

From the documentation (perldoc perlop):

Binary "<=>" returns -1, 0, or 1 depending on whether the left argument is numerically less than, equal to, or greater than the right argument.

Now what I'd like to know is if something similar exists in C, C++ and/or Objective-C.

Zaid
  • 36,680
  • 16
  • 86
  • 155