JavaScript 中的 Number.sign()

发布于 2024-12-07 18:31:27 字数 2685 浏览 0 评论 0原文

想知道是否有任何重要的方法可以找到数字的符号(符号函数)?
可能比显而易见的

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

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


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éric Hamidi - 不知怎的,得到最多支持的答案(在撰写本文时),这有点酷,但这绝对不是事情应该如何完成,恕我直言。此外,它不能正确处理无穷大数字,这也是数字,你知道。

  • kbec - 是对显而易见的解决方案的改进。虽然不是那么革命性,但综合考虑,我认为这种方法是最好的。投票给他:)

Wonder if there are any nontrivial ways of finding number's sign (signum function)?
May be shorter / faster / more elegant solutions than the obvious one

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

Short answer!

Use this and you'll be safe and fast (source: moz)

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

You may want to look at performance and type-coercing comparison fiddle

Long time has passed. Further is mainly for historical reasons.


Results

For now we have these solutions:


1. Obvious and fast

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

1.1. Modification from kbec - one type cast less, more performant, shorter [fastest]

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

caution: sign("0") -> 1


2. Elegant, short, not so fast [slowest]

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

caution: sign(+-Infinity) -> NaN, sign("0") -> NaN

As of Infinity is a legal number in JS this solution doesn't seem fully correct.


3. The art... but very slow [slowest]

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

4. Using bit-shift
fast, but sign(-Infinity) -> 0

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

5. Type-safe [megafast]

