2009-08-10 4 views
2

Wie kann ein hexadezimale Gleitkomma konstant, wie in C99 angegeben, aus einem Array von Bytes gedruckt werden, die die Maschinen Darstellung eines Gleitkommawert? z.B. gegebenPrinting Hex Gleitkommafehler Constants von Array von Bytes

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

Ein Beispiel hexadezimal ein String der Form konstant ist

0x1.FFFFFEp127f 

kann eine Syntaxspezifikation für diese Form von wörtlichen Gleitkomma- auf den IBM site, und eine kurze Beschreibung der Syntax zu finden ist hier auf der GCC site.

Die printf-Funktion kann auf Plattformen mit Zugriff auf C99-Funktionen in der Standardbibliothek verwendet werden, aber ich möchte in der Lage sein, das Drucken in MSVC, das C99 nicht unterstützt, unter Verwendung von Standard C89 oder C auszuführen ++ 98.

Antwort

4

printf Handbuch sagt:

a, A

(C99, nicht in SUSv2) Für eine Umwandlung, die doppelte Argument in hexadezimale Darstellung umgewandelt wird (die Buchstaben abcdef verwendet wird) in den Stil [ -] 0xh.hhhhp d; Für die Umwandlung werden das Präfix 0X, die Buchstaben ABCDEF und das Exponententrennzeichen P verwendet. Es gibt eine hexadezimale Ziffer vor dem Dezimalpunkt und die Anzahl der Ziffern nach der Genauigkeit. Die Standardpräzision reicht für eine exakte Darstellung des Wertes, wenn eine exakte Darstellung in der Basis 2 existiert und ansonsten ausreichend groß ist, um Werte vom Typ double zu unterscheiden. Die Zahl vor dem Dezimalpunkt ist nicht spezifiziert für nicht normalisierte Zahlen und nicht Null, aber ansonsten nicht spezifiziert für normalisierte Zahlen.

+0

Danke, aber ich brauche diesen Druck in Standard-C-98 oder C89 ++ zu einem C99-Stil printf ohne Zugang zu führen ;-( +1. – grrussel

+0

dann sollten Sie eine spezifische Anfrage in Ihrer Frage hinzufügen :) – dfa

+0

Da für viele Standardbibliotheken Quellen verfügbar sind, können Sie den Formatierungscode von vaprintf abrufen und diesen für Ihre Ausgabe verwenden. – plinth

1

Sie können frexp() verwenden, das in math.h seit mindestens C90 ist und dann die Konvertierung selbst vornehmen. So etwas wie diese (nicht getestet, nicht Grenzen wie NaN, Unendlichkeiten zu behandeln entworfen, Puffergrenzen, usw.)

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

Verwendet dies nicht alle Bits im Double, um Frac_Bits zu erstellen? – grrussel

+0

@grussel: nein, nur die unteren 52 – Christoph

0

Dies könnte eine „outside the box“ Antwort sein, aber warum das nicht konvertieren Doppel in einen String sprintf verwenden, dann die Zeichenfolge für die Mantisse und Exponent analysieren, konvertieren diejenigen

zB so etwas wie:

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

ich bin sicher, dass Sie die Formate für sprintf ändern müss und sscanf. Und Sie würden nie eine erste hexadezimale Zahl zwischen A und F bekommen. Aber im Allgemeinen denke ich, dass diese Idee funktionieren sollte. Und es ist einfach.

Eine bessere Möglichkeit wäre, eine Open-Source-Bibliothek zu finden, die dieses Format für printf implementiert (z.B. newlib, uClibc?) Und kopieren, was sie tun.