从二进制文件中读取并转换为双精度?

Posted

技术标签:

【中文标题】从二进制文件中读取并转换为双精度?【英文标题】:Reading from binary file and converting to double? 【发布时间】:2017-02-03 18:26:23 【问题描述】:

我正在尝试编写一个读取二进制文件并将其转换为数据类型的 C 程序。我正在生成一个带有 head 命令head -c 40000 /dev/urandom > data40.bin 的二进制文件。该程序适用于数据类型 int 和 char,但适用于 double。这是程序的代码。

void double_funct(int readFrom, int writeTo)
    double buffer[150];
    int a = read(readFrom,buffer,sizeof(double));
    while(a!=0)
        int size = 1;
        int c=0;

         for(c=0;c<size;c++)
            char temp[100];
            int x = snprintf(temp,100,"%f ", buffer[c]);
            write(writeTo, temp, x);
        
        a = read(readFrom,buffer,sizeof(double));
    

这是有效的 char 函数

void char_funct(int readFrom, int writeTo)
    char buffer[150];
    int a = read(readFrom,buffer,sizeof(char));
    while(a!=0)
        int size = 1;
        int c=0;

        for(c=0;c<size;c++)
            char temp[100]=" ";
            snprintf(temp,100,"%d ", buffer[c]);
            write(writeTo, temp, strlen(temp));
        
        a = read(readFrom,buffer,sizeof(char));
    

问题是使用 char 我需要使用 wc -w file 得到 40000 个单词,然后我得到了它们。现在使用 double 我得到随机数量的单词,但理论上我应该从 40000 字节的数据中得到 5000,但我得到 4000 到 15000 之间的随机数量,对于 char 我得到 40000,就像一个字符应该有 1 个字节一样。

我不知道有什么问题,同样的代码适用于 int,我从 40000 个字节的数据中得到 10000 个字。

【问题讨论】:

假设read() 读取请求的全部字节数是不安全的。必须使用返回值来确定实际读取了多少字节。 此外,如果发生错误,read() 将返回 -1。您不考虑这种可能性,如果确实发生错误,您将处理谁知道什么数据。 这一行:int x = snprintf(temp,100,"%f ", buffer[c]); 从缓冲区中取出一个字符并尝试将其转换为浮点数。您可能想要检查返回值x,它告诉您有多少写入缓冲区。但是,这肯定不是你想要做的。 此外,write() 在传输的字节数方面与read() 具有相似的特征,但实际上这对于本地文件来说很少出现问题。 @bruceg 在该版本中,缓冲区是double 数组,而不是其他版本中的char 数组。 【参考方案1】:

主要问题似乎是您的 temp 数组对于您的 printf 格式和数据来说不够大。 IEEE-754 doubles 的十进制指数范围从 -308 到 +308。您正在使用"%f" 格式打印双打,这会产生一个简单的十进制表示。由于未指定精度,因此应用默认精度 6。这可能需要多达 1(符号)+ 309(数字)+ 1(小数点)+ 6(尾随小数位)+ 1(终止符)字符(总共 318),但您只有 100 个空间。

您使用snprintf() 打印到缓冲区,因此不会超出那里的数组边界,但snprintf() 返回需要的字节数,减去所需的字节数终结者。这就是您write() 的字节数,并且在许多情况下确实 超出了您的缓冲区。您会在输出中看到结果。

其次,您还会在输出中看到大量 0.00000,这是因为将小数字四舍五入到 6 位小数精度。

如果您更改打印数字的格式,您可能会获得更好的成功。例如,"%.16e " 将为您提供指数格式的输出,共有 17 位有效数字(小数点前一位)。这将不需要内存或磁盘上的过多空间,并且它将准确地传达所有数字,无论规模如何,再次假设您的 doubles 是根据 IEEE 754 表示的。如果您愿意,您可以进一步消除(非常安全) 通过采用@chux 在 cmets 中建议的变体来假设 IEEE 754 格式。这将是最安全的方法。

还有一件事:IEEE 浮点支持无穷大和多个非数字值。与普通的 FP 数字相比,这些数字很少,但您仍然有可能偶尔会遇到其中一个。它们可能会很好地转换为输出,但您可能需要考虑是否需要专门处理它们。

【讨论】:

注意:关闭 1。需要buffer[318]。 --> 1e308 为“309(数字)”。 "%.15e" 很好,但不足以将一些不同的double 打印为不同的文本。建议printf("%.*e\n", DBL_DECIMAL_DIG - 1, buffer[c]); 以不同方式打印所有不同的double。或printf("%a\n", buffer[c]); 谢谢@chux。我已经修正了我的数学并根据你的 cmets 调整了我建议的格式。您也很正确,可以进行概括以消除有关浮点表示的假设,我很满意我的回答,请参阅您的评论以获取详细信息。

以上是关于从二进制文件中读取并转换为双精度?的主要内容,如果未能解决你的问题,请参考以下文章

使用 c++ 时从二进制文件中读取 int 不正确

从二进制文件读取时将大端转换为小端

读取二进制文件碎片并转换为具有内存效率的整数

在 C++ 中写入整数并从二进制文件中读取它们:字节数 mismaych

从二进制文件中读取矩阵

C# 将字符串转换为双精度/十进制并返回字符串,保留尾随零,为千位添加逗号