Trasmetti float to int (bitwise) in C

Dati i 32 bit che rappresentano un numero in virgola mobile IEEE 754, in che modo il numero può essere convertito in un numero intero, utilizzando operazioni integer o bit sulla rappresentazione (anziché utilizzare un’operazione di macchina o un’operazione di compilazione da convertire)?

MODIFICA # 1:

Devo seguire la funzione ma fallisce in alcuni casi:

Input: int x (contiene il numero di precisione singola a 32 bit nel formato IEEE 754)

if(x == 0) return x; unsigned int signBit = 0; unsigned int absX = (unsigned int)x; if (x < 0) { signBit = 0x80000000u; absX = (unsigned int)-x; } unsigned int exponent = 158; while ((absX & 0x80000000) == 0) { exponent--; absX <> 8; unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff); printf("\nfor x: %x, result: %x",x,result); return result; 

MODIFICA # 2:

Anche bisogno di aiuto con: https://cs.stackexchange.com/questions/3484/converting-function-to-bitwise-only

C ha il “sindacato” per gestire questo tipo di visualizzazione dei dati:

 typedef union { int i; float f; } u; u u1; u1.f = 45.6789; /* now u1.i refers to the int version of the float */ printf("%d",u1.i); 

(Qualcuno dovrebbe ricontrollare questa risposta, in particolare i casi di confine e l’arrotondamento dei valori negativi. Inoltre, l’ho scritto per round-to-nearest. Per riprodurre la conversione di C, questo dovrebbe essere cambiato in round-zero.)

Essenzialmente, il processo è:

Separare i 32 bit in un bit di segno ( s ), otto bit di esponente ( e ) e 23 significando bit ( f ). Tratteremo questi come interi a due complementi.

Se e è 255, l’object a virgola mobile è infinito (se f è zero) o un NaN (altrimenti). In questo caso, la conversione non può essere eseguita e deve essere segnalato un errore.

Altrimenti, se e non è zero, aggiungi da 2 24 a f . (Se e non è zero, il significato e implicitamente ha un 1 bit nella parte anteriore. L’aggiunta di 2 24 rende quel bit esplicito in f ).

Sottrai 127 da e . (Questo converte l’esponente dalla sua forma polarizzata / codificata all’esponente effettivo.Se stessimo facendo una conversione generale a qualsiasi valore, dovremmo gestire il caso speciale quando e è zero: Sottrai 126 invece di 127. Ma, poiché stiamo convertendo solo in un risultato intero, possiamo trascurare questo caso, a patto che il risultato intero sia zero per questi piccoli numeri di input).

Se s è 0 (il segno è positivo) e e è 31 o più, allora il valore trabocca un intero con segno a 32 bit (è 2 31 o più grande). La conversione non può essere eseguita e deve essere segnalato un errore.

Se s è 1 (il segno è negativo) e e è maggiore di 31, il valore trabocca un intero con segno a 32 bit (è minore o uguale a -2 32 ). Se s è uno, e è 32, e f è maggiore di 2 24 (è stato impostato uno qualsiasi dei bit e del significato originale), il valore trabocca un intero con segno a 32 bit (è inferiore a -2 31 ; se l’originale f erano zero, sarebbe esattamente -2 31 , che non trabocca). In nessuno di questi casi, la conversione non può essere eseguita e deve essere segnalato un errore.

Ora abbiamo un s , un e e un f per un valore che non trabocca, quindi possiamo preparare il valore finale.

Se s è 1, imposta f su -f .

Il valore dell’esponente è per un valore compreso tra 1 (incluso) e 2 (esclusivo), ma il nostro significato e inizia con un bit a 2 24 . Quindi dobbiamo adattarci per questo. Se e è 24, il nostro significato e è corretto, e abbiamo finito, quindi restituisci f come risultato. Se e è maggiore di 24 o meno di 24, dobbiamo spostare il significato e in modo appropriato. Inoltre, se sposteremo a destra, potremmo dover arrotondarlo per ottenere un risultato arrotondato al numero intero più vicino.

Se e è maggiore di 24, sposta f left e -24 bit. Restituisci f come risultato.

Se e è inferiore a -1, il numero a virgola mobile è compreso tra -½ e ½, esclusivo. Restituisci 0 come risultato.

Altrimenti, sposteremo f a destra 24 bit. Tuttavia, prima salviamo i bit di cui abbiamo bisogno per l’arrotondamento. Impostare r sul risultato del cast di un intero a 32 bit senza segno e spostarlo a sinistra di 32- (24- e ) bit (equivalentemente, lasciato da 8+ e bit). Questo prende i bit che saranno spostati da f (sotto) e “left” li aggiusta nei 32 bit, quindi abbiamo una posizione fissa da cui partono.

Shift f a destra 24 bit.

Se r è inferiore a 2 31 , non fare nulla (questo è un arrotondamento verso il basso, il turno ha troncato i bit). Se r è maggiore di 2 31 , aggiungi da uno a f (questo è un arrotondamento). Se r equivale a 2 31 , aggiungi il bit basso di f a f . (Se f è dispari, aggiungi uno a F. Dei due valori ugualmente vicini, questo arrotonda al valore pari.) Return f .

&x fornisce l’indirizzo di x quindi ha il tipo float* .

(int*)&x cast quel puntatore a un puntatore a int ie a una cosa int* .

*(int*)&x dereference quel puntatore in un valore int . Non farà ciò che credi sulle macchine in cui int e float hanno dimensioni diverse.

E potrebbero esserci problemi di endianità.

Questa soluzione è stata utilizzata nell’algoritmo della radice quadrata inversa rapida .

Non è ansible (significativamente) convertire un numero in virgola mobile in un ‘numero intero’ ( signed int o int ) in questo modo.

Potrebbe finire per avere il tipo intero, ma in realtà è solo un indice nello spazio di codifica di IEEE754, non un valore significativo in sé.

Si potrebbe argomentare che un int unsigned un duplice scopo come un pattern a bit e un valore intero, ma int no.


Inoltre ci sono problemi di piattaforma con la manipolazione di bit di firme.

 float x = 43.133; int y; assert (sizeof x == sizeof y); memcpy (&y, &x, sizeof x); ... 

Puoi lanciare il float usando un riferimento. Un cast come questo non dovrebbe mai generare alcun codice.

C ++

 float f = 1.0f; int i = (int &)f; printf("Float %f is 0x%08x\n", f, i); 

Produzione:

 Float 1.000000 is 0x3f800000 

Se vuoi cast in stile c ++ usa reinterpret_cast, come questo.

 int i = reinterpret_cast(f); 

Non funziona con le espressioni, devi memorizzarlo in una variabile.

  int i_times_two; float f_times_two = f * 2.0f; i_times_two = (int &)f_times_two; i_times_two = (int &)(f * 2.0f); main.cpp:25:13: error: C-style cast from rvalue to reference type 'int &'