Coulée float to int (bitwise) en C

Étant donné que les 32 bits représentent un nombre à virgule flottante IEEE 754, comment le nombre peut-il être converti en un entier, en utilisant des opérations entières ou en bits sur la représentation (plutôt que d’utiliser une instruction machine ou une opération de compilation à convertir)?

EDIT # 1:

Je dois suivre la fonction mais cela échoue dans certains cas:

Entrée: int x (contient un nombre simple précision de 32 bits au format 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; 

EDIT # 2:

Vous avez également besoin d’aide avec: https://cs.stackexchange.com/questions/3484/converting-function-to-bitwise-only

C a “l’union” pour gérer ce type d’affichage de données:

 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); 

(Quelqu’un devrait revérifier cette réponse, en particulier les cas-frontières et l’arrondi des valeurs négatives. De plus, je l’ai écrite pour arrondir au plus proche. Pour reproduire la conversion de C, il convient de passer à arrondir vers zéro.)

Le processus consiste essentiellement à:

Séparez les 32 bits en un ( 1 ) bit de signe, huit bits d’exposant ( e ) et 23 bits de significande ( f ). Nous les traiterons comme des entiers à deux compléments.

Si e vaut 255, l’object en virgule flottante est soit l’infini (si f est égal à zéro), soit un NaN (sinon). Dans ce cas, la conversion ne peut pas être effectuée et une erreur doit être signalée.

Sinon, si e n’est pas nul, ajoutez 2 24 à f . (Si e n’est pas zéro, le significande a implicitement un bit de 1. Ajouter 2 24 rend ce bit explicite dans f .)

Soustrayez 127 de e . (Ceci convertit l’exposant de sa forme biaisée / codée en exposant réel. Si nous procédions à une conversion générale en une valeur quelconque, nous devrions gérer le cas spécial où e est égal à zéro: soustrayez 126 au lieu de 127. convertissons uniquement en un résultat entier, nous pouvons négliger ce cas, tant que le résultat entier est égal à zéro pour ces minuscules nombres.)

Si s est égal à 0 (le signe est positif) et e à 31 ou plus, la valeur déborde d’un nombre entier signé de 32 bits (supérieur ou égal à 2 31 ). La conversion ne peut pas être effectuée et une erreur doit être signalée.

Si s est 1 (le signe est négatif) et e est supérieur à 31, alors la valeur déborde d’un nombre entier signé de 32 bits (inférieure ou égale à -2 32 ). Si s est un, e est 32 et f est supérieur à 2 24 (l’un quelconque des bits significatifs d’origine a été défini), la valeur dépasse alors un entier signé de 32 bits (inférieur à -2 31 ; si le f d’ origine étaient zéro, ce serait exactement -2 31 , ce qui ne déborde pas). Dans tous les cas, la conversion ne peut pas être effectuée et une erreur doit être signalée.

Nous avons maintenant un s , un e et un f pour une valeur qui ne déborde pas afin de pouvoir préparer la valeur finale.

Si s est 1, définissez f sur – f .

La valeur de l’exposant vaut pour une signification comprise entre 1 (inclus) et 2 (exclusif), mais notre signification commence par un peu à 2 24 . Nous devons donc nous adapter à cela. Si e vaut 24, notre signification est correcte, et nous avons terminé, alors retournons f comme résultat. Si e est supérieur à 24 ou inférieur à 24, nous devons déplacer la signification de manière appropriée. De plus, si nous allons déplacer f à droite, il se peut que nous devions l’arrondir pour obtenir un résultat arrondi à l’entier le plus proche.

Si e est supérieur à 24, décaler f à gauche e -24 bits. Retourne f comme résultat.

Si e est inférieur à -1, le nombre à virgule flottante est compris entre -½ et ½, exclusif. Renvoie 0 comme résultat.

Sinon, nous décalerons 24 bits de droite. Cependant, nous allons d’abord enregistrer les éléments dont nous avons besoin pour l’arrondissement. Définissez r sur le résultat de la conversion de f en un entier non signé de 32 bits et en le décalant de 32 bits (24 e ) à gauche (de manière équivalente, à gauche de 8 bits e ou plus ). Cela prend les bits qui seront décalés de f (ci-dessous) et «gauche les ajuster» dans les 32 bits, nous avons donc une position fixe où ils commencent.

Maj f droite 24 bits.

Si r est inférieur à 2 31 , ne faites rien (ceci est arrondi au nombre de bits tronqué). Si r est supérieur à 2 31 , ajoutez un à f (ceci est arrondi). Si r est égal à 2 31 , ajoutez le bit bas de f à f . (Si f est impair, ajoutez un à f . Des deux valeurs également proches, ceci arrondit à la valeur paire.) Retourne f .

&x donne l’adresse de x donc a float* type.

(int*)&x convertit ce pointeur en pointeur int c’est-à-dire en chose int* .

*(int*)&x déréférencent ce pointeur dans une valeur int . Cela ne fera pas ce que vous croyez sur des machines où int et float ont des tailles différentes.

Et il pourrait y avoir des problèmes d’endianité.

Cette solution a été utilisée dans l’algorithme de racine carrée inverse rapide .

Vous ne pouvez pas (de manière significative) convertir un nombre à virgule flottante en un ‘entier’ ( signed int ou int ) de cette manière.

Il se peut qu’il finisse par avoir le type entier, mais il s’agit en fait d’un index dans l’espace de codage IEEE754, et non d’une valeur significative en soi.

Vous pourriez peut-être soutenir qu’un int unsigned sert à la fois un motif binary et une valeur entière, mais pas l’ int .


Il existe également des problèmes de plate – forme avec la manipulation de bits des entiers signés.

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

Vous pouvez lancer le float en utilisant une référence. Un tel casting ne devrait jamais générer de code.

C ++

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

Sortie:

 Float 1.000000 is 0x3f800000 

Si vous voulez une conversion de style c ++, utilisez un reinterpret_cast, comme ceci.

 int i = reinterpret_cast(f); 

Cela ne fonctionne pas avec les expressions, vous devez le stocker dans une variable.

  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 &'