hakre
hakre

Reputation: 197775

How to get sign of a number?

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

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";
}

(Example code might run into floating point precision problems probably when number is not a number or infinity etc.)

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

Upvotes: 42

Views: 38208

Answers (9)

Rolf
Rolf

Reputation: 945

To compare the proposals made here:

  • 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.

Upvotes: 4

kdojeteri
kdojeteri

Reputation: 805

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

$sign = $i <=> 0;

This is also known as "spaceship operator".

Upvotes: 49

sansig
sansig

Reputation: 81

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.

Upvotes: 4

Milosz
Milosz

Reputation: 3074

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

function sign($n) {
    return ($n > 0) - ($n < 0);
}

Upvotes: 74

hakre
hakre

Reputation: 197775

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))

Upvotes: 17

Toto
Toto

Reputation: 91430

Use strcmpDocs:

echo $number, ': ', strcmp($number, 0), "\n";

Upvotes: 1

zzzzBov
zzzzBov

Reputation: 179066

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 );

Upvotes: 6

Yeroon
Yeroon

Reputation: 3243

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');

Upvotes: 1

rocksportrocker
rocksportrocker

Reputation: 7419

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.

Upvotes: 10

Related Questions