2009-08-10 5 views
2

Comment une constante à virgule flottante hexadécimale, telle que spécifiée dans C99, peut-elle être imprimée à partir d'un tableau d'octets représentant la représentation machine d'une valeur à virgule flottante? par exemple. donnéeImpression de constantes à virgule flottante hexadécimale à partir d'un tableau

union u_double 
{ 
    double dbl; 
    char data[sizeof(double)]; 
}; 

Un exemple virgule flottante hexadécimale constante est une chaîne de la forme

0x1.FFFFFEp127f 

Une spécification de syntaxe pour cette forme de littéral peut se trouver dans la IBM site, et une brève description de la syntaxe est ici sur le GCC site. La fonction printf peut être utilisée pour faire cela sur des plates-formes avec accès aux fonctions C99 dans la bibliothèque standard, mais je voudrais pouvoir effectuer l'impression en MSVC, qui ne supporte pas C99, en utilisant C89 ou C standard ++ 98.

Répondre

4

printf manuel dit:

a, A

(C99 pas SUSv2) Pour une conversion, le double argument est converti en notation hexadécimale (en utilisant les lettres abcdef) dans le style [ -] 0xh.hhhhp d; pour une conversion, le préfixe 0X, les lettres ABCDEF et le séparateur d'exposant P sont utilisés. Il y a un chiffre hexadécimal avant le point décimal et le nombre de chiffres après celui-ci est égal à la précision. La précision par défaut suffit pour une représentation exacte de la valeur si une représentation exacte en base 2 existe et sinon est suffisamment grande pour distinguer les valeurs de type double. Le chiffre avant la virgule décimale n'est pas spécifié pour les nombres non normalisés, et différent de zéro mais non spécifié pour les nombres normalisés.

+0

Merci, mais je dois effectuer cette impression dans la norme C++ 98 ou C89, sans accès à un style C99 printf ;-( +1. – grrussel

+0

alors vous devez ajouter une demande spécifique dans votre question :) – dfa

+0

Puisque de nombreuses bibliothèques standard ont des sources disponibles, vous pouvez récupérer le code de formatage de vaprintf et l'utiliser pour votre sortie. – plinth

1

Vous pouvez utiliser frexp() qui se trouve dans math.h depuis au moins C90, puis effectuer vous-même la conversion. Quelque chose comme ça (non testé, pas conçu pour gérer les frontières comme NaN, infinités, limites tampons, etc.)

void hexfloat(double d, char* ptr) 
{ 
    double fract; 
    int exp = 0; 

    if (d < 0) { 
     *ptr++ = '-'; 
     d = -d; 
    } 
    fract = frexp(d, &exp); 

    if (fract == 0.0) { 
     strcpy(ptr, "0x0.0"); 
    } else { 
     fract *= 2.0; 
     --exp; 
     *ptr++ = '0'; 
     *ptr++ = 'x'; 
     *ptr++ = '1'; 
     fract -= 1.0; 
     fract *= 16.0; 
     *ptr++ = '.'; 
     do { 
      char const hexdigits[] = "ABCDEF"; 
      *ptr++ = hexdigits[(int)fract]; // truncate 
      fract -= (int)fract; 
      fract *= 16; 
     } while (fract != 0.0); 
     if (exp != 0) { 
      sprintf(ptr, "p%d", exp); 
     } else { 
      *ptr++ = '\0'; 
     } 
    } 
} 
0
#include <stdint.h> 
#include <stdio.h> 

int main(void) 
{ 
    union { double d; uint64_t u; } value; 
    value.d = -1.234e-5; 

    // see http://en.wikipedia.org/wiki/Double_precision 
    _Bool sign_bit = value.u >> 63; 
    uint16_t exp_bits = (value.u >> 52) & 0x7FF; 
    uint64_t frac_bits = value.u & 0xFFFFFFFFFFFFFull; 

    if(exp_bits == 0) 
    { 
     if(frac_bits == 0) 
      printf("%s0x0p+0\n", sign_bit ? "-" : ""); 
     else puts("subnormal, too lazy to parse"); 
    } 
    else if(exp_bits == 0x7FF) 
     puts("infinity or nan, too lazy to parse"); 
    else printf("%s0x1.%llxp%+i\n", 
     sign_bit ? "-" : "", 
     (unsigned long long)frac_bits, 
     (int)exp_bits - 1023); 

    // check against libc implementation 
    printf("%a\n", value.d); 
} 
+0

N'utilise-t-il pas tous les bits du double pour créer des frac_bits? – grrussel

+0

@grussel: non, seulement les 52 inférieurs – Christoph

0

Cela pourrait être une réponse « en dehors de la boîte », mais pourquoi ne pas convertir la double pour une chaîne en utilisant sprintf, puis analyser la chaîne pour la mantisse et l'exposant, convertir ceux

par exemple, quelque chose comme:

char str[256]; 
long long a, b, c; 
sprintf(str, "%e", dbl); 
sscanf(str, "%d.%de%d", &a, &b, &c); 
printf("0x%x.%xp%x", a, b, c); 

Je suis sûr que vous auriez à modifier les formats pour sprintf et sscanf. Et vous n'obtiendrez jamais un premier chiffre hexadécimal entre A et F. Mais en général, je pense que cette idée devrait fonctionner. Et c'est simple.

Un meilleur moyen serait de trouver une bibliothèque open source qui implémente ce format pour printf (par exemple, newlib, uClibc?) Et de copier ce qu'ils font.

Questions connexes