Number.sign () in Javascript


101

Frage mich, ob es nicht triviale Möglichkeiten gibt, das Vorzeichen einer Zahl zu finden ( Signum-Funktion )?
Kann kürzer / schneller / eleganter sein als die offensichtliche

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

Kurze Antwort!

Verwenden Sie dies und Sie werden sicher und schnell sein (Quelle: moz )

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

Sie können an Leistung und Typ-Nötigung Vergleich aussehen wollen Geige

Lange Zeit ist vergangen. Weiter ist vor allem aus historischen Gründen.


Ergebnisse

Im Moment haben wir diese Lösungen:


1. Offensichtlich und schnell

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

1.1. Modifikation von kbec - ein Typ weniger, leistungsfähiger, kürzer [am schnellsten]

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

Vorsicht: sign("0") -> 1


2. Elegant, kurz, nicht so schnell [am langsamsten]

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

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

Da Infinityes sich bei JS um eine legale Nummer handelt, scheint diese Lösung nicht vollständig korrekt zu sein.


3. Die Kunst ... aber sehr langsam [am langsamsten]

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

4. Verwenden Sie Bit-Shift
schnell, abersign(-Infinity) -> 0

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

5. Typ-safe [megafast]

! Es scheint, als würden Browser (insbesondere Chrome v8) einige magische Optimierungen vornehmen, und diese Lösung ist viel leistungsfähiger als andere, selbst als (1.1), obwohl sie zwei zusätzliche Vorgänge enthält und logischerweise niemals schneller sein kann.

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

Werkzeuge

Verbesserungen sind willkommen!


[Offtopic] Akzeptierte Antwort

  • Andrey Tarantsov - +100 für die Kunst, aber leider ist es ungefähr 5 mal langsamer als der offensichtliche Ansatz

  • Frédéric Hamidi - irgendwie die am besten bewertete Antwort (für die Zeit des Schreibens) und es ist ein bisschen cool, aber es ist definitiv nicht so, wie Dinge gemacht werden sollten, imho. Außerdem werden Infinity-Zahlen, die auch Zahlen sind, nicht richtig verarbeitet.

  • kbec - ist eine Verbesserung der offensichtlichen Lösung. Nicht so revolutionär, aber zusammengenommen halte ich diesen Ansatz für den besten. Stimmen Sie für ihn :)


3
Der Punkt ist, dass manchmal 0ein Sonderfall ist
am

1
Ich habe eine Reihe von JSPerf-Tests (mit verschiedenen Arten von Eingaben) durchgeführt, um jeden Algorithmus zu testen. Diese finden Sie hier: jsperf.com/signs Die Ergebnisse
Alba Mendez

2
@ zufrieden, welcher von ihnen? Wenn Sie die test everythingVersion ausführen , weigert sich Safe natürlich, die speziellen Werte zu testen, sodass sie schneller sind! Versuchen Sie only integersstattdessen , den Test auszuführen. Außerdem macht JSPerf nur seinen Job, es geht nicht darum, ihn zu mögen. :)
Alba Mendez

2
Laut jsperf-Tests stellt sich heraus, dass typeof x === "number"die Leistung etwas magischer wird. Bitte machen Sie mehr Läufe, insbesondere FF, Opera und IE, um es klar zu machen.
disfated

4
Der Vollständigkeit halber habe ich einen neuen Test jsperf.com/signs/7 für Math.sign()(0 === 0, nicht so schnell wie "Sicher") hinzugefügt, der in FF25 erschien und in Chrom erscheint.
Alex K.

Antworten:



28

Das Teilen der Zahl durch ihren absoluten Wert gibt auch ihr Vorzeichen. Die Verwendung des logischen UND-Kurzschlussoperators ermöglicht es uns, Sonderfälle zu erstellen, 0damit wir uns nicht dadurch teilen:

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

6
Sie würden wahrscheinlich var sign = number && number / Math.abs(number);für den Fall wollennumber = 0
NullUserException

@NullUserException, Sie haben absolut Recht, 0muss speziell behandelt werden. Antwort entsprechend aktualisiert. Danke :)
Frédéric Hamidi

Du bist der Beste für jetzt. Ich hoffe aber, dass es in Zukunft weitere Antworten geben wird.
Desfated

24

Die gesuchte Funktion heißt signum und lässt sich am besten implementieren:

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

