web-dev-qa-db-fra.com

Utilisation de bitwise OR 0 pour définir un nombre

Un de mes collègues est tombé sur une méthode de calcul des nombres flottants en utilisant un bitwise ou:

var a = 13.6 | 0; //a == 13

Nous en parlions et nous nous demandions quelques petites choses.

  • Comment ça marche? Notre théorie était que l'utilisation d'un tel opérateur convertit le nombre en un entier, supprimant ainsi la partie fractionnaire
  • At-il des avantages sur le fait de faire Math.floor? Peut-être que c'est un peu plus rapide? (jeu de mots non prévu)
  • At-il des inconvénients? Peut-être que cela ne fonctionne pas dans certains cas? La clarté est une évidence, car nous devions le comprendre, et bien, j’écris cette question.

Merci.

175
Alex Turpin

Comment ça marche? Notre théorie était que l'utilisation d'un tel opérateur convertit le nombre en un entier, supprimant ainsi la partie fractionnaire

Toutes les opérations sur les bits sauf le décalage droit non signé, >>>, Fonctionnent sur des entiers 32 bits signés. Donc, utiliser des opérations au niveau des bits convertira un float en entier.

At-il des avantages par rapport à Math.floor? Peut-être que c'est un peu plus rapide? (jeu de mots non prévu)

http://jsperf.com/or-vs-floor/2 semble légèrement plus rapide

At-il des inconvénients? Peut-être que cela ne fonctionne pas dans certains cas? La clarté est une évidence, car nous devions le comprendre, et bien, j’écris cette question.

  • Ne passera pas jsLint.
  • Entiers signés 32 bits uniquement
  • Comportement comparatif impair: Math.floor(NaN) === NaN, tandis que (NaN | 0) === 0
139
Joe

C'est troncature par opposition au revêtement de sol. La réponse de Howard est correcte. Mais j'ajouterais que Math.floor fait exactement ce qu'il est censé faire en ce qui concerne les nombres négatifs. Mathématiquement, c'est ce qu'est un plancher.

Dans le cas que vous avez décrit ci-dessus, le programmeur était plus intéressé par troncature ou découpait complètement la décimale. Bien que, la syntaxe employée masque en quelque sorte le fait qu’ils convertissent le float en int.

28
Chad La Guardia

Dans ECMAScript 6, l’équivalent de |0 est Math.trunc , genre de devrais-je dire:

Renvoie la partie intégrale d'un nombre en supprimant les chiffres décimaux. Il ne fait que tronquer le point et les chiffres qui le suivent, que l'argument soit un nombre positif ou un nombre négatif.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN
18
zangw

Votre premier point est correct. Le nombre est converti en un entier et ainsi tous les chiffres décimaux sont supprimés. Veuillez noter que Math.floor arrondit à l'entier suivant vers moins l'infini et donne ainsi un résultat différent lorsqu'il est appliqué aux nombres négatifs.

10
Howard
  • Les spécifications disent qu'il est converti en un entier:

    Soit lnum ToInt32 (lval).

  • Performance: ceci a été testé à jsperf auparavant.

note: le lien mort vers la spécification a été supprimé

5
pimvdb

Javascript représente Number comme nombres flottants double précision 64 bits .

Math.floor fonctionne dans cet esprit.

Les opérations au niveau des bits fonctionnent en entiers 32 bits signé. Les entiers signés 32 bits utilisent le premier bit comme signifiant négatif et les 31 autres bits sont le nombre. De ce fait, les nombres minimum et maximum autorisés signés sur 32 bits sont respectivement -2 147 483 648 et 2147483647 (0x7FFFFFFFF).

Alors quand tu fais | 0, vous faites essentiellement est & 0xFFFFFFFF. Cela signifie que tout nombre représenté par 0x80000000 (2147483648) ou supérieur sera renvoyé sous forme de nombre négatif.

Par exemple:

 // Safe
 (2147483647.5918 & 0xFFFFFFFF) ===  2147483647
 (2147483647      & 0xFFFFFFFF) ===  2147483647
 (200.59082098    & 0xFFFFFFFF) ===  200
 (0X7FFFFFFF      & 0xFFFFFFFF) ===  0X7FFFFFFF

 // Unsafe
 (2147483648      & 0xFFFFFFFF) === -2147483648
 (-2147483649     & 0xFFFFFFFF) ===  2147483647
 (0x80000000      & 0xFFFFFFFF) === -2147483648
 (3000000000.5    & 0xFFFFFFFF) === -1294967296

Également. Les opérations au niveau des bits ne "solent" pas. Ils troncent, ce qui revient à dire qu'ils se rapprochent le plus de 0. Une fois que vous passez aux nombres négatifs, Math.floor tours en bas en commençant au niveau du bit en haut.

Comme je le disais avant, Math.floor est plus sûr car il fonctionne avec des nombres flottants de 64 bits. Au niveau des bits est plus rapide, oui, mais limité à une portée signée de 32 bits.

Résumer:

  • Bitwise fonctionne de la même manière si vous travaillez à partir de 0 to 2147483647.
  • Au niveau des bits, 1 chiffre est désactivé si vous travaillez à partir de -2147483647 to 0.
  • Le bit à bit est complètement différent pour les nombres inférieurs à -2147483648 et supérieur à 2147483647.

Si vous vraiment voulez modifier les performances et utiliser les deux:

function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

Juste pour ajouter Math.trunc fonctionne comme des opérations au niveau des bits. Donc, vous pouvez faire ceci:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}
2
ShortFuse