Quel est le plus grand niveau «relatif» que je puisse faire en utilisant float?

13

Tout comme cela a été démontré avec des jeux comme le siège de donjon et KSP, un niveau suffisamment grand commencera à avoir des problèmes en raison du fonctionnement de la virgule flottante. Vous ne pouvez pas ajouter 1e-20 à 1e20 sans perdre en précision.

Si je choisis de limiter la taille de mon niveau, comment puis-je calculer la vitesse minimale à laquelle mon objet peut se déplacer jusqu'à ce qu'il commence à être saccadé?

jokoon
la source

Réponses:

26

Un flottant de 32 bits a une mantisse de 23 bits .

Cela signifie que chaque nombre est représenté par 1.xxx xxx xxx xxx xxx xxx xxx xx fois une puissance de 2, où chaque x est un chiffre binaire, soit 0 ou 1. (À l'exception des nombres dénormalisés extrêmement petits inférieurs à 2-126 - ils commencent par 0. au lieu de 1., mais je les ignorerai pour ce qui suit)

Ainsi, dans la plage de 2je et 2(je+1) , vous pouvez représenter n'importe quel nombre avec une précision de ±2(je-24)

Par exemple, pour je=0 , le plus petit nombre dans cette plage est (20)1=1 . Le plus petit nombre suivant est (20)(1+2-23) . Si vous vouliez représenter 1+2-24 , vous devrez arrondir vers le haut ou vers le bas, pour une erreur de2-24 deux cas.

In this range:                You get accuracy within:
-----------------------------------------------
         0.25   -     0.5    2^-26 = 1.490 116 119 384 77 E-08
         0.5    -     1      2^-25 = 2.980 232 238 769 53 E-08
         1     -      2      2^-24 = 5.960 464 477 539 06 E-08
         2     -      4      2^-23 = 1.192 092 895 507 81 E-07
         4     -      8      2^-22 = 2.384 185 791 015 62 E-07
         8     -     16      2^-21 = 4.768 371 582 031 25 E-07
        16     -     32      2^-20 = 9.536 743 164 062 5  E-07
        32     -     64      2^-19 = 1.907 348 632 812 5  E-06
        64     -    128      2^-18 = 0.000 003 814 697 265 625
       128    -     256      2^-17 = 0.000 007 629 394 531 25
       256    -     512      2^-16 = 0.000 015 258 789 062 5
       512    -   1 024      2^-15 = 0.000 030 517 578 125
     1 024    -   2 048      2^-14 = 0.000 061 035 156 25
     2 048    -   4 096      2^-13 = 0.000 122 070 312 5
     4 096    -   8 192      2^-12 = 0.000 244 140 625
     8 192   -   16 384      2^-11 = 0.000 488 281 25
    16 384   -   32 768      2^-10 = 0.000 976 562 5
    32 768   -   65 536      2^-9  = 0.001 953 125
    65 536   -  131 072      2^-8  = 0.003 906 25
   131 072   -  262 144      2^-7  = 0.007 812 5
   262 144   -  524 288      2^-6  = 0.015 625
   524 288 -  1 048 576      2^-5  = 0.031 25
 1 048 576 -  2 097 152      2^-4  = 0.062 5
 2 097 152 -  4 194 304      2^-3  = 0.125
 4 194 304 -  8 388 608      2^-2  = 0.25
 8 388 608 - 16 777 216      2^-1  = 0.5
16 777 216 - 33 554 432      2^0   = 1

Donc, si vos unités sont en mètres, vous perdrez une précision millimétrique autour de la bande 16 484 - 32 768 (à environ 16-33 km de l'origine).

On pense généralement que vous pouvez contourner ce problème en utilisant une unité de base différente, mais ce n'est pas vraiment vrai, car c'est la précision relative qui compte.

  • Si nous utilisons les centimètres comme unité, nous perdons la précision millimétrique dans la bande de 1 048 576-2 097 152 (10-21 km de l'origine)

  • Si nous utilisons des hectamètres comme unité, nous perdons une précision millimétrique dans la bande 128-256 (13-26 km de l'origine)

... donc changer l'unité sur quatre ordres de grandeur se termine toujours par une perte de précision millimétrique quelque part dans la gamme de dizaines de kilomètres. Tout ce que nous changeons, c'est où exactement dans cette bande il frappe (en raison de l'inadéquation entre la numérotation base-10 et base-2), n'allongeant pas considérablement notre zone de jeu.

La précision exacte de votre jeu dépendra des détails de votre gameplay, de la simulation physique, de la taille de l'entité / des distances de tirage, de la résolution de rendu, etc., il est donc difficile de définir une limite exacte. Il se peut que votre rendu semble bien à 50 km de l'origine, mais vos balles se téléportent à travers les murs, ou un script de gameplay délicat se détraque. Ou vous pouvez trouver que le jeu se déroule bien, mais tout a une vibration à peine perceptible due à des inexactitudes dans la transformation de la caméra.

Si vous connaissez le niveau de précision dont vous avez besoin (par exemple, une plage de 0,01 unité correspond à environ 1 px à votre distance de visualisation / interaction typique, et tout décalage plus petit est invisible), vous pouvez utiliser le tableau ci-dessus pour trouver où vous perdez précision, et reculer de quelques ordres de grandeur pour la sécurité en cas d'opérations avec perte.

Mais si vous pensez à des distances énormes, il peut être préférable de contourner tout cela en recentrant votre monde pendant que le joueur se déplace. Vous choisissez une région de forme carrée ou cubique conservativement petite autour de l'origine. Chaque fois que le joueur se déplace en dehors de cette région, traduisez-les, ainsi que tout ce qui se trouve dans le monde, recule de la moitié de la largeur de cette région, en gardant le joueur à l'intérieur. Puisque tout bouge ensemble, votre joueur ne verra pas de changement. Des inexactitudes peuvent encore se produire dans des régions éloignées du monde, mais elles sont généralement beaucoup moins visibles là-bas que sous vos pieds, et vous êtes assuré d'avoir toujours une haute précision disponible près du joueur.

DMGregory
la source
1
Le recentrage est définitivement la voie à suivre!
Floris
2
Qu'en est-il de l'utilisation de coordonnées à point fixe? Peut-être avec des entiers 64 bits si nécessaire?
API-Beast
la question est, quelle taille cette région recentrée peut-elle être? si par exemple dans mon jeu je veux tirer à haute distance avec un zoom puissant, dois-je absolument utiliser le double ou est-ce assez flottant? n'est-il pas préférable de recentrer selon un arbre quadruple ou un algorithme de tuile?
jokoon
Cela dépendra de la stabilité numérique des algorithmes utilisés par vos systèmes de rendu et de physique - donc pour une base de code / moteur donnée, la seule façon de savoir avec certitude est d'essayer une scène de test. Nous pouvons utiliser le tableau pour estimer la précision maximale (par exemple. Une caméra à 16 km d'un objet aura tendance à voir au moins des erreurs de taille millimétrique, donc votre zoom doit être suffisamment large pour les garder plus petites qu'un pixel - si le zoom a besoin être plus serré pour votre jeu, puis doubler ou des calculs intelligents peuvent être nécessaires), mais une chaîne d'opérations avec perte peut rencontrer des problèmes bien avant cette limite hypothétique.
DMGregory
Je me demande encore ce que cela signifie de recentrer le monde pour une API graphique. Si j'ai un gros morceau de géométrie instanciée (ou non instanciée), est-il toujours sûr de le faire? Je suppose que cela signifie traduire TOUTES les transformations, mais si je le fais plusieurs fois, n'y a-t-il pas un risque de perte de précision en virgule flottante?
jokoon
3

Il est difficile de répondre car cela dépend de l'échelle de votre physique: Quelle est la vitesse de mouvement minimale acceptable qui ne doit PAS être arrondie à zéro?

Si vous avez besoin d'un monde vaste et d'une physique cohérente, il est préférable d'utiliser une classe à points fixes.

Par exemple, tirer une balle de canon de n'importe où dans le monde vous donnera le même résultat et un point fixe 64 bits (32,32) vous donne une énorme quantité de précision et plus que tout ce qui est perceptible dans la plupart des jeux. Si votre unité mesure 1 mètre, vous êtes toujours à une précision de 232 picomètres à 2147483 km de l'origine.

Vous pouvez toujours effectuer la physique locale en virgule flottante dans la cellule locale pour économiser sur le travail de programmation et utiliser un moteur physique standard. Il restera raisonnablement cohérent à toutes fins pratiques.

En bonus, la phase large et l'AABB ont tendance à être plus rapides en virgule fixe en raison de la latence du FPU. Il est également plus rapide de convertir un point fixe en index octree (ou quadtree) car vous pouvez effectuer un masquage de bits simple.

Ces opérations ne bénéficient pas autant des instructions SIMD et du pipelining qui masqueraient normalement la latence des FPU.

Vous pouvez convertir les positions en virgule flottante APRÈS avoir soustrait la position de la caméra en virgule fixe pour tout rendre, en évitant les problèmes de virgule flottante dans un grand monde et en utilisant toujours un moteur de rendu classique utilisant des virgules flottantes.

Stéphane Hockenhull
la source
-3

Vous pouvez l'éviter complètement par multiplication.
Au lieu de travailler avec des flotteurs, multipliez-les simplement par 10 ^ (x), stockez-les et, si nécessaire, multipliez-les à nouveau par 10 ^ (- x).
De cela, cela dépend du type d'int que vous souhaitez utiliser.

Anon
la source
2
Cela n'évite pas le problème. Un format à virgule fixe a toujours une précision et une plage finies - plus la plage est grande, plus la précision est faible (en fonction de l'endroit où vous mettez ce point décimal fixe) - donc la décision de "quelle taille puis-je faire un niveau sans erreurs d'arrondi visibles" s'applique toujours.
DMGregory
3
De plus, la base-10 n'est pas très pratique. Les nombres à virgule fixe fonctionnent beaucoup mieux lorsque vous divisez l'entier en termes de bits (considérez non signé 26,6 - la composante fractionnaire est les 6 bits inférieurs ( 1,0 / 64,0 * x & 63) et la partie intégrale est simplement x >> 6) . C'est beaucoup plus simple à mettre en œuvre que d'élever quelque chose à une puissance de dix.
Andon M. Coleman