Number.sign() in javascript

扶醉桌前 提交于 2019-11-26 11:58:01

问题


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

  • jsperf preformance tests;
  • fiddle - type-cast tests;

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


回答1:


More elegant version of fast solution:

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



回答2:


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



回答3:


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



回答4:


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.



回答5:


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



回答6:


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




回答7:


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




回答8:


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.




回答9:


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




回答10:


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)




回答11:


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




回答12:


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



回答13:


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




回答14:


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)




回答15:


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.



来源:https://stackoverflow.com/questions/7624920/number-sign-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!