2010-04-07 15 views
17

sto sviluppando per una piattaforma senza una libreria matematica, quindi ho bisogno di costruire i miei propri strumenti. Il mio attuale modo di ottenere la frazione è quello di convertire il galleggiante a punto fisso (moltiplicare con (float) 0xFFFF, gettato a int), ottenere solo la parte inferiore (maschera con 0xFFFF) e riconvertirlo in un galleggiante di nuovo.Ottenere la parte frazionaria di un galleggiante senza utilizzare modf()

Tuttavia, l'imprecisione mi sta uccidendo. Sto usando le mie funzioni Frac() e InvFrac() per disegnare una linea anti-alias. Usando modf ottengo una linea perfettamente liscia. Con il mio metodo, i pixel iniziano a saltare a causa della perdita di precisione.

Questo è il mio codice:

const float fp_amount = (float)(0xFFFF); 
const float fp_amount_inv = 1.f/fp_amount; 

inline float Frac(float a_X) 
{ 
    return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv; 
} 

inline float Frac(float a_X) 
{ 
    return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv; 
} 

Grazie in anticipo!

+2

Non dovrebbe fp_amount essere 0x10000 invece di 0xFFFF? –

+1

Santa merda. Fallo una risposta così posso accettarlo! Hai appena risolto il mio intero problema di precisione! – knight666

risposta

35

Se ho capito bene la tua domanda, vuoi solo la parte dopo il decimale giusto? Non ne hai realmente bisogno in una frazione (numeratore e denominatore intero)?

Quindi abbiamo un numero, diciamo 3.14159 e vogliamo finire con solo 0.14159. Supponendo che il nostro numero è memorizzato in float f;, siamo in grado di fare questo:

f = f-(long)f; 

Il che, se inseriamo il nostro numero, funziona così:

0.14159 = 3.14159 - 3; 

Quello che fa è rimuovere la parte intera numero del fluttuare lasciando solo la parte decimale. Quando si converte il float su un valore lungo, si elimina la parte decimale. Quindi, quando sottrai quello dal tuo float originale, ti viene restituito il solo la parte decimale. Abbiamo bisogno di utilizzare un lungo qui a causa della dimensione del tipo float (8 byte sulla maggior parte dei sistemi). Un numero intero (solo 4 byte su molti sistemi) non è necessariamente abbastanza grande da coprire lo stesso intervallo di numeri di uno float, ma dovrebbe essere un long.

+4

Un if ... then ... else ... in una funzione matematica usata spesso come questa? La mia cache, piange! – knight666

+7

Questo è sbagliato quando 'f' è negativo. (Stai aggiungendo due numeri negativi.) Non hai bisogno di "if" affatto: "f = f - (int) f". Se 'f' è negativo, verrà sottratto un negativo negativo arrotondato allo zero. – jamesdlin

+5

Inoltre, si presuppone che la parte intera del float si adatti a un int. – jamesdlin

4

Vorrei raccomandare di dare un'occhiata a come il modf è implementato sui sistemi che si usano oggi. Controlla la versione di uClibc.

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(per motivi legali, sembra essere licenza BSD, ma si sarebbe, ovviamente, vuole raddoppiare controllo)

Alcune delle macro sono definite here.

+0

Perché tutto il cambio di bit, è davvero tanto un guadagno di velocità? O il mio piccolo trucco di conversione int ha qualche problema che mi manca? –

+1

@Daniel Bingham: probabilmente quest'ultimo. I float potrebbero non essere codificati nel modo in cui pensi sulla piattaforma che stai utilizzando, quindi le tue maschere potrebbero essere disattivate. @sharth: il tuo link si basa su alcune macro e ho difficoltà a trovarle. Puoi tentare la fortuna e trovare le definizioni per EXTRACT_WORDS, INSERT_WORDS e GET_HIGH_WORD? – Randolpho

+0

Int per fluttuare va bene. Float to int è terribilmente lento. – knight666

7

Come sospettavo, modf non usufruisce di aritmetica per sé - è tutti i turni e maschere, date un'occhiata here. Non potete usare le stesse idee sulla vostra piattaforma?

+0

Il collegamento è interrotto, puoi tentare di aggiornarlo? –

+0

Wayback machine per il salvataggio: https://web.archive.org/web/20121030234640/http://www.raspberryginger.com/jbailey/minix/html/modf_8c-source.html – Kaganar

2

io non sono completamente sicuro, ma penso che quello che stai facendo è sbagliato, dal momento che si stanno prendendo in considerazione solo la mantissa e dimenticando completamente l'esponente.

È necessario utilizzare l'esponente di spostare il valore nel mantissa per trovare la parte reale intero.

Per una descrizione del meccanismo di memorizzazione di galleggianti a 32 bit, dare un'occhiata a here.

0

Il tuo metodo presuppone che ci siano 16 bit nella parte frazionaria (e come note Mark Ransom significa che devi spostarti di 16 bit, cioè moltiplicare per 0x1000). Potrebbe non essere vero. L'esponente è ciò che determina quanti bit ci sono nella parte frazionaria.

Per inserire questo in una formula, il metodo funziona calcolando (x modf 1.0) come ((x << 16) mod 1<<16) >> 16, ed è quel 16 hardcoded che dovrebbe dipendere dall'esponente - la sostituzione esatta dipende dal formato float.

4

C'è un errore nelle costanti. In pratica stai provando a fare uno spostamento a sinistra del numero di 16 bit, mascherando tutto tranne i bit più bassi, quindi spostati di nuovo di nuovo di 16 bit. Lo spostamento è lo stesso di moltiplicare per una potenza di 2, ma non stai usando una potenza di 2 - stai usando 0xFFFF, che è disattivato di 1. Sostituendo questo con 0x10000, la formula funzionerà come previsto.

1

Perché andare a virgola mobile a tutti per il disegno a tratteggio? Si potrebbe semplicemente attenersi alla versione a virgola fissa e utilizzare invece una routine di disegno a linee integer/a virgola fissa - viene in mente Bresenham. Sebbene questa versione non sia alias, so che ce ne sono altri.

Bresenham's line drawing

+0

Intendi anti-aliasing? –

+0

Per le linee anti-aliasing vedere [Linee Wu] (https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm) – Bim

1

Sembra come forse si vuole questo.

float f = something; 
float fractionalPart = f - floor(f); 
+0

Questo non funziona con i numeri negativi. –

+0

'floor' è anche più lento di un cast. – aledalgrande

0

Un'opzione è utilizzare fmod(x, 1).

Problemi correlati