2009-08-10 125 views
2

如何從代表浮點值的機器表示的字節數組中打印十六進制浮點常量,如C99中所指定的那樣?例如給定從字節數組中打印十六進制浮點常量

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

一個例子十六進制浮點常數是形式

0x1.FFFFFEp127f 

這種形式的字面語法規範可以在IBM site中找到的字符串,並在語法的簡要說明是這裏的GCC site

printf函數可用於在具有標準庫中的C99功能的平臺上執行此操作,但我希望能夠使用標準C89或C在MSVC中執行不支持C99的打印++ 98。

回答

4

printf手冊說:

一個,A

(C99;未在SUSv2)對於轉換,雙參數被轉換爲十六進制表示法(使用字母ABCDEF)在樣式[ - ] 0xh.hhhhpd;對於A轉換,使用前綴0X,字母ABCDEF和指數分隔符P.小數點前有一個十六進制數字,其後的數字數等於精度。如果基數2中的確切表示存在,並且否則足夠大以區分類型double的值,則默認精度足以確切表示該值。小數點前的數字未指定爲非標準化數字,非零則爲非標準化數字。

+0

謝謝,但我需要執行標準C++ 98或C89該打印,而無需訪問C99型printf ;-( +1。 – grrussel 2009-08-10 11:22:38

+0

,那麼你應該在你的問題中添加特定的請求:) – dfa 2009-08-10 11:24:59

+0

由於許多標準庫都有可用的源代碼,因此您可以從vaprintf獲取格式代碼並將其用於輸出。 – plinth 2009-08-10 12:23:10

1

您可以使用math.h中的frexp(),因爲至少C90,然後自己進行轉換。事情是這樣的(未測試,不是設計來處理邊界喜歡的NaN,無窮大,緩衝的限制,等等)

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

這是不是使用double中的所有位來創建frac_bits? – grrussel 2009-08-10 13:33:21

+0

@grussel:不,只有最低的52個 – Christoph 2009-08-10 13:42:00

0

這可能是一個「框框」的答案,但爲什麼不轉換雙成字符串使用sprintf的,然後解析尾數和指數的字符串轉換那些

例如,像:

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

我敢肯定,你不得不修改sprintf的格式和sscanf的。而且你永遠不會在A和F之間得到第一個十六進制數字。但是總的來說,我認爲這個想法應該起作用。而且很簡單。

更好的方法是找到一個爲printf實現此格式的開源庫(例如newlib,uClibc?)並複製它們的功能。