JavaScript中的Number.sign()

108

想知道有没有任何非平凡的方法来找到数字的符号 (signum函数)?
可能有比显而易见的方法更短、更快、更优雅的解决方案。

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

简短回答!

使用这个方法,您就能安全且快速地操作(来源:moz)。

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

您可能需要查看性能和类型转换比较的fiddle

很长一段时间已经过去了。下面的内容主要是出于历史原因。


结果

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


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) -> NaN, sign("0") -> NaN

由于在JS中Infinity是一个合法的数字,因此这个解决方案似乎并不完全正确。


3. 这种方法... 但非常缓慢[最慢]

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

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

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

5. 安全类型的 [超级快速]

看起来浏览器(尤其是 Chrome 的 V8 引擎)会进行一些神奇的优化,这种解决方案比其他方案表现更好,甚至比 (1.1) 还要快,尽管它包含了两个额外的操作并且从逻辑上讲不可能更快。

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

工具

欢迎提出改进建议!


[离题] 接受的答案

  • Andrey Tarantsov - 艺术加分100,但不幸的是比明显的方法慢了大约5倍

  • Frédéric Hamidi - 目前为止最被赞同的答案,有点酷,但我认为这绝对不是正确的做法。另外,它不能正确处理无穷大的数字,你知道的,这也是数字。

  • kbec - 是对明显解决方案的改进。不算革命性,但综合考虑,我认为这种方法是最好的。请投他一票 :)


3
有时候,“0”是一个特殊情况。 - disfated
2
@disfated,哪一个?当然,如果你运行“测试一切”版本,Safe会拒绝测试特殊值,所以速度会更快!尝试运行only integers测试。此外,JSPerf只是在执行它的工作,这不是喜欢或不喜欢的问题。 :) - Alba Mendez
1
哇,我的方法很慢!感谢测试;我从来没有想到过。有趣的是,在 ACM ICPC 的早期,我们一直在使用 (x>0)-(x<0) 这种方式,因为它是 _最快的解决方案_(内联汇编被规则禁止),而且显然是正确的。 - Andrey Tarantsov
2
根据jsperf测试结果显示,typeof x === "number" 对性能有一些神奇的影响。请多运行几次,特别是在FF、Opera和IE上进行测试以明确结果。 - disfated
4
为了完整性,我添加了一个新的测试 http://jsperf.com/signs/7 用于 Math.sign() (0===0,不像“安全模式”那样快),该方法出现在 Firefox25 中并将在 Chrome 中推出。 - Alex K.
显示剩余13条评论
16个回答

82

更优雅的快速解决方案:

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

7
给你的三元运算符混在一起打-1分。 将该代码转化为:如果number存在,则判断number是否小于0,是则返回-1,否则返回1;如果number不存在,则返回0。 - Patrick Michaelsen
3
Math.sign(number) - Илья Зеленько

29

将一个数除以它的绝对值也能得到它的符号。使用短路逻辑 AND 运算符可以使我们特殊处理0,这样我们就不会除以它:

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

6
如果number = 0,您可能想要使用var sign = number && number / Math.abs(number); - NullUserException
@NullUserException,你说得完全正确,0需要特殊处理。回答已相应更新。谢谢 :) - Frédéric Hamidi
你现在是最好的。但我希望未来会有更多的答案。 - disfated

25

你要找的函数叫做符号函数,实现最好的方法是:

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

3
等一下,有错误: 当代码为 for (x = -2; x <= 2; x++) console.log((x > 1) - (x < 1)); 时, 输出应该是 [-1, -1, -1, 0, 1]; 当代码为 for (x = -2; x <= 2; x++) console.log((x > 0) - (x < 0)); 时,输出应该是正确的 [-1, -1, 0, 1, 1]。 - disfated

16

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

这个问题是关于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

12

对于那些对最新的浏览器有兴趣的人来说,在ES6版本中有一个本地的Math.sign方法。你可以在这里查看支持情况

基本上它返回-110NaN

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;

如果你不需要无限制的数字,并且知道这个数字是整数,那么在openjdk-7源代码中可以找到一个非常快速的方法:java.lang.Integer.signum()


1
这在小的负分数(如-0.5)上失败了。(看起来源代码是针对整数实现的) - starwed

1

一个适用于所有数字,包括0-0,以及Infinity-Infinity的解决方案是:

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

查看问题“ +0 和 -0 是否相同?”以获取更多信息。


警告:这些答案中包括现在标准的Math.sign都无法处理0-0的情况。这可能对您来说不是问题,但在某些物理实现中可能很重要。


1

我觉得我加这个只是为了好玩:

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

0 和 NaN 将返回 -1
在 +/-Infinity 上运行正常


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

为了正确处理 sign('abc')(--> NaN),应该使用 (+x && -1) 而不是 (x && -1)


0

我不认为从Math.sign返回-0和0有任何实际意义,所以我的版本是:

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

这不是一个符号函数。 - disfated

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