JavaScript中的Number.sign()


101

想知道是否有任何简单的方法来查找数字的符号(signum函数)?
可能比显而易见的解决方案更短/更快/更优雅的解决方案

var sign = number > 0 ? 1 : number < 0 ? -1 : 0;

简短的答案!

使用此工具,您将安全快捷(来源:moz

if (!Math.sign) Math.sign = function(x) { return ((x > 0) - (x < 0)) || +x; };

您可能想看看性能和类型强制比较小提琴

很久过去了。进一步主要是由于历史原因。


结果

目前,我们有以下解决方案:


1.明显且快速

function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; }

1.1。kbec的修改-一种类型投放较少,性能更高,时间更短[最快]

function sign(x) { return x ? x < 0 ? -1 : 1 : 0; }

警告: sign("0") -> 1


2.优雅,简短,不那么快[最慢]

function sign(x) { return x && x / Math.abs(x); }

提醒: sign(+-Infinity) -> NaNsign("0") -> NaN

截至InfinityJS中的合法数字为止,此解决方案似乎并不完全正确。


3.艺术...但是很慢[最慢]

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

4.使用移位
快,但是sign(-Infinity) -> 0

function sign(x) { return (x >> 31) + (x > 0 ? 1 : 0); }

5.类型安全[megafast]

似乎浏览器(尤其是chrome的v8)进行了一些魔术般的优化,并且该解决方案比(1.1)更具性能,尽管它包含2个额外的操作,并且从逻辑上讲永远不会更快。

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN;
}

工具类

欢迎改进!


[Offtopic]已接受的答案

  • 安德烈·塔兰佐夫Andrey Tarantsov) -+100,但遗憾的是,它比明显的方法慢了大约5倍

  • 弗雷德里克·哈米迪FrédéricHamidi) -某种程度上最受欢迎的答案(在撰写本文时),这有点酷,但是,恕我直言,这绝对不是应该如何做的事情。同样,它不能正确处理无穷大数字,这也是数字。

  • kbec-是明显解决方案的改进。并不是那么具有革命性,但是综合起来我认为这种方法是最好的。为他投票:)


3
问题是,有时这0是一种特殊情况
2011年

1
我已经进行了一组JSPerf测试(使用不同类型的输入)来测试每种算法,可以在这里找到:jsperf.com/signs 结果可能与本文中列出的不一样!
阿尔巴·门德斯

2
@disfated,其中哪一个?当然,如果您运行test everything版本,Safe将拒绝测试特殊值,因此速度会更快!尝试运行only integers测试。而且,JSPerf只是在做他的工作,不是喜欢它的问题。:)
阿尔巴·门德斯

2
根据jsperf的测试,事实证明,这typeof x === "number"为性能提供了一些魔术。请进行更多运行,尤其是FF,Opera和IE,以使其更加清晰。
2013年

4
为了完整起见,我添加了一个新的测试jsperf.com/signs/7(0Math.sign() === 0,速度不如“安全”),它出现在FF25中,即将在chrome中出现。
Alex K. 2014年

Answers:



28

用数字除以其绝对值也会得到其符号。使用短路逻辑AND运算符可以使我们有特殊情况,0因此我们最终不会除以它:

var sign = number && number / Math.abs(number);

6
您可能需要var sign = number && number / Math.abs(number);以防万一number = 0
NullUserException 2011年

@NullUserException,您是完全正确的,0需要特殊设置。答案已相应更新。谢谢:)
弗雷德里克·哈米迪

你是目前最好的。但我希望将来会有更多答案。
2011年

24

您要查找的函数称为signum,实现它的最佳方法是:

function sgn(x) {
  return (x > 0) - (x < 0);
}

3
等待。有一个错误:for(x = -2; x <= 2; x ++)console.log((x> 1)-(x <1)); 给出[x,-2; x <= 2; x ++)console.log((x> 0)-(x <0))的[-1,-1,-1,0,1]; 给出正确的[-1,
-1,0,1,1

13

这应该不支持JavaScript的(ECMAScript)签名零吗?在“ megafast”函数中返回x而不是0时,它似乎可以工作:

function sign(x) {
    return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? x : NaN : NaN;
}

这使其与ECMAScript的Math.signMDN)草案兼容:

返回x的符号,指示x是正,负还是零。

  • 如果x为NaN,则结果为NaN。
  • 如果x为-0,则结果为-0。
  • 如果x为+0,则结果为+0。
  • 如果x为负而不是-0,则结果为-1。
  • 如果x为正而不是+0,则结果为+1。

令我印象深刻的是,这种机制非常快且有趣。等待更多测试。
kbec 2014年

10

对于有兴趣使用最新浏览器的用户,在ES6版本中提供了本机Math.sign方法。您可以在此处查看支持

基本上它返回-110或者NaN

Math.sign(3);     //  1
Math.sign(-3);    // -1
Math.sign('-3');  // -1
Math.sign(0);     //  0
Math.sign(-0);    // -0
Math.sign(NaN);   // NaN
Math.sign('foo'); // NaN
Math.sign();      // NaN

4
var sign = number >> 31 | -number >>> 31;

如果您不需要Infinity并且知道数字是整数,则是超快速的,可在openjdk-7源代码中找到: java.lang.Integer.signum()


1
对于小负分数(如-0.5),此操作将失败。(看起来来源是专门针对Integers的实现的)
2015年

1

我以为只是为了好玩而添加:

function sgn(x){
  return 2*(x>0)-1;
}

