【问题标题】:IBM Single Precision Floating Point data conversion to intended valueIBM 单精度浮点数据转换为预期值
【发布时间】:2017-12-26 22:19:24
【问题描述】:

我需要从二进制文件中读取值。数据格式为 IBM 单精度浮点(4 字节十六进制指数数据)。我有从文件中读取并取出每个字节并像这样存储它的 C++ 代码

 unsigned char buf[BUF_LEN];

        for (long position = 0; position < fileLength; position += BUF_LEN) {
            file.read((char* )(&buf[0]), BUF_LEN);

           // printf("\n%8ld:  ", pos);

            for (int byte = 0; byte < BUF_LEN; byte++) {
               // printf(" 0x%-2x", buf[byte]);
            }
        }

这会打印出每个字节的十六进制值。

此图指定IBM单精度浮点 IBM single precision floating point

如何将缓冲区转换为浮点值?

【问题讨论】:

标签: c++ byte bit


【解决方案1】:

格式实际上很简单,与 IEEE 754 binary32 格式并没有特别的不同(实际上更简单,不支持任何“神奇”的 NaN/Inf 值,并且没有次正规数,因为这里的尾数有一个隐含的左边的 0 而不是隐含的 1)。

正如Wikipedia 所说,

数字表示为以下公式:(−1)sign × 0.significand × 16exponent−64

如果我们假设您读取的字节位于 uint8_t b[4] 中,那么结果值应该类似于:

uint32_t mantissa = (b[1]<<16) | (b[2]<<8) | b[3];
int exponent = (b[0] & 127) - 64;
double ret = mantissa * exp2(-24 + 4*exponent);
if(b[0] & 128) ret *= -1.;

请注意,这里我在 double 中计算了结果,因为 IEEE 754 float 的范围不足以表示相同大小的 IBM 单精度值(也相反)。另外,请记住,由于字节序问题,您可能必须恢复我上面代码中的索引。


编辑@Eric Postpischil 正确指出,如果您有可用的 C99 或 POSIX 2001,则应使用 mantissa * exp2(-24 + 4*exponent) 而不是 ldexp(mantissa, -24 + 4*exponent),这应该跨实现更精确(可能更快)。

【讨论】:

  • 抱歉,您能否解释一下尾数是什么以及还原索引的含义
  • 为了清楚起见,我添加的图片本质上就是数据在文件中的存储方式。因此,鉴于在表中似乎最重要的位在最右边的列中。所以我相信这意味着它是小端的?因此我将不得不恢复索引。不过,我不确定这将如何在您的代码中起作用。它是否需要另一个交换位的函数。
  • 恢复索引是指更改b[0] b[3]b[1] b[2];这应该解释字节端的差异。它所占用的 2^-1 是 mantissa 的最重要 bit,即它只是说尾数的 MSb 对应于值 2^-1 ( IOW,你必须将尾数的 24 位整数乘以 2^-24)。
  • 我写的代码按照你的图片显示的字节顺序,是大端。如果它真的是这样,我的代码应该按原样工作。另外,不要将位排序与字节排序混淆;当您处理软件时,字节内的位已经正确排序,您通常只需要担心字节顺序。
  • ldexp(mantissa, -24 + 4*exponent) 优于 mantissa * exp2(-24 + 4*exponent)ldexp 就是为这种情况而设计的,它从有效数和指数组合一个浮点值(或通过指数的整数值调整现有值),它避免了 exp2 的数值问题。由于exp2 必须处理非整数参数,因此它使用各种近似值实现,并且在某些实现中可能返回不完美的结果。
猜你喜欢
  • 2020-02-03
  • 1970-01-01
  • 2013-04-16
  • 2011-11-17
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
  • 1970-01-01
  • 2014-09-28
相关资源
最近更新 更多