funzione per convertire float in int (interi enormi)

Questa è una domanda universitaria. Solo per essere sicuri 🙂 Dobbiamo implementare (float) x

Ho il seguente codice che deve convertire l’intero x nella sua rappresentazione binaria in virgola mobile memorizzata in un numero intero senza segno.

unsigned float_i2f(int x) { if (!x) return x; /* get sign of x */ int sign = (x>>31) & 0x1; /* absolute value of x */ int a = sign ? ~x + 1 : x; /* calculate exponent */ int e = 0; int t = a; while(t != 1) { /* divide by two until t is 0*/ t >>= 1; e++; }; /* calculate mantissa */ int m = a <> 9) & ~(((0x1 <> 9 << 1)); /* add bias for 32bit float */ e += 127; int res = sign << 31; res |= (e << 23); res |= m; /* lots of printf */ return res; } 

Un problema che ho riscontrato ora è che quando i miei numeri interi sono troppo grandi, il mio codice fallisce. Ho implementato questa procedura di controllo:

 float f = (float)x; unsigned int r; memcpy(&r, &f, sizeof(unsigned int)); 

Questo ovviamente produce sempre l’output corretto.

Ora quando eseguo alcune prove, questo è il mio risultato (GOAL è quello che deve essere, risultato è quello che ho ottenuto)

 :!make && ./btest -f float_i2f -1 0x80004999 make: Nothing to be done for `all'. Score Rating Errors Function x: [-2147464807] 10000000000000000100100110011001 sign: 1 expone: 01001110100000000000000000000000 mantis: 00000000011111111111111101101100 result: 11001110111111111111111101101100 GOAL: 11001110111111111111111101101101 

Quindi in questo caso, viene aggiunto un 1 come LSB.

Il prossimo caso:

 :!make && ./btest -f float_i2f -1 0x80000001 make: Nothing to be done for `all'. Score Rating Errors Function x: [-2147483647] 10000000000000000000000000000001 sign: 1 expone: 01001110100000000000000000000000 mantis: 00000000011111111111111111111111 result: 11001110111111111111111111111111 GOAL: 11001111000000000000000000000000 

Qui 1 è aggiunto all’esponente mentre la mantissa è il complemento di esso.

Ho provato ore a guardare ip su internet e nei miei libri, ecc. Ma non riesco a trovare alcun riferimento a questo problema. Immagino che abbia qualcosa a che fare con il fatto che la mantissa ha solo 23 bit. Ma come devo gestirlo allora?

EDIT: QUESTA PARTE È OBSOLETA GRAZIE AI COMMENTI QUI SOTTO. int l deve essere senza firma l.

 int x = 2147483647; float f = (float)x; int l = f; printf("l: %d\n", l); 

allora l diventa -2147483648.

Come può accadere? Quindi C sta facendo il casting sbagliato?

Spero che qualcuno possa aiutarmi qui! Thx Markus

MODIFICA 2:

Il mio codice aggiornato ora è questo:

 unsigned float_i2f(int x) { if (x == 0) return 0; /* get sign of x */ int sign = (x>>31) & 0x1; /* absolute value of x */ int a = sign ? ~x + 1 : x; /* calculate exponent */ int e = 158; int t = a; while (!(t >> 31) & 0x1) { t <> 8) & ~(((0x1 <> 8 << 1)); m &= 0x7fffff; int res = sign << 31; res |= (e << 23); res |= m; return res; } 

Ho anche capito che il codice funziona per tutti gli interi nell’intervallo -2 ^ 24, 2 ^ 24. Tutto sopra / sotto a volte funziona, ma per lo più no.

Manca qualcosa, ma non ho davvero idea di cosa. Qualcuno può aiutarmi?

La risposta stampata è assolutamente corretta in quanto dipende totalmente dalla rappresentazione sottostante dei numeri che vengono lanciati. Tuttavia, se comprendiamo la rappresentazione binaria del numero, non sarai sorpreso da questo risultato.

Per capire una conversione implicita è associata all’operatore di assegnazione (ref C99 Standard 6.5.16). Lo standard C99 prosegue dicendo:

6.3.1.4 Reale floating e intero Quando un valore finito del tipo floating reale viene convertito in un tipo intero diverso da _Bool, la parte frazionaria viene scartata (ovvero il valore viene troncato verso zero). Se il valore della parte integrale non può essere rappresentato dal tipo intero, il comportamento non è definito.

L’esempio precedente illustra un comportamento non definito dovuto all’assegnazione di un valore al di fuori dell’intervallo del tipo di destinazione. Cercando di assegnare un valore negativo a un tipo senza segno, non convertendo il numero in virgola mobile in numero intero.

L’asserzione nel seguente frammento dovrebbe impedire che si verifichino comportamenti indefiniti.

 #include  #include  unsigned int convertFloatingPoint(double v) { double d; assert(isfinite(v)); d = trunc(v); assert((d>=0.0) && (d<=(double)UINT_MAX)); return (unsigned int)d; } 

Un altro modo per fare la stessa cosa, Creare un unione contenente un numero intero a 32 bit e un float. L'int e il float ora sono solo modi diversi di guardare lo stesso bit di memoria;

 union { int myInt; float myFloat; } my_union; my_union.myInt = 0x BFFFF2E5; printf("float is %f\n", my_union.myFloat); float is -1.999600 

Stai dicendo al compilatore di prendere il numero che hai (numero intero grande) e renderlo in un float, per non interpretare il numero AS float. Per fare ciò, devi dire al compilatore di leggere il numero da quell'indirizzo in una forma diversa, quindi questo:

 myFloat = *(float *)&myInt ; 

Ciò significa che se lo smontiamo, partendo da destra:

  • & myInt: la posizione in memoria che contiene il numero intero.
  • (float *) - davvero, voglio che il compilatore usi questo come un puntatore per fluttuare, non come pensa il compilatore.
  • * - leggi dall'indirizzo di ciò che è a destra.
  • myFloat = - imposta questa variabile su qualunque cosa sia a destra.

Quindi stai dicendo al compilatore: Nella posizione di (myInt), c'è un numero in virgola mobile, ora lo metto in float in myFloat.