! Seems like browsers (especially chrome's v8) make some magic optimizations and this solution turns out to be much more performant than others, even than (1.1) despite it contains 2 extra operations and logically never can't be faster.

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

Tools

Improvements are welcome!


[Offtopic] Accepted answer

  • Andrey Tarantsov - +100 for the art, but sadly it is about 5 times slower than the obvious approach

  • Frédéric Hamidi - somehow the most upvoted answer (for the time writing) and it's kinda cool, but it's definitely not how things should be done, imho. Also it doesn't correctly handle Infinity numbers, which are also numbers, you know.

  • kbec - is an improvement of the obvious solution. Not that revolutionary, but taking all together I consider this approach the best. Vote for him :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

Bonjour°[大白 2024-12-14 18:31:27

更优雅的快速解决方案版本:

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

More elegant version of fast solution:

var sign = number?number<0?-1:1:0
野却迷人 2024-12-14 18:31:27

将数字除以其绝对值也可以得出其符号。使用短路逻辑 AND 运算符允许我们使用特殊情况 0,这样我们就不会最终除以它:

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

Dividing the number by its absolute value also gives its sign. Using the short-circuiting logical AND operator allows us to special-case 0 so we don't end up dividing by it:

var sign = number && number / Math.abs(number);
掀纱窥君容 2024-12-14 18:31:27

您正在寻找的函数称为 signum,实现它的最佳方法是:

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

The function you're looking for is called signum, and the best way to implement it is:

function sgn(x) {
  return (x > 0) - (x < 0);
}
与之呼应 2024-12-14 18:31:27

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

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

这使得它与 ECMAScript 的 Math.sign (MDN):

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

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

Should this not support JavaScript’s (ECMAScript’s) signed zeroes? It seems to work when returning x rather than 0 in the “megafast” function:

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

This makes it compatible with a draft of ECMAScript’s Math.sign (MDN):

Returns the sign of the x, indicating whether x is positive, negative or zero.

  • If x is NaN, the result is NaN.
  • If x is −0, the result is −0.
  • If x is +0, the result is +0.
  • If x is negative and not −0, the result is −1.
  • If x is positive and not +0, the result is +1.
瀞厅☆埖开 2024-12-14 18:31:27

对于对最新浏览器的情况感兴趣的人,在 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

For people who are interested what is going on with latest browsers, in ES6 version there is a native Math.sign method. You can check the support here.

Basically it returns -1, 1, 0 or 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
丘比特射中我 2024-12-14 18:31:27
var sign = number >> 31 | -number >>> 31;

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

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

Superfast if you do not need Infinity and know that the number is an integer, found in openjdk-7 source: java.lang.Integer.signum()

离线来电— 2024-12-14 18:31:27

我想我添加这个只是为了好玩:

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

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

I thought I'd add this just for fun:

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

0 and NaN will return -1
works fine on +/-Infinity

梦巷 2024-12-14 18:31:27

适用于所有数字、0-0、以及 Infinity-Infinity 的解决方案>,是:

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

参见问题“+0 和 -0 相同吗?”了解更多信息。


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

A solution that works on all numbers, as well as 0 and -0, as well as Infinity and -Infinity, is:

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

See the question "Are +0 and -0 the same?" for more information.


Warning: None of these answers, including the now standard Math.sign will work on the case 0 vs -0. This may not be an issue for you, but in certain physics implementations it may matter.

无风消散 2024-12-14 18:31:27

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

You could bit shift the number and check the Most Significant Bit (MSB). If the MSB is a 1 then the number is negative. If it is 0 then the number is positive (or 0).

小女人ら 2024-12-14 18:31:27

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

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

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

I just was about to ask the same question, but came to a solution before i was finished writing, saw this Question already existed, but didn't saw this solution.

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

it seems to be faster by adding a ternary though (n >> 31) + (n>0?1:0)

你好,陌生人 2024-12-14 18:31:27

与 Martijn 的答案非常相似的是

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

我发现它更具可读性。此外(或者,取决于您的观点),它还可以理解可以解释为数字的事物;例如,当出现 '-5' 时,它会返回 -1

Very similar to Martijn's answer is

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

I find it more readable. Also (or, depending on your point of view, however), it also groks things that can be interpreted as a number; e.g., it returns -1 when presented with '-5'.

﹏雨一样淡蓝的深情 2024-12-14 18:31:27

我没有看到从 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

I don't see any practical sence of returning -0 and 0 from Math.sign so my version is:

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
自找没趣 2024-12-14 18:31:27

我知道的方法如下:

Math.sign(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 [顺便说一句:非数字字符串,例如!!'abc',在本例中变为 0,而不是 NaN]),然后使用按位 OR 运算来组合这些位。

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

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

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

由于某种原因,在 IE 中最快。

jsPerf

执行的测试:https://jsperf.com/get-sign -来自值

The methods I know of are as follows:

Math.sign(n)

var s = Math.sign(n)

This is the native function, but is slowest of all because of the overhead of a function call. It does however handle 'NaN' where the others below may just assume 0 (i.e. Math.sign('abc') is NaN).

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

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

In this case only the left or right side can be a 1 based on the sign. This results in either 1-0 (1), 0-1 (-1), or 0-0 (0).

The speed of this one seems neck and neck with the next one below in Chrome.

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

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

Uses the "Sign-propagating right shift". Basically shifting by 31 drops all bits except the sign. If the sign was set, this results in -1, otherwise it is 0. Right of | it tests for positive by converting the value to boolean (0 or 1 [BTW: non-numeric strings, like !!'abc', become 0 in this case, and not NaN]) then uses a bitwise OR operation to combine the bits.

This seems the best average performance across the browsers (best in Chrome and Firefox at least), but not the fastest in ALL of them. For some reason, the ternary operator is faster in IE.

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

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

Fastest in IE for some reason.

jsPerf

Tests performed: https://jsperf.com/get-sign-from-value

停顿的约定 2024-12-14 18:31:27

我的两分钱,有一个返回与 Math.sign 相同结果的函数,即 sign(-0) --> -0, 符号(-无穷大) --> -无穷大,符号(null) --> 0, 符号(未定义) --> NaN 等。Jsperf

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

不允许我创建测试或修订,很抱歉无法为您提供测试(我已经尝试过 jsbench.github.io,但结果似乎比 Jsperf 更接近) ...)

如果有人可以将其添加到 Jsperf 修订版中,我很好奇它与之前给出的所有解决方案相比如何...

谢谢!

吉姆.

编辑

我应该写:(

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

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

My two cents, with a function that returns the same results as Math.sign would do, ie sign(-0) --> -0, sign(-Infinity) --> -Infinity, sign(null) --> 0, sign(undefined) --> NaN, etc.

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

Jsperf won't let me create a test or revision, sorry for not being able to provide you with tests (i've given jsbench.github.io a try, but results seem much closer to one another than with Jsperf...)

If someone could please add it to a Jsperf revision, I would be curious to see how it compares to all the previously given solutions...

Thank you!

Jim.

EDIT:

I should have written:

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

((+x && -1) instead of (x && -1)) in order to handle sign('abc') properly (--> NaN)

东走西顾 2024-12-14 18:31:27

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

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

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

Math.sign is not supported on IE 11. I am combining the best answer with Math.sign answer :

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

Now, one can use Math.sign directly.

远山浅 2024-12-14 18:31:27

这是一个紧凑版本:

let sign=x=>2*(x>=0)-1
//Tests
console.log(sign(0)); //1
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //-1

如果您想处理 NaN 和其他边缘情况,请使用这个(虽然它更长):

let sign=x=>isNaN(x)?NaN:2*(x>=0)-1
//Tests
console.log(sign(0)); //1
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //NaN

如果您希望 sign(0) 返回0 以及:

let sign=x=>isNaN(x)?NaN:(x?2*(x>=0)-1:0)
//Tests
console.log(sign(0)); //0
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //NaN

Here is a compact version:

let sign=x=>2*(x>=0)-1
//Tests
console.log(sign(0)); //1
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //-1

If you want to deal with NaN and other edge cases, use this (it is longer though):

let sign=x=>isNaN(x)?NaN:2*(x>=0)-1
//Tests
console.log(sign(0)); //1
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //NaN

If you want sign(0) to return 0 as well:

let sign=x=>isNaN(x)?NaN:(x?2*(x>=0)-1:0)
//Tests
console.log(sign(0)); //0
console.log(sign(6)); //1
console.log(sign(Infinity)); //1
console.log(sign(-6)); //-1
console.log(sign(-Infinity)); //-1
console.log(sign("foo")); //NaN
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文