浮点数 |
|
浮点数作为 Lua 中唯一的数值类型,主要问题在于 **大多数程序员不理解浮点数**。浮点数比整数(定点数)复杂得多。大多数程序员对浮点数的认知模型是,运算结果有时会存在非常小的误差。因此,人们会感到不安。但这种认知模型是错误的;将浮点运算建模为“正确答案 + 噪声”是错误的,尤其是在处理最常见的浮点类型 IEEE-754 时。
解决这个问题的方案是教育。在文档、网站、常见问题解答、维基百科等中,应该更多地强调双精度浮点数的基本整数向后兼容行为。
在继续之前,需要注意的是,尽管浮点数通常用作 Lua 中的数值类型,但 Lua 并不依赖于浮点数,而是可以编译成任何数值类型,例如单精度浮点数、整数,甚至一些非常不熟悉的数值表示[*5]。当然,例如除法的语义在使用整数数值类型编译 Lua 时会发生显著变化,但在嵌入式处理器上,这可能不会让人感到意外。[*1] ARM、Coldfire 和各种嵌入式 MIPS 版本都没有 FPU,但它们被 _非常_ 广泛使用。[*2]
以下是关于 (IEEE 754) 64 位双精度浮点数 (“double”) 与 32 位整数的一些要点
总结:从精度、范围和“向后兼容性”的角度来看,64 位精度浮点运算优于 32 位整数运算。不过请参阅注意事项部分。
IEEE 754 标准定义了基本的数学运算,包括 + - * (乘法) / (除法),以产生尽可能接近精确数学答案的结果。特别是,如果答案可以精确地表示为双精度浮点数,那么该结果就是答案;否则,有两个最接近精确答案的双精度浮点数(一个略高于,一个略低于),根据舍入模式选择其中一个。
对于涉及整数的运算,大多数运算都是精确的。如果我计算 2+5,那么因为精确的数学答案是 7,这也是 IEEE 754 的答案。
诚然,英特尔的奔腾设计排名第三(因为它只有很少的 FP 寄存器)。但英特尔正在迎头赶上。
唯一剩下的性能问题是浮点数到整数的转换。无论你是否喜欢,内存加载指令都使用地址和字节偏移量(即两个整数值)进行操作。因此,如果 CPU 的浮点数到整数转换性能很差,使用浮点数而不是整数的任何性能优势都将付诸东流。一些说法指出,浮点数到整数的转换会对性能产生毁灭性的影响 [1]。(对于 gcc-3.4 和大约 2006 年的 AMD 处理器,这个问题已经不存在,但请自行测试。从该链接编译基准测试很容易。)
对于使用这些现代高端台式机 CPU 的用户来说,唯一剩下的主要潜在问题是内存带宽和内存使用。由于 Lua 中这些对象的单元(联合结构)大小,这些考虑实际上是无关紧要的。
关于这一点的硬数据会很好,例如在各种(或一种)架构下配置单精度和双精度浮点数的适当联合的 sizeof()
。
-- 在 8 字节结构对齐的世界中,它们实际上并不占用更多空间,但是如果由于 FP 数字的大小,对象结构的大小超过 64 位,那么大小问题就变得非常重要。FP 数字在 CPU 缓存中也占用更多空间 - 不仅仅是寄存器会受到影响。 -- ChuckAdams
总结:大约在 2001 年,您平均入门级的 10 美元 CPU 芯片能够以与整数运算一样快和一样精确的速度(甚至更快)计算 64 位双精度数。使用这些 CPU 的用户在 Lua 中仅使用双精度浮点数不会付出任何代价,并且可能会获得好处。使用较小芯片的用户可能被迫在 Lua 中仅使用整数运算。
-- 正如下面所述,大多数 ARM 芯片(最广泛使用的嵌入式 CPU 之一)没有硬件浮点支持。这意味着每次在 int 和 float 之间的转换,以及每次算术运算,都是一个函数调用(通常是内联的)。双精度数更糟糕。当您考虑对 NaN 和非规格化数字的检查时,与整数相比,开销相当大(是的,我已经分析过)。--BenStJohn?
虽然 Lua 的数字类型可以构建为整数,但在使用之前建议进行分析,因为(即使在仅整数的处理器上)速度提升可能不像想象的那么大,而丢失浮点数可能会使某些操作变得更加复杂。还要注意 Lua 5.3 内置了显式整数支持。
eLua http://www.eluaproject.net/ 是 Lua 的一个分支,用于一些微控制器,它有一个可选的整数构建。但是,需要注意的是,标准 Lua 也非常容易在 ARM7TDMI 等内核上构建 - 因为源代码是标准 C。如果需要,可以应用整数更改。
printf
实现可能无法准确地处理打印浮点数。信不信由你,有些可能会错误地打印整数(它们是浮点数)。这可能会表现为在 Lua 中错误地打印某些数字。QNX 存在此问题 [2]。唯一明智的解决方法是向您的供应商投诉和/或用有效的 printf
替换它。
这些没有特别的顺序
-- MartinHollis(原作者)