首页 > 解决方案 > 为什么从 32 位二进制大端编码的文件中读取 IEEE-754 浮点时会丢失精度?

问题描述

我正在用纯 C 重写一些 Matlab 文件处理代码,并且我实现了以下函数,该函数将从二进制大端编码文件中读取 4 个字节,该文件应该表示 ieee-754 单精度浮点值。我验证了我能够使用以下代码将相关的 32 位数据作为无符号整数从文件中提取出来。

int fread_uint32_be(uint32_t *result, FILE ** fp)
{
    uint8_t data[sizeof(uint32_t)];
    if (!result || !*fp || sizeof(uint32_t) != fread((void *) data, 1, sizeof(uint32_t), *fp))
    {
        return -1;
    }
    *result = ((uint32_t)(data[0]) << 24 | (uint32_t)(data[1]) << 16 |
               (uint32_t)(data[2]) << 8  | (uint32_t)(data[3]));
    return 0;
}

我期望的数据有一个0x1acba506从这个函数返回的十六进制值,并通过大端格式的数据文件的十六进制转储进行验证。现在我的问题来了...

当我将此值从uint32_t转换为 时float,我得到一个单精度浮点值,449553664.000000该值接近但不完全是 Matlab 代码所具有的,即449553670.000000. 我已经验证,当 Matlab 读取二进制文件时,它也会获得与0x1acba506我的 C 代码相同的十六进制值。

当我从floatto 转换回uint32_t并打印十六进制值时,我最终得到0x1acba500,这表明我在简单转换中失去了精度,float ans = (float)result;但我真的不明白为什么?我在 x86 机器上使用 gcc 7.4,并且我已经验证了sizeof float == sizeof uint32. 我是否假设编译器正在使用 IEEE-754 单精度浮点?

在调试的时候,我发现了一个在线的浮点计算器,它看起来精度已经无可救药地丢失了,但接下来的问题是Matlab是如何保留它的?

这是我通过转换为浮动在我的 C 代码中得到的数字

这是我在 Matlab 代码中看到的数字

标签: cmatlabfloating-pointprecisionieee-754

解决方案


IEEE 754单精度浮点数的尾数为 24 位,其中第一位隐含 1。

让我们看看你的两个整数 - Python 是调试它们的好工具。他们的位表示是

>>> format(449553664, '032b')
'00011010110010111010010100000000'

>>> format(449553670, '032b')
'00011010110010111010010100000110'

现在,如果我们查看后一个数字,看看它如何适合单精度尾数,第一个 1 位是左起第 4 位,包括我们计算 24 位,我们得到

>>> format(449553670, '032b').lstrip('0')[:24]
'110101100101110100101000'

很明显,最后一个110不适合尾数,并且该值被四舍五入。因此 的值(float)449553670表示为

1.10101100101110100101000b * 10b ^ 11100b

即十进制

1.67471790313720703125 * 2 ^ 28

等于 449553664.0。


Matlab 很可能通过不使用浮点数而是双精度数来保持精度,就像 JavaScript 一样。所有宽度小于 53 位的整数都可以用 IEEE 754双精度浮点数表示。


推荐阅读