¿Por qué la resolución de los números de coma flotante disminuye aún más desde un origen?


19

Mi escena OpenGL tiene objetos que se colocan a distancias ridículamente lejanas del origen. Cuando veo estos objetos, y desplazo / giro / acerco una cámara a su alrededor, 'vibran'. Es decir, los vértices que comprenden los objetos parecen romperse alrededor de una grilla imaginaria de puntos en 3D. He leído que este es un problema común debido a la cantidad de información que se puede almacenar con precisión de punto flotante (que OpenGL, y casi todo lo demás usa). Sin embargo, no entiendo por qué sucede esto.

Al buscar una solución, me encontré con la solución muy simple de 'origen flotante', y parece funcionar. Simplemente transformo todo para que mis objetos estén en las mismas posiciones relativas, pero cualquier cosa que mi cámara esté mirando está cerca del origen. Encontré una explicación aquí: http://floatingorigin.com/ , pero no pude seguirla.

Entonces ... ¿Podría alguien explicar por qué posicionar mi escena muy lejos (digamos 10 millones de unidades) del origen da como resultado el comportamiento errático que observé? ¿Y también por qué acercarlo al origen soluciona el problema?


44
Porque si no lo hicieran, serían números de punto fijo . Pregunta tautológica, esta.
MSalters

1
Es cierto, pero solo cuando entiendes lo que significa realmente 'punto flotante'.
Kylotan

Respuestas:


26

Todo esto se debe a la forma en que se representan los puntos flotantes en las computadoras.

Los enteros se almacenan de forma bastante sencilla; cada unidad es exactamente "una" aparte de la "anterior" tal como cabría esperar con números contables.

Con números de coma flotante, este no es exactamente el caso. En cambio, varios bits indican el EXPONENTE, y el resto indica lo que se conoce como mantisa , o parte fraccionaria que luego es MULTIPLICADA por la parte exponente (implícitamente 2 ^ exp) para dar el resultado final.

Busque aquí una explicación visual de los bits.

Precisamente debido a que este exponente es una parte real de los bits, la precisión comienza a GANAR una vez que los números crecen.

Para ver esto en acción, hagamos una representación de punto flotante falso sin entrar en el meollo del asunto: tome un pequeño exponente como 2 y haga algunas partes fraccionarias para probar:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

... etc.

Estos números no crecen muy separados en el único exponente 2. Pero ahora intentemos con el exponente 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Whoa, gran diferencia ahora!

El ejemplo, aunque no va específicamente al MUY PRÓXIMO CONTABLE (que sería la siguiente parte fraccionaria dependiendo de cuántos bits sean), está ahí para demostrar la pérdida de precisión una vez que los números crecen. La unidad "siguiente contable" en flotadores es muy pequeña con exponentes pequeños y MUY grande con exponentes más grandes, mientras que en enteros SIEMPRE es 1.

La razón por la que funciona el método de origen flotante es porque está escalando todos estos números de punto flotante de exponente potencialmente grande DOWN TO small-exponent para que los "próximos contables" (precisión) puedan ser muy pequeños y felices.


Los ejemplos que diste fueron realmente ilustrativos, gracias :)
Pris

3
En el camino correcto, pero desearía que hubieras usado ejemplos más cercanos a la forma en que realmente funciona el punto flotante. No eleva la mantisa al exponente; es mantisa * 2 ^ exponente.
Nathan Reed

3
Tienes razón, lo sabía; No sé lo que estaba pensando. Edité mi respuesta.

1
@ScottW ¡Buena edición! +1
Nathan Reed

17

Debido a que los números de coma flotante se representan como fracción + exponente + signo, y solo tiene una cantidad fija de bits para la parte de fracción.

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

A medida que obtiene números cada vez más grandes, simplemente no tiene los bits para representar las porciones más pequeñas.


8

Debe mencionarse el clásico en el campo: lo que todo informático debe saber sobre los números de coma flotante .

Pero lo esencial tiene que ver con cómo los números de coma flotante de precisión simple (doble) son solo un número binario de 32 bits (64 bits) con 1 bit que representa el signo, un exponente de 8 bits (11 bits) de la base 2 , y un significado de 23 bits (52 bits) (los paréntesis son los valores para dobles).

Eso significa que el número positivo más pequeño que puede representar con precisión simple es 0.0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1.40 x 10 -45 .

El siguiente número positivo es el doble que: 0.0000000000000000000010 x 2 -127 = 2 -148 ~ 2.80 x 10 -45 , y luego el siguiente número es la suma de los dos anteriores 0.0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4.2 - 45 .

Esto continúa aumentando por la misma diferencia constante hasta: 0.1111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1.17549435 x 10 -38 - 0.00000014 x 10 -38 = 1.17549421 x 10 -38

Ahora que ha llegado a los números normales (donde el primer dígito en la mantisa es 1), específicamente: 1.0000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 y el siguiente número es entonces 1.0000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023.


2

