我正在阅读 Michael Abrash 的 Graphics Programming Black Book,它是关于 3D 图形性能的,所以我惊讶地发现那里的很多 C 代码都使用了 double
而不是 float
.我们谈论的是 90 年代早期的计算机(286、386、奔腾)和 MS-DOS C 编译器,那么使用 double
的原因是什么?在那个时代?没有float
存在或曾经 double
的和 float
的精度与今天不同吗?
总之,为什么double
在那个时代被用于性能关键代码?
最佳答案
据我所知,没有针对 MS-DOS 的 C 编译器使用 32 位宽 double
,相反,他们都使用了 64 位宽 double
.到 90 年代初,情况确实如此。基于对本书“实时 3D 浮点”一章的快速阅读,Michael Abrash 似乎认为任何精度的浮点数学在低于 Pentium CPU 的任何东西上都太慢了。您正在寻找的浮点代码要么是为奔腾 CPU 设计的,要么是用于性能无关紧要的非关键路径。对于适用于早期 CPU 的性能关键代码,Abrash 暗示他会使用定点算法来代替。
在很多情况下使用 float
而不是 double
实际上不会有太大的不同。有几个原因。首先,如果您没有 x87 FPU (floating-point unit)安装('486 之前的单独芯片),使用较低的精度不会提高性能足以使软件模拟浮点算法足够快以用于游戏。第二个是大多数 x87 FPU 操作的性能实际上不受精度影响。在 Pentium CPU 上,如果以较窄的精度执行,则只有除法会更快。对于早期的 x87 FPU,我不确定精度会影响除法,但它可能会影响 80387 上的乘法性能。在所有 x87 FPU 上,无论精度如何,加法的速度都是相同的。
第三是具体使用的C数据类型,是否是32位float
, 64 位 double
,甚至 80 位 long double
许多编译器支持,实际上并没有影响 FPU 在计算过程中使用的精度。这是因为 FPU 对于它支持的三种不同精度没有不同的指令(或编码)。没有办法告诉它执行 float
添加或 double
划分。相反,它以 FPU 控制寄存器中设置的给定精度执行所有算术运算。 (或者更准确地说,它执行算术就像使用无限精度然后将结果四舍五入到设置精度。)虽然每次使用浮点指令时都可以更改此寄存器,但这会导致大量性能下降,所以编译器从来没有这样做过。相反,他们只是在程序启动时将其设置为 80 位或 64 位精度,然后保持这种方式。
现在,将 FPU 设置为单精度实际上是 3D 游戏的常用技术。这意味着浮点运算,无论是否使用 double
或 float
类型,将使用单精度算术执行。虽然这最终只会影响浮点除法的性能,但 3D 图形编程往往会在关键代码(例如透视除法)中进行大量除法,因此这可能会显着提高性能。
然而,有一种方法可以使用 float
而不是 double
可以提高性能,这仅仅是因为 float
占用double
的一半空间.如果您有很多浮点值,那么必须读写一半的内存会对性能产生显着影响。但是,在 Pentium 或更早的 PC 上,这不会导致今天的巨大性能差异。那时 CPU 速度和 RAM 速度之间的差距并没有那么大,浮点性能要慢一些。尽管如此,如果不需要额外的精度,这将是值得的优化,这在游戏中通常是这种情况。
请注意,现代 x86 C 编译器通常不使用 x87 FPU 指令进行浮点运算,而是使用标量 SSE 指令,与 x87 指令不同,它有单精度和 double 版本。 (但没有 80 位宽的扩展精度版本。)除了除法之外,这不会产生任何性能差异,但确实意味着结果总是被截断为 float
或 double
每次操作后的精度。在 x87 FPU 上进行数学运算时,这种截断只会在结果写入内存时发生。这意味着 SSE 浮点代码现在具有可预测的结果,而 x87 FPU 代码具有不可预测的结果,因为通常很难预测编译器何时需要将浮点寄存器溢出到内存中以便为其他内容腾出空间。
所以基本上使用 float
而不是 double
除非将浮点值存储在内存中的大数组或其他大型数据结构中,否则不会产生很大的性能差异。
关于c - 旧的 DOS C 编译器是否将 double 实现为 32 位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59094449/