Perché la risoluzione dei numeri in virgola mobile diminuisce ulteriormente da un'origine?


19

La mia scena OpenGL ha oggetti che sono posizionati a distanze ridicolmente lontane dall'origine. Quando visualizzo questi oggetti e eseguo una panoramica / rotazione / zoom di una telecamera attorno ad essi, essi "oscillano". Cioè, i vertici che comprendono gli oggetti sembrano agganciarsi attorno ad una griglia di punti immaginaria 3d. Ho letto che questo è un problema comune a causa della quantità di informazioni che possono essere archiviate usando la precisione in virgola mobile (che OpenGL e praticamente tutto il resto utilizza). Non capisco perché questo accada però.

Durante la ricerca di una soluzione, mi sono imbattuto nella semplice soluzione di "origine mobile" e sembra funzionare. Trasformo tutto in modo che i miei oggetti siano nelle stesse posizioni relative ma qualunque cosa la mia macchina fotografica stia guardando è vicina all'origine. Ho trovato una spiegazione qui: http://floatingorigin.com/ , ma non sono riuscita a seguirla.

Quindi ... Qualcuno potrebbe spiegare perché posizionare la mia scena molto lontano (diciamo 10 milioni di unità) dall'origine provoca il comportamento irregolare che ho osservato? E anche perché spostarlo vicino all'origine risolve il problema?


4
Perché se non lo facessero, sarebbero numeri a virgola fissa . Domanda tautologica, questa.
Salterio

1
Vero, ma solo quando capisci cosa significa veramente "virgola mobile".
Kylotan,

Risposte:


26

Ciò è TUTTO dovuto al modo in cui i punti mobili sono rappresentati nei computer.

I numeri interi sono memorizzati in modo piuttosto semplice; ogni unità è esattamente "una" a parte la "precedente" proprio come ci si aspetterebbe con numeri numerabili.

Con i numeri in virgola mobile questo non è esattamente il caso. Invece, diversi bit indicano l'ESPONENTE, mentre il resto indica ciò che è noto come la mantissa , o parte frazionaria che viene poi MOLTIPLICATA dalla parte esponente (implicitamente 2 ^ exp) per dare il risultato finale.

Guarda qui per una spiegazione visiva dei bit.

È proprio perché questo esponente è una parte reale dei bit che la precisione inizia a svanire una volta che i numeri diventano più grandi.

Per vederlo in azione, facciamo una rappresentazione in virgola mobile senza entrare nella nitidezza: prendiamo un piccolo esponente come 2 e facciamo alcune parti frazionarie per testare:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

...eccetera.

Questi numeri non crescono molto distanti all'unico esponente 2. Ma ora proviamo l'esponente 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Whoa, enorme differenza ora!

L'esempio, pur non andando specificamente alla CONTABILITÀ MOLTO SUCCESSIVA (che sarebbe la parte frazionaria immediatamente successiva a seconda di quanti bit è), è lì per dimostrare la perdita di precisione una volta che i numeri diventano più grandi. L'unità "prossima numerabile" nei float è molto piccola con piccoli esponenti e MOLTO grande con esponenti più grandi, mentre negli interi è SEMPRE 1.

Il motivo per cui il metodo di origine float funziona è perché sta ridimensionando tutti questi numeri in virgola mobile potenzialmente esponente di grandi dimensioni FINO A piccolo esponente in modo che i "prossimi numerabili" (precisione) possano essere molto piccoli e felici.


Gli esempi che hai dato erano davvero illustrativi, grazie :)
Pris,

3
Sulla strada giusta, ma vorrei che tu avessi usato esempi più vicini al modo in cui funziona davvero il virgola mobile. Non innalza la mantissa all'esponente; è mantissa * 2 ^ esponente.
Nathan Reed,

3
Hai ragione, lo sapevo; Non so cosa stavo pensando. Modificato la mia risposta.

1
@ScottW Nice edit! +1
Nathan Reed,

17

Perché i numeri in virgola mobile sono rappresentati come frazione + esponente + segno e hai solo una quantità fissa di bit per la parte della frazione.

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

Man mano che ottieni numeri sempre più grandi, semplicemente non hai i bit per rappresentare le porzioni più piccole.


8

Il classico sul campo deve essere sollevato: ciò che ogni informatico dovrebbe sapere sui numeri in virgola mobile .

Ma la sostanza ha a che fare con il fatto che i numeri a virgola mobile a precisione singola (doppia) siano solo un numero binario a 32 bit (64 bit) con 1 bit che rappresenta il segno, un esponente a 8 bit (11 bit) della base 2 e un significato a 23 bit (52 bit) (le parentesi sono i valori per i doppi).

Ciò significa che il numero positivo più piccolo che è possibile rappresentare con precisione singola è 0,0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .

Il prossimo numero positivo è doppio: 0,0000000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , e quindi il numero successivo è la somma dei due precedenti 0,0000000000000000000011 x 2 -127 = 3 x 2 -149 ~ 4,2 - 45 .

Questo continua ad aumentare della stessa differenza costante fino a: 0.1111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1.17549435 x 10 -38 - 0.00000014 x 10 -38 = 1.17549421 x 10 -38

Ora hai raggiunto i numeri normali (dove la prima cifra nel significato è 1) in particolare: 1.0000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38 e il numero successivo è quindi 1.000000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023.


2

