在 javascript 中的 Number.sign()

想知道是否有找到数字符号(信号函数信号函数)的重要方法?
可能比显而易见的解决方案更短、更快、更优雅

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

简短回答!

使用这个,你将是安全和快速的(来源: Moz)

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

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

时过境迁,主要是历史原因。


结果

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


明显而迅速

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

1.1. Kbec改进-一型铸造少,性能更好,更短 [最快]

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

警告:


优雅,短小,不要那么快

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

警告: sign(+-Infinity) -> NaNsign("0") -> NaN

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


艺术... ... 但是非常缓慢

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

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

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

5. 类型安全 < strong > [ Megafast ]

!看起来浏览器(特别是 chrome 的 v8)做了一些神奇的优化,而且这个解决方案比其他方案性能更好,甚至比(1.1)更好,尽管它包含两个额外的操作,并且逻辑上不可能更快。

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

工具

欢迎改进!


接受回答

39867 次浏览

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

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).

More elegant version of fast solution:

var sign = number?number<0?-1:1: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);
}

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)

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.
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()

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'.

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

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

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.

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

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

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)

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.

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