0并且NaN将返回-1
在+/- Infinity上正常工作


1

一个解决方案,对所有的数字作品,以及0-0,以及Infinity-Infinity是:

function sign( number ) {
    return 1 / number > 0 ? 1 : -1;
}

有关更多信息,请参见问题“ +0和-0是否相同? ”。


警告:这些问题的答案,包括现在的标准中没有Math.sign的情况下将工作0VS -0。对于您来说,这可能不是问题,但是在某些物理实现中,这可能很重要。


0

您可以将数字移位并检查最高有效位(MSB)。如果MSB为1,则数字为负。如果为0,则数字为正(或0)。


@ NullUserException我仍然可能是错误的,但从我的阅读中可以看出:“所有按位运算符的操作数都按big-endian顺序和二进制补码格式转换为带符号的32位整数。” 取自MDN
Brombomb

这似乎仍然是一项艰巨的工作。您仍然必须将1和0转换为-1和1,并且还必须注意0。如果OP只是希望这样做,则使用起来会更容易var sign = number < 0 : 1 : 0
NullUserException

+1。不过,无需移动,您可以n & 0x80000000像位掩码一样操作。至于转换为0,1,-1:n && (n & 0x80000000 ? -1 : 1)
davin 2011年

@davin是否确保所有数字都可与该位掩码一起使用?我接通电源-5e32,它坏了。
NullUserException '10

@NullUserExceptionఠ_ఠ,应用标准时具有相同符号的数字ToInt32。如果在此处阅读(第9.5节),则模数会影响数字的值,因为32位整数的范围小于js Number类型的范围。因此,对于这些值或无限性将不起作用。我仍然喜欢答案。
2011年

0

我刚要问同样的问题,但是在完成写作之前就找到了一个解决方案,看到这个问题已经存在,但是没有看到这个解决方案。

(n >> 31) + (n > 0)

通过添加三元似乎更快 (n >> 31) + (n>0?1:0)


非常好。您的代码似乎比(1)快很多。(n> 0?1:0)更快,因为没有类型转换。唯一令人失望的时刻是sign(-Infinity)给出0。更新了测试。
2013年

0

与Martijn的答案非常相似的是

function sgn(x) {
    isNaN(x) ? NaN : (x === 0 ? x : (x < 0 ? -1 : 1));
}

我觉得它更具可读性。同样(或者,根据您的观点),它也增加了一些可以解释为数字的事物。例如,-1当出现时返回'-5'


0

我看不到从-0和0返回的实际意义,Math.sign所以我的版本是:

function sign(x) {
    x = Number(x);
    if (isNaN(x)) {
        return NaN;
    }
    if (x === -Infinity || 1 / x < 0) {
        return -1;
    }
    return 1;
};

sign(100);   //  1
sign(-100);  // -1
sign(0);     //  1
sign(-0);    // -1

这不是
签名

0

我知道的方法如下:

数学符号(n)

var s = Math.sign(n)

这是本机函数,但由于函数调用的开销而最慢。但是,它确实会处理“ NaN”,而以下其他参数可能只是假设为0(即Math.sign('abc')为NaN)。

((n> 0)-(n <0))

var s = ((n>0) - (n<0));

在这种情况下,基于符号,只有左侧或右侧可以是1。结果是1-0(1),0-1(-1)或0-0(0)。

这个速度似乎与Chrome中的下一个并驾齐驱。

(n >> 31)|(!! n)

var s = (n>>31)|(!!n);

使用“符号传播右移”。基本上移位31将除去除符号外的所有位。如果设置了符号,则结果为-1,否则为0。它的右边|通过将值转换为布尔值(0或1 [BTW:非数字字符串,如!!'abc',在这种情况下变为0,并且然后用位或运算来组合这些位。

这似乎是所有浏览器的最佳平均性能(至少在Chrome和Firefox中最佳),但并不是所有浏览器中最快的。由于某些原因,IE中的三元运算符更快。

n?n <0?-1:1:0

var s = n?n<0?-1:1:0;

出于某些原因,在IE中最快。

jsPerf

执行的测试:https//jsperf.com/get-sign-from-value


0

我的两分钱,带有一个返回与Math.sign相同结果的函数,即sign(-0)-> -0,sign(-Infinity)-> -Infinity,sign(null)-> 0 ,sign(undefined)-> NaN等。

function sign(x) {
    return +(x > -x) || (x && -1) || +x;
}

Jsperf不会让我创建测试或修订版,对不起无法为您提供测试(我已经尝试了jsbench.github.io,但结果似乎比使用Jsperf彼此更接近...)

如果有人可以将其添加到Jsperf修订版中,我很想知道它如何与以前提供的所有解决方案进行比较...

谢谢!

吉姆

编辑

我应该写:

function sign(x) {
    return +(x > -x) || (+x && -1) || +x;
}

(+x && -1)而不是(x && -1))以便sign('abc')正确处理(-> NaN)


0

IE 11不支持Math.sign。我将最佳答案与Math.sign答案结合在一起:

Math.sign = Math.sign || function(number){
    var sign = number ? ( (number <0) ? -1 : 1) : 0;
    return sign;
};

现在,可以直接使用Math.sign。


1
您逼我更新问题。被问到已经过去了8年。还将我的jsfiddle更新为es6和window.performance api。但是我更喜欢mozilla的版本作为polyfill,因为它与Math.sign的类型强制匹配。如今,性能已不是什么大问题。
贬值
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.