3
Warten. Es gibt einen Fehler: für (x = -2; x <= 2; x ++) console.log ((x> 1) - (x <1)); gibt [-1, -1, -1, 0, 1] für (x = -2; x <= 2; x ++) console.log ((x> 0) - (x <0)); gibt korrekte [-1, -1, 0, 1, 1]
disfated

13

Sollte dies nicht die vorzeichenbehafteten Nullen von JavaScript (ECMAScript) unterstützen? Es scheint zu funktionieren, wenn in der Funktion „Megafast“ eher x als 0 zurückgegeben wird:

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

Dies macht es kompatibel mit einem Entwurf von ECMAScript's Math.sign ( MDN ):

Gibt das Vorzeichen des x zurück und gibt an, ob x positiv, negativ oder null ist.

  • Wenn x NaN ist, ist das Ergebnis NaN.
  • Wenn x –0 ist, ist das Ergebnis –0.
  • Wenn x +0 ist, ist das Ergebnis +0.
  • Wenn x negativ und nicht −0 ist, ist das Ergebnis −1.
  • Wenn x positiv und nicht +0 ist, ist das Ergebnis +1.

Unglaublich schneller und interessanter Mechanismus, ich bin beeindruckt. Warten auf weitere Tests.
Kbec

10

Für Leute, die interessiert sind, was mit den neuesten Browsern los ist, gibt es in der ES6-Version eine native Math.sign- Methode. Sie können den Support hier überprüfen .

Grundsätzlich kehrt es zurück -1 , 1, 0oderNaN

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;

Superschnell, wenn Sie Infinity nicht benötigen und wissen, dass die Zahl eine Ganzzahl ist, die in der Quelle openjdk-7 enthalten ist: java.lang.Integer.signum()


1
Dies schlägt bei kleinen negativen Brüchen wie -0,5 fehl. (Sieht so aus, als ob die Quelle aus einer Implementierung speziell für Ganzzahlen stammt)
Starwed

1

Ich dachte, ich würde das nur zum Spaß hinzufügen:

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

0 und NaN geben -1 zurück. Funktioniert
gut bei +/- Unendlich


1

Eine Lösung, die für alle Zahlen sowie für 0und -0sowie Infinityund funktioniert -Infinity, ist:

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

Weitere Informationen finden Sie in der Frage " Sind +0 und -0 gleich? ".


Achtung: Keine dieser Antworten, einschließlich der jetzt Standard Math.signwerden die Arbeiten an dem Fall 0vs -0. Dies ist möglicherweise kein Problem für Sie, aber in bestimmten physischen Implementierungen kann es von Bedeutung sein.


0

Sie können die Zahl bitverschieben und das höchstwertige Bit (MSB) überprüfen. Wenn das MSB eine 1 ist, ist die Zahl negativ. Wenn es 0 ist, ist die Zahl positiv (oder 0).


@ NullUserException Ich könnte mich immer noch irren, aber aus meiner Lektüre "Die Operanden aller bitweisen Operatoren werden in vorzeichenbehaftete 32-Bit-Ganzzahlen in Big-Endian-Reihenfolge und im Zweierkomplementformat konvertiert." genommen von MDN
Brombomb

Das scheint immer noch eine Menge Arbeit zu sein; Sie müssen noch 1 und 0 in -1 und 1 konvertieren, und 0 muss ebenfalls berücksichtigt werden. Wenn das OP das nur wollte, wäre es einfacher, es einfach zu benutzenvar sign = number < 0 : 1 : 0
NullUserException

+1. Sie müssen jedoch nicht wechseln, sondern können einfach n & 0x80000000eine Bitmaske verwenden. Wie für die Konvertierung in 0,1, -1:n && (n & 0x80000000 ? -1 : 1)
David

@davin Funktionieren garantiert alle Zahlen mit dieser Bitmaske? Ich steckte ein -5e32und es brach.
NullUserException

@NullUserException ఠ_ఠ, Zahlen, die bei Anwendung der Standards das gleiche Vorzeichen haben ToInt32. Wenn Sie dort lesen (Abschnitt 9.5), gibt es einen Modul, der den Wert der Zahlen beeinflusst, da der Bereich einer 32-Bit-Ganzzahl kleiner ist als der Bereich des Typs js Number. Für diese Werte oder die Unendlichkeiten funktioniert es also nicht. Die Antwort gefällt mir trotzdem.
David

0

