Question

J'ai entendu dire que vous pouviez décaler à droite un nombre de 0,5 au lieu d'utiliser Math.floor (). J'ai décidé de vérifier ses limites pour vérifier si le remplacement était approprié. J'ai donc vérifié les valeurs suivantes et obtenu les résultats suivants dans Google Chrome:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s

Après quelques manipulations, j'ai découvert que la valeur la plus élevée possible de deux qui, avec un décalage à droite de 0,5, donnerait 2, vaut 2,99999999999999997979553950749691915273666351835935437999999 & # 175; (avec le 9 répétant) dans Chrome et Firefox. Le numéro est 2.999999999999999997779 & # 175; dans IE.

Ma question est la suivante: quelle est la signification du nombre .00000000000000077795539507496819152736663818359374? C'est un nombre très étrange et cela a vraiment piqué ma curiosité.

J'ai essayé de trouver une réponse ou au moins une sorte de schéma, mais je pense que mon problème réside dans le fait que je ne comprends vraiment pas l'opération bit à bit. Je comprends l'idée en principe, mais le décalage d'une séquence de 0,5 n'a aucun sens pour moi. Toute aide est appréciée.

Pour mémoire, la séquence de chiffres étranges change avec 2 ^ x. Les valeurs les plus élevées possibles des nombres suivants toujours tronquées correctement:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯
for 1: 1.9999999999999999888977697537484345957636833190917968749¯
for 2-3: x+.99999999999999977795539507496869191527366638183593749¯
for 4-7: x+.9999999999999995559107901499373838305473327636718749¯
for 8-15: x+.999999999999999111821580299874767661094665527343749¯
...and so forth
Était-ce utile?

La solution

En fait, vous finissez simplement par faire un floor () sur le premier opérande, sans aucune opération en virgule flottante. Etant donné que les opérations de décalage à gauche et de décalage à droite aux bits n'ont de sens qu'avec des opérandes entiers, le moteur JavaScript convertit d'abord les deux opérandes en entiers:

2.999999 >> 0.5

devient:

Math.floor(2.999999) >> Math.floor(0.5)

Ce qui à son tour est:

2 >> 0

Décaler de 0 bits signifie "ne faites pas de décalage". et donc vous vous retrouvez avec le premier opérande, simplement tronqué en un entier.

Le code source de SpiderMonkey a:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;

Votre vision des "arrondis" avec certains nombres est dû au fait que le moteur JavaScript ne peut pas gérer les chiffres décimaux au-delà d'une certaine précision et donc votre nombre finit par être arrondi au prochain entier. Essayez ceci dans votre navigateur:

alert(2.999999999999999);

Vous obtiendrez 2.999999999999999. Maintenant, essayez d’ajouter un autre 9:

alert(2.9999999999999999);

Vous obtiendrez un 3.

Autres conseils

C’est peut-être la pire idée que je n’ai jamais vue. Son seul but possible d’exister est de remporter un concours de code obfusticated. Les numéros longs que vous avez publiés n'ont aucune importance - ils constituent un artefact de l'implémentation en virgule flottante sous-jacente, filtrée par le nombre illimité de couches intermédiaires. Décaler des bits d'un nombre fractionnaire d'octets est insensé et je suis surpris qu'il ne soulève pas d'exception - mais c'est du javascript, toujours prêt à redéfinir "aliéné".

Si j'étais vous, je ne voudrais jamais utiliser cette "fonctionnalité". Sa seule valeur est en tant que cause première possible d’une condition d’erreur inhabituelle. Utilisez Math.floor () et prenez en pitié le prochain programmeur qui gérera le code.

Confirmer quelques soupçons que j'avais en lisant la question:

  • Décaler à droite tout nombre décimal x de tout nombre décimal y tronquera simplement x , donnant le même résultat que Math .floor () en confondant complètement le lecteur.
  • 2.99999999999999999797955395074968691915 ... est simplement le plus grand nombre pouvant être différencié de "3". Essayez de l’évaluer par lui-même. Si vous ajoutez quelque chose, il sera évalué à 3. Il s’agit d’un artefact du navigateur et de la mise en oeuvre en virgule flottante du système local.

