2009-08-10 86 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?)并复制它们的功能。