从二进制文件中读取double(字节顺序?)

4

我有一个二进制文件,想要从中读取一个双精度浮点数。

以十六进制表示,文件中有这8个字节(然后还有更多):

40 28 25 c8 9b 77 27 c9 40 28 98 8a 8b 80 2b d5 40 ...

这应该对应着大约为10的双精度浮点数(根据该条目的含义)。

我已经使用过

#include<stdio.h>
#include<assert.h>

int main(int argc, char ** argv) {
   FILE * f = fopen(argv[1], "rb");
   assert(f != NULL);
   double a;
   fread(&a, sizeof(a), 1, f);
   printf("value: %f\n", a);
}

然而,它输出的值是:-261668255698743527401808385063734961309220864.000000

很明显,这些字节没有被正确地转换为双精度浮点数。到底发生了什么事情呢? 使用ftell,我确认了确实读取了8个字节。


3
这些“字节”是由哪个代码编写的?它来自同一个平台吗? - WhozCraig
看起来原始系统将数据写成大端格式,而您正在尝试以小端格式读取它 - 您只需要反转这8个字节的顺序即可。 - Paul R
1
顺便说一下,如果你反转字节,第一个是12.0738,第二个是12.2979等等。因此,在LE平台上阅读时,请反转字节。我真诚地希望如果您这样做,所有平台都会以BE方式写入数据。 - WhozCraig
太好了!我能够使用来自 https://dev59.com/fFTTa4cB1Zd3GeqPqC0T 的联合技巧与 fgetc 一起使用。 - j13r
2个回答

4

与整数类型一样,浮点类型也会受到平台字节序的影响。当我在一个小端机器上运行这个程序时:

#include <stdio.h>
#include <stdint.h>

uint64_t byteswap64(uint64_t input) 
{
    uint64_t output = (uint64_t) input;
    output = (output & 0x00000000FFFFFFFF) << 32 | (output & 0xFFFFFFFF00000000) >> 32;
    output = (output & 0x0000FFFF0000FFFF) << 16 | (output & 0xFFFF0000FFFF0000) >> 16;
    output = (output & 0x00FF00FF00FF00FF) << 8  | (output & 0xFF00FF00FF00FF00) >> 8;
    return output;
}

int main() 
{
    uint64_t bytes = 0x402825c89b7727c9;
    double a = *(double*)&bytes;
    printf("%f\n", a);

    bytes = byteswap64(bytes);
    a = *(double*)&bytes;
    printf("%f\n", a);

    return 0;
}

然后输出结果为

12.073796
-261668255698743530000000000000000000000000000.000000

这说明您的数据以小端格式存储在文件中,但您的平台是大端。因此,读取值后需要执行字节交换。上面的代码显示了如何执行字节交换。


感谢您提供详细的答案。最终,我使用了来自stackoverflow.com/questions/4949144/how-to-byteswap-a-double的联合技巧和fgetc。 - j13r
8年后,我最终到达了这里。一个旁注:这会失去精度:36.24139221666073 -> 36.241392。我仍在寻找解决此问题的方法。 - user656208
@soze 不,它不会失去精度,你的诊断有误。 - David Heffernan

2

字节序是一种约定。读写双方应当就使用哪种字节序达成一致并保持一致。

你应该将数字读作int64,转换字节序,然后转换为double类型。


2
强制类型转换在想要重新解释相同位时是行不通的。强制类型转换将把(非常大的)整数转换为非常大的“double”。 - unwind
指针转换或联合转换都可以使用;后者在大小不匹配的情况下更安全,因为至少您知道它不会溢出到附近的变量。 - Medinoc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接