Si vous voulez aller plus loin, lisez "Ce que tous les informaticiens devraient savoir sur l'arithmétique en virgule flottante": http://docs.sun.com/source/806-3568/ncg_goldberg.html

Je ne pense pas que votre changement de droite soit pertinent. Vous êtes simplement au-delà de la résolution d'une constante à virgule flottante double précision.

Dans Chrome:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);

Imprime: x = 2.9999999999999996 y = 3

Essayez cette sortie javascript:   alert (parseFloat ("2.99999999999999979759550397496919152736663818359374999999"));

Alors essayez ceci:   alert (parseFloat ("2.99999999999999979795539507496919152736663818359375"));

Ce que vous voyez est une simple inexactitude en virgule flottante. Pour plus d'informations à ce sujet, consultez, par exemple, cet élément: http://en.wikipedia.org/wiki / Floating_point # Accuracy_problems .

Le problème fondamental est que le nombre le plus proche d’une valeur à virgule flottante pouvant représenter le deuxième nombre est supérieur ou égal à 3, alors que le nombre que le flottant peut atteindre au premier nombre est strictement inférieur à trois.

En ce qui concerne la raison pour laquelle un décalage à droite de 0,5 fait quelque chose de sain, il semble que 0,5 soit tout simplement en train d'être converti au préalable en int (0). Ensuite, le float original (2.999 ...) est converti en int par troncation, comme d’habitude.

L'opérateur de décalage droit ne fonctionne que sur les entiers (les deux côtés). Donc, passer à droite par 0,5 bits devrait être exactement équivalent à passer à droite par 0 bits. Et le côté gauche est converti en un entier avant l'opération de décalage, ce qui fait la même chose que Math.floor ().

  

Je soupçonne que la conversion de 2.99999999999999979795539507496819152736663818359374999999   à sa représentation binaire serait éclairant. C'est probablement seulement 1 bit différent   du vrai 3.

Bien, mais pas de cigare. Le nombre de PF en double précision ayant 53 bits, le dernier numéro de PF avant 3 est en fait (exact): 2.99999999999999999555910790149937383830547332763671875

Mais pourquoi c'est 2.99999999999999979795539507496819152736663818359375

(et c'est exact, pas 49999 ...!)

qui est supérieur à la dernière unité affichable? Arrondi. La routine de conversion (chaîne en nombre) est simplement correctement programmée pour arrondir l’entrée au prochain nombre à virgule flottante.

2.99999999999999999555910790149937383830547332763671875

....... (valeurs comprises entre, augmentant) - > arrondir vers le bas

2.9999999999999999979795539507496819152736663818359375

....... (valeurs comprises entre, augmentant) - > arrondir à 3

3

L'entrée de conversion doit utiliser une précision maximale. Si le nombre est exactement la moitié entre ces deux numéros fp (qui est 2.99999999999999979795539507496819152736663818359375) l'arrondi dépend des drapeaux configurés. L'arrondi par défaut est arrondi à pair, ce qui signifie que le nombre sera arrondi au nombre pair suivant.

Maintenant

3 = 11. (binaire)

2,999 ... = 10.11111111111 ...... (binaire)

Tous les bits sont définis, le nombre est toujours impair. Cela signifie que le demi-chiffre exact sera arrondi, vous obtiendrez donc la période étrange ..... 49999 car il doit être plus petit que la moitié exacte pour pouvoir être distingué de 3.

Je soupçonne que la conversion de 2.999999999999999997779553950749681919272766661818359374999999 en sa représentation binaire serait éclairante. C'est probablement seulement 1 bit différent du vrai 3.

Et pour ajouter à la réponse de John, les chances que cela soit plus performant que Math.floor sont infimes.

Je ne sais pas si JavaScript utilise des nombres à virgule flottante ou une sorte de bibliothèque de précision infinie, mais dans tous les cas, vous obtiendrez des erreurs d'arrondi sur une opération comme celle-ci, même si elle est assez bien définie.

Il est à noter que le nombre ".000000000000000000077795539507496819152736663818359374" est probablement le Epsilon , défini comme le "plus petit nombre E tel que (1 + E ) > 1. "

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top