¿Cuál es el nivel "relativo" más grande que puedo hacer con flotador?

13

Al igual que se demostró con juegos como Dungeon Siege y KSP, un nivel lo suficientemente grande comenzará a tener fallas debido a cómo funciona el punto flotante. No puede agregar 1e-20 a 1e20 sin perder precisión.

Si elijo limitar el tamaño de mi nivel, ¿cómo calculo la velocidad mínima a la que puede moverse mi objeto hasta que comience a estar entrecortado?

jokoon
fuente

Respuestas:

26

Un flotador de 32 bits tiene una mantisa de 23 bits .

Eso significa que cada número se representa como 1.xxx xxx xxx xxx xxx xxx xxx xx multiplicado por una potencia de 2, donde cada x es un dígito binario, ya sea 0 o 1. (Con la excepción de los números desnormalizados extremadamente pequeños menores de 2126 - comienzan con 0. en lugar de 1., pero los ignoraré para lo que sigue)

Entonces, en el rango de 2i y 2(i+1) , puede representar cualquier número con una precisión de ±2(i24)

Como ejemplo, para yo=0 0 , el número más pequeño en este rango es (20 0)1=1 . El siguiente número más pequeño es (20 0)(1+2-23) . Si desea representar 1+2-24 , tendrá que redondear hacia arriba o hacia abajo, para un error de 2-24 cualquier manera.

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

Entonces, si sus unidades son metros, perderá precisión milimétrica alrededor de la banda 16 484 - 32 768 (aproximadamente 16-33 km desde el origen).

Se cree comúnmente que puede solucionar esto utilizando una unidad base diferente, pero eso no es realmente cierto, ya que lo que importa es la relativa precisión.

  • Si usamos centímetros como nuestra unidad, perdemos precisión milimétrica en la banda 1 048 576-2 097 152 (10-21 km desde el origen)

  • Si usamos hectáreas como nuestra unidad, perdemos precisión milimétrica en la banda de 128-256 (13-26 km del origen)

... así que cambiar la unidad en cuatro órdenes de magnitud todavía termina con una pérdida de precisión milimétrica en algún lugar en el rango de decenas de kilómetros. Todo lo que estamos cambiando es donde exactamente en esa banda golpea (debido a la falta de coincidencia entre la numeración de base 10 y base-2) que no extiende drásticamente nuestra área jugable.

La cantidad exacta de imprecisión que pueda tolerar su juego dependerá de los detalles de su juego, simulación física, tamaño de entidad / distancias de dibujo, resolución de representación, etc., por lo que es difícil establecer un límite exacto. Puede que su renderizado se vea bien a 50 km del origen, pero sus balas se teletransportan a través de las paredes o un guión de juego sensible se vuelve loco. O puede encontrar que el juego funciona bien, pero todo tiene una vibración apenas perceptible debido a imprecisiones en la transformación de la cámara.

Si conoce el nivel de precisión que necesita (por ejemplo, un intervalo de 0,01 unidades se asigna a aproximadamente 1 px en su distancia de visualización / interacción típica, y cualquier desplazamiento más pequeño es invisible), puede usar la tabla de arriba para encontrar dónde pierde eso precisión y retroceda unos pocos órdenes de magnitud por seguridad en caso de operaciones con pérdidas.

Pero si está pensando en grandes distancias, puede ser mejor evitar todo esto volviendo a centrar su mundo a medida que el jugador se mueve. Elige una región conservadoramente pequeña, cuadrada o en forma de cubo alrededor del origen. Cada vez que el jugador se mueva fuera de esta región, traduzca, y todo en el mundo, a la mitad del ancho de esta región, manteniendo al jugador adentro. Como todo se mueve de manera conjunta, tu jugador no verá un cambio. Las inexactitudes aún pueden suceder en partes distantes del mundo, pero generalmente son mucho menos notables que suceder justo debajo de tus pies, y tienes la garantía de que siempre tendrás alta precisión disponible cerca del jugador.

