我正在写浮点数到文件,但有两种不同的方式来编写这些数字,我想知道要使用哪些。
两个选择是:
选项1似乎对我来说更实用,因为我将每个浮点数截断为4个字节。阅读时,可以完全跳过解析每个数字。但在实践中,我只看过使用过的选项2。
有问题的数据是3D模型信息,其中小文件大小和快速阅读可能是非常有利的,但同样,我知道没有现有的3D模型格式,我想它背后一定有充分的理由
我的问题是,选择写出数字形式而不是位表示的原因是什么?是否有使用二进制形式的首选?
答案 0 :(得分:3)
如果符合以下条件,您可能更喜欢二进制格式:
由于您提到的数据是3D模型模拟,编码的紧凑性(可能还有性能)和精度可能与您相关。另一方面,文本编码是人类可读的。
也就是说,使用二进制编码时,您通常会遇到类似字节序的问题,并且浮点表示在不同的计算机上可能会有所不同,但here是一种以便携方式对二进制格式的浮点数(或双精度数)进行编码的方法:
uint64_t pack754(long double f, unsigned bits, unsigned expbits)
{
long double fnorm;
int shift;
long long sign, exp, significand;
unsigned significandbits = bits - expbits - 1; // -1 for sign bit
if (f == 0.0) return 0; // get this special case out of the way
// check sign and begin normalization
if (f < 0) { sign = 1; fnorm = -f; }
else { sign = 0; fnorm = f; }
// get the normalized form of f and track the exponent
shift = 0;
while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
fnorm = fnorm - 1.0;
// calculate the binary form (non-float) of the significand data
significand = fnorm * ((1LL<<significandbits) + 0.5f);
// get the biased exponent
exp = shift + ((1<<(expbits-1)) - 1); // shift + bias
// return the final answer
return (sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand;
}
*:在C中,由于C99有seems方法可以做到这一点,但我认为它还需要更多空间。
答案 1 :(得分:3)
首先,float
s在您可能正常遇到的任何架构上都是4个字节,所以没有什么是&#34;截断&#34;当你将4个字节从内存写入文件时。
至于你的主要问题,许多常规文件格式都是针对&#34;互操作性而设计的。并且易于阅读/写作。这就是为什么文本,这是一种几乎普遍可移植的表示(尽管有字符编码问题),但最常使用。
例如,程序很容易读取字符串&#34; 123
&#34;从文本文件中知道它代表数字123。
(但请注意,文本本身不是一种格式。您可以选择将所有数据元素表示为ASCII / Unicode /任何字符串,并将所有这些字符串相互叠加以形成文本文件,但是您仍然需要准确指定每个元素的含义以及可以在哪里找到哪些数据。例如,一个非常简单的基于文本的3D三角形网格文件格式可能在文件的第一行的网格中有三角形的数量,其次是接下来的N行中的三个实数三元组,每个都指定三角形的三个顶点的X,Y,Z坐标所需的9个数字。)
另一方面是二进制格式。这些通常中包含与计算机内存中相同格式的数据元素。这意味着整数用固定的字节数表示(1,2,4或8,通常用&#34;二进制补码&#34;格式)或实数用4或8字节表示IEEE 754格式。 (请注意,为了保持原状,我省略了很多细节。)
二进制格式的主要优点是:
它们通常尺寸较小。写为ASCII字符串的32位整数可以达到10或11个字节(例如-1000000000),但在二进制中它总是占用4个字节。较小意味着更快转移(通过网络,从磁盘到内存等)并且更容易存储。
每个数据元素的读取速度都更快。不需要复杂的解析。如果数据元素恰好是您的平台/语言可以使用的确切格式/布局,那么您只需要将几个字节从磁盘传输到内存就可以完成。
即使是大型复杂的数据结构也可以在中完全在磁盘上布局,就像它们在内存中一样,然后你需要做的就是&#34;读&#34;这种格式是从磁盘到内存中获取大量字节(可能包含许多数据元素),只需一个简单快速的操作即可完成。
但是,第三个优势要求您将磁盘上的数据布局完全(逐位)与内存中数据结构的布局相匹配。这意味着,几乎总是,该文件格式仅适用于您的代码和代码,即使您在自己的代码中更改了某些内容也是如此。这意味着它完全不是可移植的或可互操作的。但是与它合作很快!
二进制格式也有缺点:
您无法在简单的通用软件(如文本编辑器)中查看,编辑或理解它们。您可以在任何文本编辑器中打开任何XML,JSON或配置文件,并且很容易理解它,但不是JPEG文件。
您通常需要更多特定代码来读/写二进制格式,而不是文本格式。更不用说记录文件的每一位应该是什么的规范。文本文件通常更加清晰明了。
在某些(许多)语言(脚本和&#34;更高级别的语言)中,您通常无法访问构成整数或浮点数的字节,而不是读他们也不写。这意味着当您使用C或C ++等低级语言工作时,您将失去二进制文件为您提供的大部分速度优势。
原始数据类型的二进制内存格式几乎总是与内存所附加的硬件(或更一般地说,整个平台)相关联。当您选择将相同的位从内存写入文件时,文件格式也会依赖于硬件。一个硬件可能不会以与另一个硬件完全相同的方式存储浮点实数,这意味着写在一个上的二进制文件无法在另一个上天真地读取(必须小心并将数据小心地转换为目标格式。)一个主要区别在硬件架构之间被称为&#34; endianness&#34;这会影响多字节基元(例如4字节整数,或8字节浮点数)如何存储在内存中(从最高位到最低位,反之亦然,这被称为&#34; big endian&#34;和#34; little endian&#34;分别写入大端架构(例如PowerPC)上的二进制文件并在小端架构(例如x86)上逐字读取的数据将包含所有内容每个基元中的字节从高值换成低值,这意味着所有(好的,几乎所有)值都是错误的。
由于您提到3D模型数据,让我举一个典型游戏引擎中使用的格式示例。游戏引擎运行时很可能需要在读取模型时具有的最大速度,并且3D模型很大,因此通常它的模型文件具有非常特定且不是全部可移植的格式。但是任何建模软件都很可能不支持这种格式。因此,您需要编写一个转换器(也称为导出器或导入器),它将采用通用的常用格式(例如OBJ,DAE等)并将其转换为特定于引擎的专有格式。但正如我所提到的,使用基于文本的格式读取/传输/工作比二进制格式更容易,因此您通常会选择基于文本的通用格式将模型导出,然后在其上运行转换器以进行优化,二进制,特定于引擎的运行时格式。