开发者

From a given number, set a var to -1 if negative and to 1 if positive (without using if)

开发者 https://www.devze.com 2023-01-06 04:17 出处:网络
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, 开发者_StackOverflowlike this:

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, 开发者_StackOverflowlike 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; }


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.


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.


direction = distY / abs(distY)

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


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.


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.


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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消