2017-08-08 2 views
1

J'essaye de faire une conversion de hexadécimal en float64 en C++ avec une double précision IEEE 754. C'est la première fois que j'ai joué avec des bits alors peut-être que mon code n'est pas assez propre. Je ne sais pas pourquoi ma mantisse me donne des résultats étranges mais je pense que je fais quelque chose de mal.Hexadécimal à flotter IEEE 754 double précision C++

long int raw = 0x40000F0000000001; 
int sign = raw >> 63; 
long int mantissa = (raw & 0xFFFFFFFFFFFFF); 
mantissa +=1; 
double exp = ((raw >> 52) & 0x7FF) - 1023; 
double result = pow(-1., sign) * mantissa * pow(2.0, exp); 
cout << "MANTISSA: " << mantissa << " EXP: " << exp << endl; 
cout << "RESULT: " << result << endl; 

Et la sortie est:

MANTISSA: 16492674416642 EXP: 1 
RESULT: 3.29853e+13 

Tout le monde sait comment le faire?

Merci

Répondre

2
long int raw = 0x40000F0000000001; 

Il est la mise en œuvre spécifiée si long est assez long pour contenir autant de bits (généralement sous Windows il n'est pas, sous Linux, il est si vous compilez un programme 64 bits, mais pas 32 bits .)

int sign = raw >> 63; 

Cette ligne a un comportement défini de mise en œuvre si est réglé le bit de signe. (Les résultats sont vraisemblables 1 et -1, mais il n'y a rien pour arrêter une mise en œuvre spécifiant « 42 ».) Vous seriez bien mieux définir raw que uint64_t

long int mantissa = (raw & 0xFFFFFFFFFFFFF); 
mantissa +=1; 

Ceci est votre problème. Le bit '1' manquant est au avant de tous les bits. Vous devez ajouter 0x1000000000000 au lieu (ou mieux, définir une const uint64_t MantissaOffset = 1uLL << 52; et une autre const uint64_t MantissaMask = MantissaOffset-1; constante -. De cette façon vous ne devez pas compter tous les F s et 0 s)

Vous aurez alors une mantisse qui est 2 * * 52 trop grand (vous devez expliquer que lors du calcul de l'exposant.

double exp = ((raw >> 52) & 0x7FF) - 1023; 
double result = pow(-1., sign) * mantissa * pow(2.0, exp); 

... et bien sûr, cela ne tient pas compte de dénormalisés, Nans et fichiers INF.

cout << "MANTISSA: " << mantissa << " EXP: " << exp << endl; 
cout << "RESULT: " << result << endl; 
1

Il semble que votre mee mantisse est presque ok. Pour 0x40000F0000000001 fraction est 0xF0000000001 (au moins 52 bits) qui est exactement 16492674416641. Pour être honnête, je ne sais pas pourquoi vous ajoutez 1 à cela.

Si vous voulez un bon exemple comment vous pouvez jouer avec, vous pouvez voir la page wikipedia this. A la fin d'un chapitre, il est un très bel exemple de la façon d'obtenir un double à partir de son étape de représentation brute 64 bits par étape:

Given the hexadecimal representation 3FD5 5555 5555 5555(16), 
    Sign = 0 
    Exponent = 3FD(16) = 1021 
    Exponent Bias = 1023 (constant value; see above) 
    Fraction = 5 5555 5555 5555(16) 
    Value = 2^(Exponent - Exponent Bias) × 1.Fraction – Note that Fraction must not be converted to decimal here 
     = 2^-2 × (15 5555 5555 5555(16) × 2^-52) 
     = 2^-54 × 15 5555 5555 5555(16) 
     = 0.333333333333333314829616256247390992939472198486328125 
     ˜ 1/3 

Aussi, notez: que vous traitez avec des valeurs 64 bits, il est plus sûr d'utiliser uint64_t type qui garantit d'être de taille 64 bits. Vous pouvez l'utiliser en incluant l'en-tête <stdint.h>.

+0

Il en ajoutant '1' parce que votre code exemple montre '1.Fraction'. Et après avoir extrait beaucoup de 5, il utilise ensuite '15 ...' - c'est ce leader qu'il essaie d'ajouter. –

+0

@MartinBonner Eh bien, alors il ne devrait certainement pas être '+ = 1' comme vous l'avez déjà couvert dans votre réponse. –