DMGregory
fuente
1
¡Volver a centrar es definitivamente el camino a seguir!
Floris
2
¿Qué pasa con el uso de coordenadas de punto fijo? ¿Quizás con enteros de 64 bits si es necesario?
API-Beast
la pregunta es, ¿qué tan grande puede ser esa región recentrada? si, por ejemplo, en mi juego quiero disparar a gran distancia con un zoom fuerte, ¿necesito usar el doble o es flotante? ¿No es mejor volver a centrar según un árbol cuádruple o algún algoritmo de mosaico?
jokoon
Dependerá de la estabilidad numérica de los algoritmos empleados por sus sistemas de representación y física, por lo que para una base de código / motor dada, la única forma de saberlo con certeza es probar una escena de prueba. Podemos usar la tabla para estimar la máxima precisión (por ejemplo, una cámara a 16 km de distancia de un objeto tenderá a ver al menos errores de tamaño milimétrico, por lo que su zoom debe ser lo suficientemente ancho como para mantenerlos más pequeños que un píxel, si el zoom necesita para ser más estrictos para su juego, luego se duplica o se pueden requerir algunas matemáticas inteligentes), pero una cadena de operaciones con pérdidas puede tener problemas mucho antes de este límite hipotético.
DMGregory
Todavía me pregunto qué significa volver a centrar el mundo en una API gráfica. Si tengo una gran parte de la geometría instanciada (o no instanciada), ¿es seguro hacerlo? Supongo que significa traducir TODAS las transformaciones, pero si hago esto varias veces, ¿no hay riesgo de pérdida de precisión de coma flotante?
jokoon
3

Es difícil de responder ya que depende de la escala de su física: ¿Cuál es la velocidad de movimiento mínima aceptable que NO necesita redondearse a cero?

Si necesita un mundo grande y física consistente, es mejor usar una clase de puntos fijos.

Por ejemplo, disparar una bala de cañón desde cualquier parte del mundo le dará el mismo resultado y un punto fijo de 64 bits (32.32) le brinda una gran cantidad de precisión y más que nada perceptible en la mayoría de los juegos. Si su unidad está a 1 metro, todavía está a 232 picómetros de precisión a 2147483 km del origen.

Todavía puede hacer la física local en puntos flotantes dentro de la celda local para ahorrar en el trabajo de programación y usar un motor de física listo para usar. Seguirá siendo razonablemente consistente para todos los fines prácticos.

Como un bono de fase amplia y AABB tienden a ser más rápidos en punto fijo debido a la latencia de FPU. También es más rápido convertir un punto fijo en un índice de octree (o quadtree), ya que puede hacer un simple enmascaramiento de bits.

Esas operaciones no se benefician tanto de las instrucciones SIMD y la canalización que normalmente ocultarían la latencia de la FPU.

Puede convertir las posiciones a punto flotante DESPUÉS de restar la posición de la cámara en punto fijo para renderizar todo, evitando problemas de punto flotante en un mundo grande y aún utilizando un renderizador regular usando puntos flotantes.

Stephane Hockenhull
fuente
-3

Puedes evitarlo por completo mediante la multiplicación.
En lugar de trabajar con flotadores, simplemente multiplíquelos por 10 ^ (x), guárdelos y, cuando sea necesario, multiplíquelos nuevamente por 10 ^ (- x).
A partir de eso, depende del tipo de int que quieras usar.

Anon
fuente
2
Esto no evita el problema. Un formato de punto fijo todavía tiene precisión y rango finitos: cuanto mayor es el rango, menor es la precisión (según el lugar donde colocas ese punto decimal fijo), por lo que la decisión de "qué tan grande puedo hacer un nivel sin errores de redondeo visibles" Todavía se aplica.
DMGregory
3
Además, la base 10 no es muy práctica. Los números de punto fijo funcionan mucho mejor cuando divide el número entero en términos de bits (considere 26.6 sin signo: el componente fraccional es el de 6 bits más bajo ( 1.0 / 64.0 * x & 63) y la parte integral es simplemente x >> 6) . Eso es mucho más simple de implementar que elevar algo a una potencia de diez.
Andon M. Coleman