2009-07-13 20 views

risposta

2

Intendi i bit impostati nella rappresentazione a precisione singola IEEE-754 di un numero? In tal caso, esegui il cast su int (sia float che int sono a 32 bit di larghezza) e esegui un numero di bit regolare: SO question #109023.

+2

La trasmissione a int troncerà e convertirà, non ti darà la rappresentazione grezza. – bdonlan

+2

Buon punto. Quindi una soluzione migliore sarebbe quella di unirlo o di galleggiare f; * (int *) & f –

+2

Penso che Adrian volesse dire gettare il puntatore, non il valore. –

4

Se si vuole lavorare sulla rappresentazione bit a bit reale di un numero in virgola mobile, si dovrebbe fare qualcosa di simile:

float f; /* whatever your float is */ 
int i = *(int *)&f; 

Quello che fa è prendere l'indirizzo del f con l'operatore di indirizzo, &. Questo indirizzo è di tipo float *, un puntatore a un float. Quindi lo ricama con (int *), che dice "fai finta che questo puntatore non punti più a float, ma ora punta a uno int". Si noti che non cambia affatto il valore a f. Quindi l'ultimo * (o prima, dal momento che leggiamo da destra a sinistra) dereferenzia questo puntatore, che è un puntatore a un int, e quindi restituisce un intero int, a.k.a. con la stessa rappresentazione bitwise del float.

di fare il contrario (convertire e int i di nuovo ad un float f), fare il contrario:

f = *(float *)&i; 

Se non erro, questa operazione non è definito dallo standard C, ma probabilmente funzionerà sulla maggior parte dei computer e compilatori. Non è definito perché ritengo che la reale rappresentazione in virgola mobile dei numeri sia dipendente dall'implementazione e possa essere lasciata alla CPU o al compilatore e quindi il valore di i è quasi impossibile da prevedere dopo questa operazione (lo stesso vale per il valore di f nell'operazione inversa). È notoriamente usato nella funzione di radice quadrata inversa di John Carmack per lo stesso scopo nefasto.

Ad ogni modo, se lo fai in codice reale, dovresti probabilmente fermarti a riflettere due volte su cosa stai cercando di fare e perché stai usando float s per farlo. Tuttavia, se lo fai solo per curiosità, o se hai pensato a questi e sei sicuro del tuo design e dei tuoi metodi, fallo.

Sono portato a credere che tu sappia già come contare il numero di bit impostato in un numero intero regolare, poiché questo è un compito molto più semplice. Se non lo sai, il tuo compilatore (o il linguaggio C, non lo so nemmeno io) potrebbe avere una funzione per contare i bit, o potresti usare qualcosa dal meraviglioso sito web Bit-Twiddling Hacks, che ha modi per fare cose come questa con operazioni bit a bit (che dovrebbero essere piuttosto veloci).

+1

Non è definito non solo a causa di ciò - GCC (in alcune configurazioni) avviserà ** il puntatore con puntatore tipo dereferencing interromperà le regole di aliasing rigoroso ** con una buona ragione, poiché in questo modo si rompe l'ipotesi (da ISO C che benefici prestazioni) che due puntatori a tipi distinti semplici (diversi da 'char') non puntano mai allo stesso oggetto. – ephemient

+0

Dove sono i due indicatori qui? – sigjuice

+0

'& f' e' (int *) & f' – ephemient

6
#include <stdio.h> /* for printf() */ 
#include <limits.h> /* for CHAR_BIT */ 

int main(void) { 
    /* union method */ 
    { 
    /* a union can only be initialized for the first option in the union */ 
    union { float f; char cs[sizeof(float)]; } const focs = { 1.0 }; 
    int j,k; 
    int count = 0; 
    for (j = 0; j < sizeof(float); j++) 
    { 
     char const byte = focs.cs[j]; 
     for (k = 0; k < CHAR_BIT; k++) 
     { 
     if ((1 << k) & byte) 
     { 
      count++; 
     } 
     } 
    } 
    printf("count(%2.1f) = %d\n", focs.f, count); 
    } 
    /* cast method */ 
    { 
    float const f = 2.5; 
    int j,k; 
    int count = 0; 
    for (j = 0; j < sizeof(float); j++) 
    { 
     char const byte = ((char *)&f)[j]; 
     for (k = 0; k < CHAR_BIT; k++) 
     { 
     if ((1 << k) & byte) 
     { 
      count++; 
     } 
     } 
    } 
    printf("count(%2.1f) = %d\n", f, count); 
    } 
    return 0; 
} 
+0

Ho dimenticato l'hack del sindacato! Ma dovresti usare sizeof (float) invece di sizeof (int). Inoltre, int e float hanno le stesse dimensioni sulla maggior parte dei sistemi, ma non riesco a trovare nessuno standard che dica qualcosa sulle loro dimensioni relative (o qualcosa sulle dimensioni float), quindi non posso esserne sicuro - ma non sarebbe più bello avere solo 1 ciclo for() e usare invece un'unione di float e int? –

+0

Questa è la cosa - penso che per quanto riguarda l'implementazione, sono quasi sempre gli stessi, ma non conosco nessuno standard che detta così, ed è per questo che l'ho fatto in questo modo. Potrei ancora comprimere i due loop facendo 'int i; int count = 0; for (i = 0; i <8 * sizeof (float); i ++) {if ((1 << (i% 8)) & focs.cs [i/8]) {count ++; }} ' – rampion

+0

Anche se con questo (http://stackoverflow.com/questions/881894/is-char-guaranteed-to-be-exactly-8-bit-long-in-c) dovrei usare' CHAR_BIT' invece di 8 (anche se sono sicuro assumendo 'sizeof (char) == 1', dato che è per definizione). – rampion

3

una bella funzione per il conteggio impostati i bit in un intero di cui parla la prima risposta:

int NumberOfSetBits(int i) 
{ 
    i = i - ((i >> 1) & 0x55555555); 
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 
    return ((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 
} 

di utilizzarlo sul galleggiante si dovrebbe fare qualcosa di simile:

//... 
float f; 
//... 
int numBitsOfF = NumberOfSetBits(*(int*) &f); 
+0

Grazie mille a tutti per il supporto –

-1
The following function will find the number of bits in a 32-bit number. Just type case your float with integer and call this function by a cast 
float f=3.14f; 
count_bits(*(int *)&f); 

int count_bits(int v) 
{ 
    // count the number of bits set in v 
    int c; // c accumulates the total bits set in v 
    int b=v; 
    for (c = 0; v; c++) 
    { 
      v &= v - 1; // clear the least significant bit set 
    } 
    //printf("No of bits in %d is %d\n",b,c); 
    return c; 
} 
Problemi correlati