La razón por la cual los números de punto flotante se vuelven menos precisos más lejos del origen es porque se supone que un número de punto flotante puede representar números grandes. La forma en que se hace esto le da el término "punto flotante". Divide los posibles valores que puede tomar (que está determinado por su longitud de bits) para que haya aproximadamente el mismo número para cada exponente: para un flotante de 32 bits, 23 de los bits definen la mantisa o significado. Por lo tanto, podrá tomar el valor de 2 ^ 23 valores diferentes en cada rango de exponente. Uno de estos rangos de exponente es 1-2 [2 ^ 0 a 2 ^ 1], por lo que dividir el rango 1 a 2 en 2 ^ 23 valores diferentes permite mucha precisión.

Pero dividir el rango [2 ^ 10 a 2 ^ 11] en 2 ^ 23 valores diferentes significa que el espacio entre cada valor es mucho mayor. Si no fuera así, entonces 23 bits no serían suficientes. Todo es un compromiso: necesita un número infinito de bits para representar cualquier número real. Si su aplicación funciona de una manera que le permite escapar con menor precisión para valores más grandes, y se beneficia de poder representar valores grandes , entonces utiliza una representación de punto flotante.


solo haciendo una nota aquí después de una revisión adicional 7 años después ... mis números en mis ejemplos no están particularmente bien elegidos. Pero los puntos generales son válidos.
Steven Lu

1

Puede ser un poco difícil ofrecer ejemplos específicos de cómo funciona la precisión de punto flotante. Para complementar las otras respuestas, aquí hay una. Digamos que tenemos un número decimal de coma flotante, con tres dígitos de mantisa y un dígito de exponente:

mantisa × 10 exponente

Cuando el exponente es 0, cada número entero en el rango 0–999 se puede representar con precisión. Cuando es 1, esencialmente estás multiplicando cada elemento de ese rango por 10, por lo que obtienes el rango 0–9990; pero ahora, solo se pueden representar con precisión múltiplos de 10, ya que solo tiene tres dígitos de precisión. Cuando el exponente está en su máximo de 9, la diferencia entre cada par de enteros representables es mil millones . Literalmente, está cambiando la precisión por el rango.

Funciona de la misma manera con números binarios de punto flotante: cada vez que el exponente aumenta en uno, el rango se duplica , pero el número de valores representables dentro de ese rango se reduce a la mitad . Esto también se aplica a los números fraccionarios, que por supuesto es la fuente de su problema.


0

En general, la resolución empeora porque la resolución se multiplica por el valor del exponente (2 ** parte del exponente).

en reconocimiento del comentario de josh: lo anterior fue solo para poner la respuesta en una declaración sucinta. Por supuesto, como he tratado de indicar en http://floatingorigin.com/ , esto solo está comenzando hacia una solución general y su programa podría tener fluctuaciones de varios lugares: en la tubería de precisión u otras partes del código .


Esto no agrega nada que no esté presente en otras respuestas.

Cierto: me di cuenta de que podía describir la respuesta en una sola línea y pensé que alguien podría encontrar útil una respuesta sucinta.
Chris Thorne

-1

El tampón de profundidad OpenGL no es lineal . Cuanto más lejos vayas, peor resolución tiene. Recomiendo leer esto . Algo tomado de allí (12.070):

En resumen, la división de perspectiva, por su naturaleza, causa más precisión Z cerca del frente del volumen de la vista que cerca del reverso.

Y otro (12.040):

Es posible que haya configurado sus planos de recorte zNear y zFar de una manera que limite severamente la precisión de su búfer de profundidad. En general, esto es causado por un valor de plano de recorte zNear que está demasiado cerca de 0.0. A medida que el plano de recorte zNear se establece cada vez más cerca de 0.0, la precisión efectiva del búfer de profundidad disminuye drásticamente. Mover el plano de recorte zFar más lejos del ojo siempre tiene un impacto negativo en la precisión del buffer de profundidad, pero no es tan dramático como mover el plano de recorte zNear.

Por lo tanto, debe mover su plano de recorte cercano lo más lejos que pueda y su plano lejano lo más cerca que pueda.


-1: la pregunta es acerca de la precisión de punto flotante, no los problemas de precisión con la representación de búfer de profundidad no lineal.
Nathan Reed

Es posible que lo que estoy viendo se deba a problemas de almacenamiento en profundidad. Estoy usando una biblioteca en la parte superior de OpenGL para ver mi escena, y estoy asumiendo que está configurando la cámara, la vista y los planos de recorte cercanos y lejanos para tener en cuenta el tamaño y la posición de la geometría (desde el espectador la herramienta parece establecer automáticamente una vista óptima para el contenido de la escena). Pero supongo que este puede no ser el caso: intentaré jugar con los planos de recorte dejando la posición original intacta y veré qué sucede.
Pris

2Nathan Reed: El autor escribió que tiene una escena OpenGL, así que pensé que podría ser este problema también.
zacharmarz

Este problema puede parecer similar o relacionado, pero los valores del búfer de profundidad definitivamente NO se almacenan de manera compatible con los números de coma flotante. Es un formato de punto fijo. Es por esto que la respuesta puede ser engañosa.
Steven Lu
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.