Pourquoi la résolution des nombres à virgule flottante diminue-t-elle davantage à partir d'une origine?

19

Ma scène OpenGL contient des objets qui sont positionnés à des distances ridiculement éloignées de l'origine. Lorsque je regarde ces objets et que je fais un panoramique / rotation / zoom d'une caméra autour d'eux, ils «tremblent». Autrement dit, les sommets comprenant les objets semblent se casser autour d'une grille de points 3D imaginaire. J'ai lu que c'est un problème courant en raison de la quantité d'informations qui peuvent être stockées en utilisant la précision en virgule flottante (qu'OpenGL, et à peu près tout le reste utilise). Je ne comprends pas pourquoi cela se produit cependant.

Lors de la recherche d'une solution, je suis tombé sur le très simple correctif «origine flottante», et il semble fonctionner. Je transforme simplement tout pour que mes objets soient dans les mêmes positions relatives mais tout ce que mon appareil photo regarde est proche de l'origine. J'ai trouvé une explication ici: http://floatingorigin.com/ , mais je n'ai pas pu la suivre.

Alors ... Quelqu'un pourrait-il expliquer pourquoi le fait de positionner ma scène très loin (disons 10 millions d'unités) de l'origine entraîne le comportement erratique que j'ai observé? Et aussi pourquoi le déplacer près de l'origine résout le problème?

Pris
la source
4
Parce que s'ils ne le faisaient pas, ce seraient des nombres à virgule fixe . Question tautologique, ça.
MSalters
1
C'est vrai, mais seulement lorsque vous comprenez ce que signifie vraiment «virgule flottante».
Kylotan

Réponses:

26

Tout cela est dû à la façon dont les virgules flottantes sont représentées dans les ordinateurs.

Les entiers sont stockés assez simplement; chaque unité est exactement "une" à part de la "précédente" comme vous pouvez vous y attendre avec des nombres dénombrables.

Avec les nombres à virgule flottante, ce n'est pas exactement le cas. Au lieu de cela, plusieurs bits indiquent l'exposant, et le reste indique ce qui est connu comme la mantisse , ou partie fractionnaire qui est ensuite MULTIPLIÉE par la partie exposante (implicitement 2 ^ exp) pour donner le résultat final.

Regardez ici pour une explication visuelle des bits.

C'est précisément parce que cet exposant est une partie réelle des bits que la précision commence à décroître une fois que les nombres augmentent.

Pour voir cela en action, faisons une représentation en virgule flottante sans entrer dans le vif du sujet: prenez un petit exposant comme 2 et faites quelques parties fractionnaires pour tester:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

...etc.

Ces chiffres ne croissent pas très loin l'un de l'autre sur l'exposant 2. Mais maintenant, essayons l'exposant 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Whoa, énorme différence maintenant!

L'exemple, même s'il ne va pas spécifiquement à la VERY NEXT COUNTABLE (ce serait la toute prochaine partie fractionnaire en fonction du nombre de bits), est là pour démontrer la perte de précision une fois que les nombres augmentent. L'unité "suivant dénombrable" dans les flottants est très petite avec de petits exposants et TRÈS grande avec des exposants plus grands, alors qu'en entier, elle est TOUJOURS 1.

La raison pour laquelle la méthode de l'origine flottante fonctionne est parce qu'elle met à l'échelle tous ces nombres à virgule flottante à exposant potentiellement grand vers le bas à petit exposant afin que les "prochains comptes" (précision) puissent être très petits et heureux.


la source
Les exemples que vous avez donnés étaient vraiment illustratifs, merci :)
Pris le
3
Sur la bonne voie, mais j'aurais aimé que vous ayez utilisé des exemples plus proches de la façon dont fonctionne réellement la virgule flottante. Il n'élève pas la mantisse à l'exposant; c'est la mantisse * 2 ^ exposant.
Nathan Reed
3
Vous avez raison, je le savais; Je ne sais pas à quoi je pensais. Modifié ma réponse.
1
@ScottW Nice edit! +1
Nathan Reed
17

Parce que les nombres à virgule flottante sont représentés sous la forme fraction + exposant + signe, et vous n'avez qu'une quantité fixe de bits pour la partie fraction.

http://en.wikipedia.org/wiki/Single_precision

Comme vous obtenez des nombres de plus en plus grands, vous n'avez tout simplement pas les bits pour représenter les plus petites portions.

Tetrad
la source
8

Le classique du domaine doit être évoqué: ce que tout informaticien doit savoir sur les nombres à virgule flottante .

Mais l'essentiel a à voir avec la façon dont les nombres à virgule flottante de précision simple (double) ne sont qu'un nombre binaire de 32 bits (64 bits) avec 1 bit représentant le signe, un exposant de 8 bits (11 bits) de la base 2 et une signification de 23 bits (52 bits) (les parenthèses sont les valeurs des doubles).

Cela signifie que le plus petit nombre positif que vous pouvez représenter en simple précision est 0,000000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .

Le nombre positif suivant est le double de: 0,000000000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , puis le nombre suivant est la somme des deux précédents 0,000000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4,2 - 45 .

Cela continue d'augmenter de la même différence constante jusqu'à ce que: 0,111111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,17549435 x 10 -38 - 0,0000000014 x 10 -38 = 1,17549421 x 10 -38

Maintenant , vous avez atteint le nombre normal (où le premier chiffre du significand est 1) spécifiquement: 1,0000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 et le numéro suivant est alors 1,0000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1,17549435 x 1,00000023.

dr jimbob
la source
2

