web-dev-qa-db-fra.com

Number.sign () en javascript

Je me demande s’il existe des moyens non triviaux de trouver le signe du nombre ( fonction signum )?
Peut être plus court/plus rapide/des solutions plus élégantes que la solution évidente

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

Court extrait

Utilisez ceci et vous serez en sécurité et rapide

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

Résultats

Pour l'instant nous avons ces solutions:


1. Évident et rapide

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

1.1. Modification de kbec - un type moins, plus performant, plus court [le plus rapide]

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

attention:sign("0") -> 1


2. Élégant, court, pas si rapide [le plus lent]

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

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

À partir de Infinity est un numéro légal dans JS, cette solution ne semble pas totalement correcte.


3. L'art ... mais très lent [le plus lent]

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

4. Utiliser bit-shift
rapide, mais sign(-Infinity) -> 0

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

5. Type-safe [mégafast]

! On dirait que les navigateurs (en particulier la version 8 de chrome) effectuent des optimisations magiques et que cette solution s'avère beaucoup plus performante que d’autres, même si (1.1) bien qu’elle contienne 2 opérations supplémentaires et logiquement, elle ne peut être plus rapide.

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

Outils

Les améliorations sont les bienvenues!


[Offtopic] Réponse acceptée

  • Andrey Tarantsov - +100 pour l'art, mais malheureusement, il est environ 5 fois plus lent que l'approche évidente

  • Frédéric Hamidi - D'une certaine manière, la réponse la plus élevée (pour le moment d'écrire) et c'est plutôt cool, mais ce n'est certainement pas la façon dont les choses devraient être faites, à mon humble avis. En outre, il ne gère pas correctement les nombres Infinity, qui sont également des nombres, vous savez.

  • kbec - est une amélioration de la solution évidente. Pas si révolutionnaire, mais dans l’ensemble, je considère que cette approche est la meilleure. Votez pour lui :)

99
disfated

Version plus élégante de la solution rapide:

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

Diviser le nombre par sa valeur absolue donne également son signe. L'utilisation de l'opérateur AND logique de court-circuitage nous permet d'utiliser un 0 spécial afin d'éviter de nous diviser par ce dernier:

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

La fonction que vous recherchez s'appelle signum , et la meilleure façon de la mettre en œuvre est la suivante:

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

Cela ne devrait-il pas supporter les zéros signés de JavaScript (ECMAScript)? Cela semble fonctionner quand on retourne x plutôt que 0 dans la fonction «mégafast»:

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

Ceci le rend compatible avec un brouillon de ECMAScript’s Math.sign ( MDN ):

Renvoie le signe du x, indiquant si x est positif, négatif ou nul.

  • Si x est NaN, le résultat est NaN.
  • Si x est -0, le résultat est -0.
  • Si x est +0, le résultat est +0.
  • Si x est négatif et non -0, le résultat est -1.
  • Si x est positif et non +0, le résultat est +1.
13
Martijn

Pour les personnes intéressées par ce qui se passe avec les derniers navigateurs, dans la version ES6, il existe une méthode native Math.sign . Vous pouvez vérifier le support ici .

Fondamentalement, il renvoie -1, 1, 0 ou 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
9
Salvador Dali
var sign = number >> 31 | -number >>> 31;

Superfast si vous n'avez pas besoin d'Infinity et savez que le nombre est un entier, trouvé dans le source openjdk-7: Java.lang.Integer.signum()

4
Toxiro

Je pensais ajouter ceci juste pour le plaisir:

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

0 et NaN reviendra -1
fonctionne bien sur +/- Infinity

1
jaya

Une solution qui fonctionne sur tous les nombres, ainsi que sur 0 et -0, ainsi que sur Infinity et -Infinity, est la suivante:

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

Voir la question " Est-ce que +0 et -0 sont les mêmes? " pour plus d'informations.


Avertissement: Aucune de ces réponses, y compris le désormais standard Math.sign ne fonctionnera sur le cas 0 vs -0. Ce n'est peut-être pas un problème pour vous, mais dans certaines implémentations de physique, cela peut avoir de l'importance.

1
Andy Ray

Très semblable à la réponse de Martijn est

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

Je le trouve plus lisible. Aussi (ou, selon votre point de vue, cependant), il raconte aussi des choses qui peuvent être interprétées comme un nombre; Par exemple, il retourne -1 lorsqu'il est présenté avec '-5'.

0
equaeghe

Vous pouvez décaler le nombre et vérifier le bit le plus significatif (MSB). Si le MSB est un 1, le nombre est négatif. Si c'est 0 alors le nombre est positif (ou 0). 

0
Brombomb

Je ne vois aucun sens pratique de renvoyer -0 et 0 à partir de Math.sign alors ma version est la suivante:

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
0
Alexander Shutov

Mes deux cents, avec une fonction qui retourne les mêmes résultats que Math.sign le ferait, à savoir signe (-0) -> -0, signe (-Infinity) -> -Infinity, signe (null) -> 0 , signe (non défini) -> NaN, etc.

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

Jsperf ne me laissera pas créer un test ou une révision, désolé de ne pas pouvoir vous fournir de tests (j’ai essayé jsbench.github.io, mais les résultats semblent beaucoup plus proches les uns des autres qu’avec Jsperf ...)

Si quelqu'un pouvait l'ajouter à une révision Jsperf, je serais curieux de voir comment il se compare à toutes les solutions données précédemment ...

Je vous remercie!

Jim.

MODIFIER:

J'aurais dû écrire:

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

((+x && -1) au lieu de (x && -1)) afin de gérer correctement sign('abc') (-> NaN)

0
Jimshell

J'étais sur le point de poser la même question, mais je suis arrivé à une solution avant d'avoir fini d'écrire, de voir que cette question existait déjà, mais de voir cette solution.

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

Il semble que ce soit plus rapide en ajoutant un ternaire bien que (n >> 31) + (n>0?1:0)

0
Moritz Roessler