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からの変更-1つのタイプのキャストが少なく、パフォーマンスが高く、短い[最速]

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

ツール

改善は大歓迎です!


[オフトピック]受け入れられた回答

  • Andrey Tarantsov-アートの+100ですが、残念なことに、明らかなアプローチよりも約5倍遅いです

  • フレデリック・ハミディ -どういうわけか(執筆期間中)最も支持された答えであり、それはちょっとクールですが、物事をどのように行うべきかは間違いなく、私見です。また、それは数値でもある無限数を正しく処理しません。

  • kbec-明白なソリューションの改良版です。それほど革命的ではありませんが、すべてをまとめると、このアプローチが最良だと思います。彼に投票する:)


3
重要なのは、時々0特別なケースであるということです
11:15

1
すべてのアルゴリズムをテストするために(さまざまな種類の入力を使用して)JSPerfテストのセットを作成しました。これは次の場所にあります:jsperf.com/signs 結果はこの投稿にリストされていない可能性があります!
アルバメンデス2013年

2
@disfated、それらのどれですか?もちろん、test everythingバージョンを実行すると、Safeは特別な値のテストを拒否するため、より高速になります!only integers代わりにテストを実行してみてください。また、JSperfは彼の仕事をしているだけで、好きなことではありません。:)
アルバメンデス2013年

2
jsperfテストによると、performace typeof x === "number"にいくつかの魔法をかけることが判明しました。より明確にするために、特にFF、Opera、IEを実行してください。
disfated

4
完全を期すためにMath.sign()、FF25に登場し、Chromeで予定されている新しいテストjsperf.com/signs/7(0 === 0、「Safe」ほど速くない)を追加しました。
Alex K. 14年

回答:



28

数値を絶対値で割ると、符号も得られます。ショートサーキットの論理AND演算子を使用すると、特別な場合に使用できる0ため、最終的に除算されなくなります。

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

6
おそらく必要var sign = number && number / Math.abs(number);になるでしょうnumber = 0
NullUserException

@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)); (-1、-1、-1、0、1]を(x = -2; x <= 2; x ++)console.log((x> 0)-(x <0));に与える; 正しい[-1、
-1、0、1、1

13

これはJavaScript(ECMAScript)の符号付きゼロをサポートしないのですか?「megafast」関数で0ではなくxを返すときに機能するようです。

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の実装からのものであるようです)
15年

1

私はこれを単に楽しみのために追加すると思いました:

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


+/- Infinityでは0とNaNは-1を返します


1

すべての数字上の作品は、だけでなく、そのソリューション0-0、同様にInfinityして-Infinity、次のとおりです。

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

詳細については、「+ 0と-0は同じですか?」という質問を参照してください。


警告:今標準を含むこれらの答えのいずれも、Math.signケースの意志の仕事0-0。これはあなたにとって問題ではないかもしれませんが、特定の物理的実装ではそれが問題になるかもしれません。


0

数値をビットシフトして、最上位ビット(MSB)を確認できます。MSBが1の場合、数値は負です。0の場合、数値は正(または0)です。


@ NullUserException私はまだ間違っている可能性がありますが、私の読書から「すべてのビット演算子のオペランドはビッグエンディアン順で2の補数形式の符号付き32ビット整数に変換されます。」MDN
Brombomb、2011年

それはまだ大変な作業のようです。それでも、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

@davinすべての数値はそのビットマスクで動作することが保証されていますか?差し込んだら-5e32壊れた。
NullUserException 2010年

@NullUserExceptionఠ_ఠ、標準を適用したときに同じ符号を保持する数値ToInt32。そこを読むと(セクション9.5)、32ビット整数の範囲はjs数値型の範囲よりも小さいため、数値の値に影響を与える係数があります。したがって、これらの値、または無限大に対しては機能しません。私はまだ答えが好きです。
デーヴィン

0

私はちょうど同じ質問をしようとしていましたが、書き終える前に解決策に行きました、この質問はすでに存在しているのを見ましたが、この解決策を見ていませんでした。

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

三項を追加することでより高速になるようですが (n >> 31) + (n>0?1:0)


非常に素晴らしい。あなたのコードは(1)よりかなり速いようです。(n> 0?1:0)は、型キャストがないため高速です。唯一の残念な瞬間は、sign(-Infinity)が0を与えることです。更新されたテスト。
2013

0

マルティンの答えと非常に似ています

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

これはシグナム関数ではありません
2016

0

私が知っている方法は次のとおりです。

Math.sign(n)

var s = Math.sign(n)

これはネイティブ関数ですが、関数呼び出しのオーバーヘッドのため、すべての中で最も遅いです。ただし、以下のものは単に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になり、 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-from-value


0

Math.signと同じ結果を返す関数を使用した私の2セント、つまり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

Math.signはIE 11ではサポートされていません。私は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に更新しました。ただし、Math.signの型強制と一致するため、ポリフィルとしてmozillaのバージョンを好みます。最近のパフォーマンスはそれほど問題ではありません。
disfated
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.