Reputation: 197775
Is there a (simple) way to get the "sign" of a number (integer) in PHP comparable to gmp_sign
Docs:
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
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
Reputation: 805
In PHP 7+ you can use the combined comparison operator (<=>
):
$sign = $i <=> 0;
This is also known as "spaceship operator".
Upvotes: 49
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
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
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
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
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
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