Ich wollte gerade die gleiche Frage stellen, kam aber zu einer Lösung, bevor ich mit dem Schreiben fertig war. Ich sah, dass diese Frage bereits vorhanden war, sah diese Lösung aber nicht.

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

Es scheint jedoch schneller zu sein, wenn ein Ternär hinzugefügt wird (n >> 31) + (n>0?1:0)


Sehr schön. Ihr Code scheint ziemlich viel schneller als (1) zu sein. (n> 0? 1: 0) ist schneller, da kein Typ gegossen wird. Der einzige enttäuschende Moment ist das Vorzeichen (-Infinity) ergibt 0. Aktualisierte Tests.
Uhr entlassen

0

Sehr ähnlich zu Martijns Antwort ist

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

Ich finde es besser lesbar. Außerdem (oder, abhängig von Ihrer Sichtweise), werden auch Dinge erfasst, die als Zahl interpretiert werden können. zB kehrt es zurück, -1wenn es präsentiert wird '-5'.


0

Ich sehe keinen praktischen Grund für die Rückgabe von -0 und 0, Math.signdaher lautet meine Version:

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

Dies ist keine Signum-Funktion
am

0

Ich kenne folgende Methoden:

Math.sign (n)

var s = Math.sign(n)

Dies ist die native Funktion, die jedoch aufgrund des Overheads eines Funktionsaufrufs am langsamsten ist. Es behandelt jedoch 'NaN', wobei die anderen unten möglicherweise nur 0 annehmen (dh Math.sign ('abc') ist NaN).

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

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

In diesem Fall kann nur die linke oder rechte Seite eine 1 sein, basierend auf dem Vorzeichen. Dies führt entweder zu 1-0(1), 0-1(-1) oder 0-0(0).

Die Geschwindigkeit dieses einen scheint Hals an Hals mit dem nächsten unten in Chrome.

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

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

Verwendet die "Vorzeichenausbreitung nach rechts". Grundsätzlich werden durch Verschieben um 31 alle Bits außer dem Vorzeichen gelöscht. Wenn das Vorzeichen gesetzt wurde, ergibt dies -1, andernfalls ist es 0. Rechts |davon wird auf Positiv geprüft, indem der Wert in einen Booleschen Wert konvertiert wird (0 oder 1 [Übrigens: nicht numerische Zeichenfolgen, wie z!!'abc' werden in diesem Fall 0, und nicht NaN]) verwendet dann eine bitweise ODER-Verknüpfung, um die Bits zu kombinieren.

Dies scheint die beste durchschnittliche Leistung aller Browser zu sein (zumindest die beste in Chrome und Firefox), aber nicht die schnellste in ALLEN. Aus irgendeinem Grund ist der ternäre Operator im IE schneller.

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

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

Aus irgendeinem Grund am schnellsten im IE.

jsPerf

Durchgeführte Tests: https://jsperf.com/get-sign-from-value


0

Meine zwei Cent mit einer Funktion, die die gleichen Ergebnisse wie Math.sign liefert, dh Vorzeichen (-0) -> -0, Vorzeichen (-Infinity) -> -Infinity, Vorzeichen (null) -> 0 , Zeichen (undefiniert) -> NaN usw.

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

Jsperf lässt mich keinen Test oder keine Revision erstellen. Es tut mir leid, dass ich Ihnen keine Tests zur Verfügung stellen kann (ich habe jsbench.github.io ausprobiert, aber die Ergebnisse scheinen viel näher beieinander zu liegen als bei Jsperf ...).

Wenn jemand es bitte zu einer Jsperf-Revision hinzufügen könnte, wäre ich gespannt, wie es mit allen zuvor angegebenen Lösungen verglichen wird ...

Danke dir!

Jim.

EDIT :

Ich hätte schreiben sollen:

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

( (+x && -1)statt (x && -1)) um sign('abc')richtig zu handhaben (-> NaN)


0

Math.sign wird in IE 11 nicht unterstützt. Ich kombiniere die beste Antwort mit der Antwort von Math.sign:

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

Jetzt kann man Math.sign direkt verwenden.


1
Sie haben mich dazu gedrängt, meine Frage zu aktualisieren. 8 Jahre sind vergangen, seit es gefragt wurde. Außerdem wurde meine jsfiddle auf es6 und window.performance api aktualisiert. Aber ich bevorzuge Mozillas Version als Polyfill, da sie dem Typ-Zwang von Math.sign entspricht. Leistung ist heutzutage kein großes Problem.
am
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.