【问题标题】:Printing Hex Floating Point Constants from Array of Bytes从字节数组打印十六进制浮点常量
【发布时间】:2009-08-10 11:08:15
【问题描述】:

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

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

一个示例十六进制浮点常量是一个格式为

的字符串
0x1.FFFFFEp127f 

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

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

【问题讨论】:

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


    【解决方案1】:

    printf 手册说:

    一个,一个

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

    【讨论】:

    • 谢谢,但我需要在标准 C++98 或 C89 中执行此打印,而不能访问 C99 样式的 printf ;-( +1.
    • 那么你应该在你的问题中添加一个特定的请求:)
    • 由于许多标准库都有可用的源代码,您可以从 vaprintf 获取格式化代码并将其用于输出。
    • 虽然阅读它是一件苦差事(你会看到),但这里是实现 __printf_fphex,它在 glibc 中进行 %A 转换:eglibc.org/cgi-bin/viewcvs.cgi/trunk/libc/stdio-common/…
    • 奇怪的是,来源中的姓氏之一是我上大学的一个人。代码质量达不到他平时的标准。
    【解决方案2】:

    至少从 C90 开始,您可以使用 math.h 中的 frexp(),然后自己进行转换。像这样的东西(未经测试,未设计用于处理 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';
            }
        }
    }
    

    【讨论】:

      【解决方案3】:
      #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);
      }
      

      【讨论】:

      • 这不是使用双精度中的所有位来创建 frac_bits 吗?
      • @grussel: 不,只有较低的 52 个
      【解决方案4】:

      这可能是一个“开箱即用”的答案,但为什么不使用 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?)并复制它们的功能。

      【讨论】:

        猜你喜欢
        • 2021-05-04
        • 2011-12-05
        • 1970-01-01
        • 2017-12-26
        • 2014-02-26
        • 2019-04-07
        • 2018-10-07
        • 2013-01-18
        • 1970-01-01
        相关资源
        最近更新 更多