如何获取一个数的符号?

41

有没有一种(简单的)方法可以在PHP中获取一个数字(整数)的“符号”,类似于gmp_signDocs

  • -1代表负数
  • 0代表零
  • 1代表正数

我记得有一些比较函数可以做到这一点,但我现在找不到它。

我快速编译了这个 (演示),这可以完成工作,但也许有更巧妙的方法(像一个函数调用?),我想将结果映射到数组上:

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

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

(这段代码可能会遇到浮点精度问题)

相关问题: 请求 #19621 数学需要一个“sign()”函数


@Tomalak Geret'kal:有时候你需要反馈,对吧? ;) - hakre
1
@Orbling:好问题,也许是因为它已经被安装了?让我试试 :) - hakre
9个回答

72

这里有一个酷炫的一行代码,可以高效可靠地为您完成操作:

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

11
不会对 $n 执行任何算术运算,因此不会遇到整数溢出问题或浮点精度问题。此外,IEEE 浮点数确实遵循排中律 (¬(A > B) ⇒ A ≤ B, ¬(A < B) ⇒ A ≥ B),因此不会得到一个同时满足两个条件并产生错误符号 0 的非零数字。最后,根据 IEEE 规范,-00 都被视为等于零,因此在两个条件下都将返回 false,产生符号 0。它将适用于所有数字输入。将 NAN 输入函数将产生 1 - 1 = 0 的结果,这是我想到的最好的答案。 - Milosz

46

1
这很好,我喜欢它。它是太空船操作符。但它将破坏那些尚未升级到PHP 7.0的服务器。可悲的是,这样的服务器仍然存在。 - asiby
2
据我所知,在 PHP 中 <=> 运算符的结果不能保证是 +10-1,而可以是任何正整数代替 +1 和任何负整数代替 -1(请参见评论区此处的讨论)。因此,TS 可能需要一些处理 <=> 运算符结果的函数。 - Sasha
它是“太空船运算符”。 - undefined

18

我在我的问题中尝试了上述方法的一个变体,它同样有效,并且没有浮点数问题:

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

编辑:上面的代码在处理浮点数(问题是关于整数的)时存在缺陷,特别是当它们在范围大于-1且小于1时,可以使用以下代码进行修复:


```python def is_int(num): return int(num) == num ```
min(1, max(-1, $number == 0 ? 0 : $number * INF))

该方法还存在一个缺陷,当传入浮点数 NAN 时,它会始终返回 -1。这可能是不正确的。相反,可能还希望返回 0

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

2
这对于整数是可以的,但如果有人将此解决方案粘贴到浮点数中,他将遇到麻烦。 - rocksportrocker
@rocksportrocker:特别是针对NAN和INF值。而且对于整数也存在溢出问题。 - hakre
不适用于0.3(或所有从-11的数字) - Yukulélé
@hakre:在这种情况下,从-1到1的所有数字都返回0,而不是它们的符号。 - Yukulélé
@Yukulélé:已编辑帖子。希望这更有帮助。请记住,问题要求整数,而不是浮点数。 - hakre

9

您可以嵌套三元运算符:

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

这段代码不存在浮点精度问题,并且避免了浮点数除法。



6
这个表单有什么问题?
if ( $num < 0 )
{
  //negative
}
else if ( $num == 0 )
{
  //zero
}
else
{
  //positive
}

或三元:

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

我不确定 abs 和值比较的性能,但你可以使用以下方法:

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

你可以将它们中的任何一个转换成一个函数:

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

我想你可能在谈论gmp_cmp,你可以这样调用它:gmp_cmp($num, 0);


该表达式应表示一个值为:(-1, 0, 1) - hakre
@hakre,我不确定你的意思是什么。 - zzzzBov
@hakre,忘记那个部分了,添加了一个零检查。 - zzzzBov

4
我知道现在已经有点晚了,但是对于这个问题,可以尝试将这个数字除以它自身的绝对值。例如:
```python result = number / abs(number) ```
这样做可以避免出现负数的情况。
function sign($n) {
    return $n/(abs($n));
}

针对除零的错误,可以添加任何您想要的错误处理方式。


1
好的观点,可能有些粗糙(并不是说其他答案都完整,我记得我的旧答案也有一些粗糙之处),这不仅涉及到除以零,还需要处理除法运算符中的INF。 - hakre

4

我认为gmp_sign不太高效,因为它需要一个GMP或字符串。 ($n ? abs($n)/$n : 0) 在数学上是正确的,但除法需要时间。 对于浮点数,min/max解决方案似乎变得不必要复杂。

($n > 0) - ($n < 0) 总共执行两次测试和一次减法。 ($n < 0 ? -1 : ($n > 0 ? 1 : 0) 只执行一次或两次测试,没有算术操作,这应该是最有效的。 但是我不相信这种差异对大多数用例都很重要。


2
使用 strcmp文档:
echo $number, ': ', strcmp($number, 0), "\n";

这对数字(作为字符串)有效吗?我想这就是我想的函数,但我不确定它是否真的能够胜任。 - hakre
1
这很优雅,但不是非常高效。将整数转换为字符串需要比与0的比较花费更长的时间。 - rocksportrocker
1
我已经测试了几个小时。如果$number实际上是一个字符串(并且表示为零,例如“n/a”),这将不起作用(min(max)在这里有效)。只是注意,这是一个特殊情况,仅供参考。它通常工作得非常好,但对于表示PHP中我们所知道的数字值0的字符串变量,则适用。@rocksportrocker:在PHP中没有真正的类型,如字符串或整数,因此在我的眼中,转换参数似乎是虚假的。无论如何,这将是微观优化,以便密切关注它。 - hakre
@hakre:感谢提醒。在比较字符串(!=“”)和数字时是正确的。 - Toto
strcmp("110","12") 表明 12110 大(此函数逐个字符比较字符串)。 - Yukulélé
显示剩余2条评论

1

这是一个没有循环的例子:

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

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

array_walk($numbers, 'sign');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接