Il motivo per cui i numeri in virgola mobile diventano meno precisi dall'origine è perché si suppone che un numero in virgola mobile sia in grado di rappresentare numeri grandi. Il modo in cui ciò viene fatto gli conferisce il termine "virgola mobile". Suddivide i possibili valori che può assumere (che è determinato dalla sua lunghezza in bit) in modo che ci sia circa lo stesso numero per ciascun esponente: per un float a 32 bit, 23 dei bit definiscono la mantissa o il significato. Quindi sarà in grado di assumere il valore di 2 ^ 23 valori diversi in ciascun intervallo di esponente. Uno di questi intervalli di esponenti è 1-2 [da 2 ^ 0 a 2 ^ 1], quindi suddividere l'intervallo da 1 a 2 in 2 ^ 23 valori diversi consente molta precisione.

Dividere l'intervallo [da 2 ^ 10 a 2 ^ 11] in 2 ^ 23 valori diversi significa che lo spazio tra ciascun valore è molto più grande. Se non lo fosse, allora 23 bit non sarebbero sufficienti. Il tutto è un compromesso: è necessario un numero infinito di bit per rappresentare qualsiasi numero reale. Se l'applicazione funziona in un modo che consente di cavarsela con una precisione inferiore per valori più grandi e di trarre vantaggio dalla possibilità di rappresentare effettivamente valori di grandi dimensioni , si utilizza una rappresentazione in virgola mobile.


sto solo prendendo nota qui dopo un'ulteriore revisione 7 anni dopo ... i miei numeri nei miei esempi non sono particolarmente scelti. Ma i punti complessivi sono validi.
Steven Lu,

1

Può essere un po 'difficile offrire esempi specifici di come funziona la precisione in virgola mobile. Per integrare le altre risposte, eccone una. Diciamo che abbiamo un numero decimale in virgola mobile, con tre cifre di mantissa e una cifra di esponente:

mantissa × 10 esponente

Quando l'esponente è 0, ogni numero intero nell'intervallo 0–999 può essere rappresentato con precisione. Quando è 1, essenzialmente moltiplichi ogni elemento di quell'intervallo per 10, quindi ottieni l'intervallo 0–9990; ma ora, solo i multipli di 10 possono essere rappresentati con precisione, perché hai ancora solo tre cifre di precisione. Quando l'esponente è al massimo di 9, la differenza tra ogni coppia di numeri interi rappresentabili è di un miliardo . Stai letteralmente scambiando precisione per la gamma.

Funziona allo stesso modo con i numeri binari in virgola mobile: ogni volta che l'esponente sale di uno, l'intervallo raddoppia , ma il numero di valori rappresentabili all'interno di quell'intervallo viene dimezzato . Questo vale anche per i numeri frazionari, che è ovviamente la fonte del tuo problema.


0

In generale, la risoluzione peggiora perché la risoluzione viene moltiplicata per il valore esponente (2 ** parte esponente).

in riconoscimento del commento di Josh: quanto sopra è stato solo per mettere la risposta in una dichiarazione succinta. Ovviamente, come ho cercato di indicare su http://floatingorigin.com/ , questo è solo l'inizio di una soluzione globale e il tuo programma potrebbe avere jitter da una serie di punti: nella pipeline di precisione o in altre parti del codice .


Questo non aggiunge nulla che non sia già presente in altre risposte.

Vero: ho capito di poter descrivere la risposta in una sola riga e ho pensato che qualcuno potesse trovare utile una risposta sintetica.
Chris Thorne,

-1

Il buffer di profondità OpenGL non è lineare . Più vai lontano, peggiore sarà la risoluzione. Vi consiglio di leggere questo . Qualcosa preso da lì (12.070):

In sintesi, il divario prospettico, per sua natura, provoca una maggiore precisione Z vicino alla parte anteriore del volume della vista rispetto alla parte posteriore.

E un altro (12.040):

È possibile che i piani di ritaglio zNear e zFar siano stati configurati in modo da limitare notevolmente la precisione del buffer di profondità. In genere, ciò è causato da un valore del piano di ritaglio zNear troppo vicino a 0,0. Man mano che il piano di ritaglio zNear è sempre più vicino a 0,0, la precisione effettiva del buffer di profondità diminuisce drasticamente. Lo spostamento del piano di ritaglio zFar più lontano dall'occhio ha sempre un impatto negativo sulla precisione del buffer di profondità, ma non è così drammatico come lo spostamento del piano di ritaglio zNear.

Quindi dovresti spostare il tuo piano di ritaglio vicino il più lontano possibile e il tuo piano lontano il più vicino possibile.


-1: la domanda riguarda la precisione in virgola mobile, non i problemi di precisione con la rappresentazione del buffer di profondità non lineare.
Nathan Reed,

È possibile che ciò che vedo sia a causa di problemi di buffering approfondito. Sto usando una lib sopra OpenGL per vedere la mia scena, e sto assumendo il presupposto che configuri la telecamera, la vista e i piani di ritaglio vicini e lontani per tenere conto delle dimensioni e della posizione della geometria (dal momento che lo spettatore lo strumento sembra impostare automaticamente una vista ottimale per il contenuto della scena). Ma suppongo che potrebbe non essere il caso: proverò a giocare con i piani di ritaglio lasciando intatta la posizione originale e vedrò cosa succede.
Pris,

Nathan Reed: l'autore ha scritto che ha una scena OpenGL, quindi ho pensato che potesse trattarsi anche di questo problema.
Zacharmarz,

Questo problema può sembrare simile o correlato, ma i valori del buffer di profondità NON sono sicuramente memorizzati in modo compatibile con i numeri in virgola mobile. È un formato a virgola fissa. È per questo che la risposta può essere fuorviante.
Steven Lu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.