IBM 单精度浮点数据转换为预期值

Cri*_*ble 1 c++ byte bit

我需要从二进制文件中读取值,数据格式是 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]);
            }
        }
Run Code Online (Sandbox Code Playgroud)

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

此图指定 IBM 单精度浮点 IBM 单精度浮点 我不明白 24 位正二进制小数是什么。我知道如何在 hex<->dec<->binary 之间进行转换,所以我的基本理解是将所有 q 视为一个非常长的二进制段,将使用 Q24(2)^(23) 作为最大的value 同时将所有前面的值加在一起,然后将数字乘以 10^-24 。但我的直觉告诉我这是错误的。澄清什么是基数点或 MSB 会有所帮助。

Mat*_*lia 5

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

正如维基百科所说,

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

如果我们想象你读取的字节在 a 中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.;
Run Code Online (Sandbox Code Playgroud)

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


编辑@Eric Postpischil正确地指出,如果您有 C99 或 POSIX 2001 可用,则mantissa * exp2(-24 + 4*exponent)不应使用ldexp(mantissa, -24 + 4*exponent),它在实现中应该更精确(并且可能更快)。

  • `ldexp(mantissa, -24 + 4*exponent)` 比 `mantissa * exp2(-24 + 4*exponent)` 更可取。`ldexp` 专为这类情况而设计,从有效数和指数组合一个浮点值(或通过指数的整数值调整现有值),它避免了 `exp2` 的数值问题。由于 `exp2` 必须处理非整数参数,因此它使用各种近似值来实现,并且在某些实现中可能会返回不完美的结果。 (2认同)