从字节数组打印十六进制浮点常量

时间:2009-08-10 11:08:15

标签: c++ c visual-studio visual-c++

如何在表示浮点值的机器表示的字节数组中打印C99中指定的十六进制浮点常量?例如给出

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

示例十六进制浮点常量是

形式的字符串
0x1.FFFFFEp127f 

可以在IBM site上找到此文字形式的语法规范,GCC site上有语法的简要说明。

printf函数可用于在可访问标准库中的C99功能的平台上执行此操作,但我希望能够使用标准C89或C +在不支持C99的MSVC中执行打印98。

4 个答案:

答案 0 :(得分:4)

printf手册说:

  

一,A

     

(C99;不在SUSv2中)对于转换,double参数在样式[ - ]0xh.hhhhp d中转换为十六进制表示法(使用字母abcdef);对于A转换,使用前缀0X,字母ABCDEF和指数分隔符P.小数点前有一个十六进制数字,后面的位数等于精度。如果存在基数2中的精确表示,则默认精度足以精确表示值,否则足够大以区分double类型的值。小数点前的数字未指定非标准化数字,非零,但未指定标准化数字。

答案 1 :(得分: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[] = "0123456789ABCDEF";
            *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';
        }
    }
}

答案 2 :(得分: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);
}

答案 3 :(得分:0)

这可能是一个“开箱即用”的答案,但为什么不使用sprintf将double转换为字符串,然后解析字符串中的尾数和指数,将它们转换为

例如,像:

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