La raison pour laquelle les nombres à virgule flottante deviennent moins précis plus loin de l'origine est qu'un nombre à virgule flottante est censé pouvoir représenter de grands nombres. La façon dont cela se fait lui confère le terme "virgule flottante". Il divise les valeurs possibles qu'il peut prendre (qui est déterminée par sa longueur en bits) de sorte qu'il y ait environ le même nombre pour chaque exposant: Pour un flottant de 32 bits, 23 des bits définissent la mantisse ou la signification. Il pourra donc prendre la valeur de 2 ^ 23 valeurs différentes dans chaque plage d'exposants. L'une de ces plages d'exposants est 1-2 [2 ^ 0 à 2 ^ 1], donc la division de la plage 1 à 2 en 2 ^ 23 valeurs différentes permet une grande précision.

Mais en divisant la plage [2 ^ 10 à 2 ^ 11] en 2 ^ 23 valeurs différentes, l'espace entre chaque valeur est beaucoup plus grand. Si ce n'était pas le cas, 23 bits ne suffiraient pas. Le tout est un compromis: vous avez besoin d'un nombre infini de bits pour représenter n'importe quel nombre réel. Si votre application fonctionne d'une manière qui vous permet de vous en sortir avec une précision inférieure pour des valeurs plus grandes, et que vous bénéficiez de la possibilité de représenter réellement de grandes valeurs , vous utilisez une représentation en virgule flottante.

Steven Lu
la source
juste faire une note ici lors d'un examen plus approfondi 7 ans plus tard ... mes chiffres dans mes exemples ne sont pas particulièrement bien choisis. Mais les points globaux sont valables.
Steven Lu
1

Il peut être un peu difficile de proposer des exemples spécifiques de fonctionnement de la précision en virgule flottante. Pour compléter les autres réponses, en voici une. Disons que nous avons un nombre décimal à virgule flottante, avec trois chiffres de mantisse et un chiffre d'exposant:

exposant de la mantisse × 10

Lorsque l'exposant est égal à 0, chaque entier compris entre 0 et 999 peut être représenté avec précision. Lorsqu'il est égal à 1, vous multipliez essentiellement chaque élément de cette plage par 10, vous obtenez donc la plage 0–9990; mais maintenant, seuls les multiples de 10 peuvent être représentés avec précision, car vous n'avez encore que trois chiffres de précision. Lorsque l'exposant est à son maximum de 9, la différence entre chaque paire d'entiers représentables est d' un milliard . Vous échangez littéralement la précision contre la portée.

Cela fonctionne de la même manière avec les nombres binaires à virgule flottante: chaque fois que l'exposant augmente de un, la plage double , mais le nombre de valeurs représentables dans cette plage est divisé par deux . Cela s'applique également aux nombres fractionnaires, ce qui est bien sûr la source de votre problème.

Jon Purdy
la source
0

En général, la résolution empire car la résolution est multipliée par la valeur de l'exposant (2 ** partie d'exposant).

en reconnaissance du commentaire de Josh: ce qui précède était juste de mettre la réponse dans une déclaration succincte. Bien sûr, comme j'ai essayé de l'indiquer sur http://floatingorigin.com/ , cela ne fait que commencer vers une solution globale et votre programme pourrait avoir de la gigue à plusieurs endroits: dans le pipeline de précision ou dans d'autres parties du code .

Chris Thorne
la source
Cela n'ajoute rien qui ne soit déjà présent dans les autres réponses.
Vrai: J'ai réalisé que je pouvais décrire la réponse sur une seule ligne et j'ai pensé que quelqu'un pourrait trouver une réponse succincte utile.
Chris Thorne
-1

Le tampon de profondeur OpenGL n'est pas linéaire . Plus vous irez loin, plus la résolution sera mauvaise. Je recommande de lire ceci . Quelque chose tiré de là (12.070):

En résumé, la division en perspective, de par sa nature, entraîne une précision Z plus proche de l'avant du volume de vue que de l'arrière.

Et un autre (12.040):

Vous avez peut-être configuré vos plans de détourage zNear et zFar d'une manière qui limite considérablement la précision de votre tampon de profondeur. En règle générale, cela est dû à une valeur de plan d'écrêtage zNear trop proche de 0,0. Comme le plan d'écrêtage zNear est de plus en plus proche de 0,0, la précision effective du tampon de profondeur diminue considérablement. Éloigner le plan de détourage zFar de l'œil a toujours un impact négatif sur la précision du tampon de profondeur, mais ce n'est pas aussi dramatique que de déplacer le plan de détourage zNear.

Vous devez donc déplacer votre avion de détourage proche le plus loin possible et votre avion lointain le plus proche possible.

zacharmarz
la source
-1: la question porte sur la précision en virgule flottante, et non sur les problèmes de précision avec la représentation du tampon de profondeur non linéaire.
Nathan Reed
Il est possible que ce que je vois soit dû à des problèmes de tampon de profondeur. J'utilise une bibliothèque au-dessus d'OpenGL pour visualiser ma scène, et je fais l'hypothèse que sa configuration de la caméra, de la vue et des plans de détourage proches et lointains pour tenir compte de la taille et de la position de la géométrie (depuis le spectateur semble définir automatiquement une vue optimale pour le contenu de la scène). Mais je suppose que ce n'est peut-être pas le cas - je vais essayer de jouer avec les plans de détourage en laissant la position d'origine intacte et voir ce qui se passe.
Pris le
2Nathan Reed: L'auteur a écrit qu'il avait une scène OpenGL, alors j'ai pensé que ça pouvait être ce problème aussi.
zacharmarz
Ce problème peut sembler similaire ou lié, mais les valeurs du tampon de profondeur ne sont certainement PAS stockées d'une manière compatible avec les nombres à virgule flottante. Il s'agit d'un format à virgule fixe. C'est à cause de cela que la réponse peut être trompeuse.
Steven Lu