9Answers

Is there a (simple) way to get the "sign" of a number (integer) in PHP comparable to gmp_signDocs:

  • -1 negative
  • 0 zero
  • 1 positive

I remember there is some sort of compare function that can do this but I'm not able to find it at the moment.

I quickly compiled this (Demo) which does the job, but maybe there is something more nifty (like a single function call?), I would like to map the result onto an array:

$numbers = array(-100, 0, 100);

foreach($numbers as $number)
{
   echo $number, ': ', $number ? abs($number) / $number : 0, "\n";
}

(this code might run into floating point precision problems probably)

Related: Request #19621 Math needs a "sign()" function

Answer

Here's a cool one-liner that will do it for you efficiently and reliably:

function sign($n) {
    return ($n > 0) - ($n < 0);
}
  • 57
Reply Report
      • 1
    • Well, you won't run into integer overflow issues or float precision issues since no arithmetic is performed on $n. Moreover, IEEE floats do follow the law of excluded middle (¬(A > B) ? A ? B, ¬(A < B) ? A ? B), so you won't get a nonzero number satisfying both conditions and yielding an incorrect sign of 0. Finally, both -0 and 0 are treated as equal to zero by IEEE specs, so will both return false under both conditions, yielding a sign of 0. It's going to work for all numeric inputs. Feeding NAN into the function yields 1 - 1 = 0 which is as good an answer as any I suppose.

In PHP 7 you should use the combined comparison operator (<=>):

$sign = $i <=> 0;
  • 35
Reply Report
    • This is good, I love it. The spaceship operator. But it will break servers that have not yet been upgraded to PHP 7.0. Sadly those still exist.
    • AFAIK, in PHP result of <=> isn't guaranteed to be +1, 0 or -1, it can be any positive integer instead of +1 and any negative integer instead if -1 (see discussion in comments here). So, the TS may need some function to process the result of <=>.

A variant to the above in my question I tested and which works as well and has not the floating point problem:

min(1, max(-1, $number))

Edit: The code above has a flaw for float numbers (question was about integer numbers) in the range greater than -1 and smaller than 1 which can be fixed with the following shorty:

min(1, max(-1, $number == 0 ? 0 : $number * INF))

That one still has a flaw for the float NAN making it return -1 always. That might not be correct. Instead one might want to return 0 as well:

min(1, max(-1, (is_nan($number) or $number == 0) ? 0 : $number * INF))
  • 17
Reply Report

You can nest ternary operators:

echo $number, ': ',  ($number >= 0 ? ($number == 0 ? 0 : 1) : -1 )

This has no problem with floating point precision and avoids an floating point division.

  • 9
Reply Report

What's wrong with this form?

if ( $num < 0 )
{
  //negative
}
else if ( $num == 0 )
{
  //zero
}
else
{
  //positive
}

or ternary:

$sign = $num < 0 ? -1 : ( $num > 0 ? 1 : 0 );

Not sure of the performance of abs vs value comparison, but you could use:

$sign = $num ? $num / abs($num) : 0;

and you could turn any of them into a function:

function valueSign($num)
{
  return $sign = $num < 0 ? -1 : ( $num > 0 ? 1 : 0 );
  //or
  return $sign = $num ? $num / abs($num) : 0;
}

I suppose you could be talking about gmp_cmp, which you could call as gmp_cmp( $num, 0 );

  • 6
Reply Report

I think that gmp_sign is not very efficient because it expects a GMP or string. ($n ? abs($n)/$n : 0) is mathematically correct, but the division costs time. The min/max solutions seem to get unnecessary complex for floats.

($n > 0) - ($n < 0) always does 2 tests and one subtraction ($n < 0 ? -1 : ($n > 0 ? 1 : 0) does one or two tests and no arithmetic, it should be most efficient. But I don't believe that the difference will be relevant for most use cases.

  • 4
Reply Report

I know this is late but what about simply dividing the number by the abs() of itself?

Something like:

function sign($n) {
    return $n/(abs($n));
}

Put whatever error handling you want for div by zero.

  • 3
Reply Report
      • 2
    • good point, perhaps a bit rough at the edges (not saying other answers are complete, my old one also has rough edges IIRC), it's not only division by zero but also INF which needs handling for the division operator.

Use strcmpDocs:

echo $number, ': ', strcmp($number, 0), "\n";
  • 2
Reply Report
      • 1
    • this is elegant, but not very efficient. Converting an int to a string needs much longer than comparisons to 0.
    • I now have tested this for some hours. If the $number actually is a string (and representing zero, like "n/a"), this won't work (min(max) works here). Just noting, it's a side-case, just leaving this for the note. It generally worked pretty well, but not for string variables representing the numerical value 0 as we know it in PHP. @rocksportrocker: There are not really actual types like string or intever in PHP, so the conversion argument seems bogus in my eyes. Would be micro-optimizing for nothing anyway to that closely watch at it.
    • Does this work for numbers (as strings)? I think that's the function I was thinking about, but I'm unsure if it's really doing the job.
    • Cool! The docs is a little unprecise if it's always -1, 0 or 1, however, I will try it in the code. Thanks!

Here's one without loop:

function sign($number){
    echo $number, ': ', $number ? abs($number) / $number : 0, "\n";
}

$numbers = array(-100, 0, 100);

array_walk($numbers, 'sign');
  • 1
Reply Report

Warm tip !!!

This article is reproduced from Stack Exchange / Stack Overflow, please click

Trending Tags

Related Questions