为什么当我从一个32位二进制大恩迪安编码的文件中读取IEEE-754浮点数时,会失去精度?

问题描述 投票:0回答:1

我正在用纯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_tfloat,我得到的单精度浮点值是 449553664.000000 接近但不完全是Matlab代码中的内容,也就是 449553670.000000. 我已经验证了当Matlab读取二进制文件时,它也得到了相同的十六进制值。0x1acba506 我的C代码有。

当我从 floatuint32_t 并打印十六进制值,我最终得到的是 0x1acba500这表明我在简单的铸造中失去了精确性,即。float ans = (float)result; 但我真的不明白为什么?我在x86机器上使用的是gcc 7.4,我已经验证了以下几点 sizeof float == sizeof uint32. 我是不是做了一个错误的假设,编译器使用的是IEEE-754单精度浮点?

在调试中,我发现一个 浮点在线计算器 这让人觉得精度似乎已经无可救药了,但问题就变成了Matlab是如何保留它的?

Here is the number I'm getting by casting to float in my C code

Here is the number that I see in the Matlab code

c matlab floating-point precision ieee-754
1个回答
2
投票

一个单精度的浮点数可以装在一个32位的寄存器中,它的大小和32位整数完全一样。但并不是所有的浮点数都是精确的:其中一部分(恰好是8位)是用来表示指数的。所以,这意味着一个单精度的浮点数不能代表与32位整数相同的精度。

因此,当你把一个32位整数转换为单精度浮点数时,一些精度损失是可以预期的。如果你想不损失精度,你应该使用更常见的双精度浮点格式,它使用64位,包括53位精度。


2
投票

IEEE 754的咒语 单精度 float是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表示。双精度浮子.

© www.soinside.com 2019 - 2024. All rights reserved.