Come eseguire un’operazione bit a bit sui numeri in virgola mobile

Ho provato questo:

float a = 1.4123; a = a & (1 << 3); 

Ricevo un errore del compilatore che dice che l’operando di & non può essere di tipo float.

Quando io faccio:

 float a = 1.4123; a = (int)a & (1 << 3); 

Ottengo il programma in esecuzione. L’unica cosa è che l’operazione bit a bit viene eseguita sulla rappresentazione intera del numero ottenuto dopo l’arrotondamento.

Non è inoltre consentito quanto segue.

 float a = 1.4123; a = (void*)a & (1 << 3); 

Non capisco perché int possa essere lanciato per void* ma non float .

Lo sto facendo per risolvere il problema descritto in Stack Overflow question Come risolvere equazioni lineari usando un algoritmo genetico? .

A livello di lingua, non esiste un’operazione come “bitwise sui numeri in virgola mobile”. Le operazioni bit a bit in C / C ++ funzionano sulla rappresentazione del valore di un numero. E la rappresentazione del valore dei numeri in virgola mobile non è definita in C / C ++. I numeri in virgola mobile non hanno bit a livello di rappresentazione del valore, motivo per cui non è ansible applicare operazioni bit a bit su di essi.

Tutto quello che puoi fare è analizzare il contenuto in bit della memoria grezza occupata dal numero in virgola mobile. Per questo è necessario utilizzare un unione come suggerito di seguito o (equivalentemente, e solo in C ++) reinterpretare l’object in virgola mobile come una matrice di oggetti unsigned char , come in

 float f = 5; unsigned char *c = reinterpret_cast(&f); // inspect memory from c[0] to c[sizeof f - 1] 

E per favore, non provare a reinterpretare un object float come object int , come suggeriscono altre risposte. Ciò non ha molto senso, è illegale e non è garantito che funzioni nei compilatori che seguono regole di aliasing rigoroso nell’ottimizzazione. L’unico modo legale per ispezionare il contenuto della memoria in C ++ è reinterpretarlo come una matrice di caratteri [signed/unsigned] char .

Si noti inoltre che tecnicamente non è garantito che la rappresentazione in virgola mobile sul proprio sistema sia IEEE754 (anche se in pratica lo è a meno che non si consenta esplicitamente di non esserlo, e quindi solo rispetto a -0.0, ± infinity e NaN).

Se stai cercando di cambiare i bit nella rappresentazione in virgola mobile, potresti fare qualcosa di simile a questo:

 union fp_bit_twiddler { float f; int i; } q; qf = a; qi &= (1 << 3); a = qf; 

Come osserva AndreyT, l'accesso a un'unione come questa richiama un comportamento indefinito, e il compilatore potrebbe far crescere le braccia e strangolarti. Fai invece quello che suggerisce.

 float a = 1.4123; unsigned int* inta = reinterpret_cast(&a); *inta = *inta & (1 << 3); 

Dai uno sguardo a quanto segue. Ispirato alla radice quadrata inversa veloce:

 #include  using namespace std; int main() { float x, td = 2.0; int ti = *(int*) &td; cout << "Cast int: " << ti << endl; ti = ti>>4; x = *(float*) &ti; cout << "Recast float: " << x << endl; return 0; } 

@mobrule:

Meglio:

 #include  ... union fp_bit_twiddler { float f; uint32_t u; } q; /* mutatis mutandis ... */ 

Per questi valori int sarà probabilmente ok, ma in generale, dovresti usare unsigned ints per lo shift di bit per evitare gli effetti dei turni aritmetici. E uint32_t funzionerà anche su sistemi i cui interi non sono a 32 bit.

L’implementazione Python nelle operazioni bitwise in virgola mobile (ricetta Python) delle operazioni bitwise in virgola mobile funziona rappresentando i numeri in binario che si estende all’infinito a sinistra e a destra dal punto frazionario. Dato che i numeri in virgola mobile hanno uno zero firmato sulla maggior parte delle architetture, usa il complemento degli uni per rappresentare i numeri negativi (beh, in realtà fa solo finta di farlo e usa qualche trucco per ottenere l’aspetto).

Sono sicuro che può essere adattato per funzionare in C ++, ma bisogna fare attenzione a non lasciare che i turni giusti trabocchino quando si equalizzano gli esponenti.

Gli operatori bit a bit NON dovrebbero essere usati sui float, poiché i float sono specifici dell’hardware, indipendentemente dalla somiglianza con qualunque hardware possiate avere. Su quale progetto / lavoro vuoi rischiare “bene ha funzionato sulla mia macchina”? Invece, per C ++, è ansible ottenere un “feel” simile per gli operatori di shift di bit sovraccaricando l’operatore di stream su un wrapper “object” per un float:

 // Simple object wrapper for float type as templates want classs. class Float { float m_f; public: Float( const float & f ) : m_f( f ) { } operator float() const { return m_f; } }; float operator>>( const Float & left, int right ) { float temp = left; for( right; right > 0; --right ) { temp /= 2.0f; } return temp; } float operator<<( const Float & left, int right ) { float temp = left; for( right; right > 0; --right ) { temp *= 2.0f; } return temp; } int main( int argc, char ** argv ) { int a1 = 40 >> 2; int a2 = 40 << 2; int a3 = 13 >> 2; int a4 = 256 >> 2; int a5 = 255 >> 2; float f1 = Float( 40.0f ) >> 2; float f2 = Float( 40.0f ) << 2; float f3 = Float( 13.0f ) >> 2; float f4 = Float( 256.0f ) >> 2; float f5 = Float( 255.0f ) >> 2; } 

Avrai un resto, che potrai buttare via in base alla tua implementazione desiderata.

 float a = 1.4123; int *b = (int *)&a; *b = *b & (1 << 3); // a is now the IEEE floating-point value caused by the manipulation of *b // equals 1.121039e-44 (tested on my system) 

Questo è simile alla risposta di Justin, tranne per il fatto che crea solo una vista dei bit negli stessi registri di a . Quindi, quando si manipola *b , il valore di a valore cambia di conseguenza.

FWIW, esiste un vero caso d’uso per operazioni bit-like su virgola mobile (l’ho appena eseguito di recente) – shader scritti per GPU che supportano solo versioni precedenti di GLSL (1.2 e precedenti non supportano operatori bit-wise) e dove ci sarebbe una perdita di precisione se i float fossero convertiti in ints.

Le operazioni bit-saggio possono essere implementate su numeri in virgola mobile usando remainders (modulo) e controlli di disuguaglianza. Per esempio:

 float A = 0.625; //value to check; ie, 160/256 float mask = 0.25; //bit to check; ie, 1/4 bool result = (mod(A, 2.0 * mask) >= mask); //non-zero if bit 0.25 is on in A 

Quanto sopra presuppone che A sia compreso tra [0..1) e che ci sia un solo “bit” in maschera da controllare, ma potrebbe essere generalizzato per casi più complessi.

Questa idea è basata su alcune delle informazioni trovate in is-it-possible-to-implement-bitwise-operators-using-integer-arithmetic

Se non c’è nemmeno una funzione mod incorporata, allora può anche essere implementata abbastanza facilmente. Per esempio:

 float mod(float num, float den) { return num - den * floor(num / den); }