首页 > 解决方案 > 旧的 DOS C 编译器是否将 double 实现为 32 位?

问题描述

我正在阅读 Michael Abrash 的图形编程黑皮书,里面全是关于 3D 图形性能的,所以我惊讶地发现那里的很多 C 代码使用double而不是float. 我们说的是 90 年代早期的计算机(286、386、Pentium)和 MS-DOS C 编译器,那么double在那个时代使用的原因是什么?不float存在还是double's 和float' 的精度与今天不同?

简而言之,为什么double在那个时代被用在性能关键的代码中?

标签: cgraphics3dfloating-pointdos

解决方案


据我所知,没有针对使用 32 位宽的 MS-DOS 的 C 编译器double,而是它们都使用 64 位宽double。上世纪 90 年代初,情况确实如此。根据对本书“实时 3D 浮点数”一章的快速阅读,Michael Abrash 似乎认为任何精度的浮点数学运算在小于 Pentium CPU 的情况下都太慢了。您正在查找的浮点代码要么是用于 Pentium CPU,要么是用于性能无关紧要的非关键路径。对于适用于早期 CPU 的性能关键代码,Abrash 暗示他会改用定点算法。

在很多情况下,使用floatdouble不是实际上并没有太大的区别。有几个原因。首先,如果您没有安装x87 FPU(浮点单元)(在 '486 之前是一个单独的芯片),则使用较低的精度不会提高性能足以使软件模拟浮点运算足够快以有用游戏。第二个是大多数 x87 FPU 操作的性能实际上并没有受到精度的影响。在 Pentium CPU 上,如果以较窄的精度执行除法,则速度会更快。对于早期的 x87 FPU,我不确定精度会影响除法,尽管它可能会影响 80387 上的乘法性能。在所有 x87 FPU 上,无论精度如何,加法的速度都是相同的。

第三是使用的特定 C 数据类型,无论是 32 位float、64 位double,还是long double许多编译器支持的 80 位,实际上都不会影响 FPU 在计算过程中使用的精度。这是因为 FPU 对于它支持的三种不同精度没有不同的指令(或编码)。没有办法告诉它执行float添加或double划分。相反,它以 FPU 控制寄存器中设置的给定精度执行所有算术运算。(或者更准确地说,它执行算术就像使用无限精度,然后将结果四舍五入到设置的精度。)虽然每次使用浮点指令时都可以更改此寄存器,但这会导致大量性能下降,所以编译器从来没有这样做过。相反,他们只是在程序启动时将其设置为 80 位或 64 位精度并保持不变。

现在将 FPU 设置为单精度实际上是 3D 游戏的常用技术。这意味着浮点算术,无论是使用double还是float类型,都将使用单精度算术执行。虽然这最终只会影响浮点除法的性能,但 3D 图形编程往往会在关键代码中进行大量除法(例如透视除法),因此这可能会显着提高性能。

然而,有一种方法可以使用float而不是double可以提高性能,这仅仅是因为 afloat占用了 a 的一半空间double。如果您有很多浮点值,那么必须读取和写入一半的内存会对性能产生显着影响。但是,在 Pentium 或更早的 PC 上,这不会像今天那样导致巨大的性能差异。那时 CPU 速度和 RAM 速度之间的差距并没有那么大,浮点性能也慢了一点。尽管如此,如果不需要额外的精度,那么优化还是值得的,就像游戏中通常的情况一样。

请注意,现代 x86 C 编译器通常不使用 x87 FPU 指令进行浮点运算,而是使用标量 SSE 指令,与 x87 指令不同,它有单精度和双精度版本。(但没有 80 位宽的扩展精度版本。)除了除法之外,这不会产生任何性能差异,但确实意味着每次运算后结果总是被截断为floatdouble精度。在 x87 FPU 上进行数学运算时,这种截断只会在结果写入内存时发生。这意味着 SSE 浮点代码现在具有可预测的结果,而 x87 FPU 代码具有不可预测的结果,因为通常很难预测编译器何时需要将浮点寄存器溢出到内存中以便为其他内容腾出空间。

所以基本上使用float代替double不会产生很大的性能差异,除非将浮点值存储在大数组或内存中的其他大型数据结构中。


推荐阅读