Floating point 浮点与舍入误差的避免

Floating point 浮点与舍入误差的避免,floating-point,rounding,rounding-error,8-bit,Floating Point,Rounding,Rounding Error,8 Bit,众所周知,浮点运算的用户必须注意舍入误差。例如,在几乎所有现代编程语言中,1.0/3*3==1的计算结果都是false。对于非专业人士来说更令人惊讶的是,1.0/10*10==1 然而,有些浮点系统至少似乎能更好地处理这些问题。特别是,我在苹果II和Commodore Vic-20的模拟器中尝试了上述两种测试,在每种情况下,每个表达式都被评估为true。这是违反直觉的:非常原始的系统似乎比更先进的系统工作得更好 在上述测试中,旧的浮点系统是如何得到期望的答案的?假设现代IEEE浮点运算有很好的理

众所周知,浮点运算的用户必须注意舍入误差。例如,在几乎所有现代编程语言中,
1.0/3*3==1
的计算结果都是false。对于非专业人士来说更令人惊讶的是,
1.0/10*10==1

然而,有些浮点系统至少似乎能更好地处理这些问题。特别是,我在苹果II和Commodore Vic-20的模拟器中尝试了上述两种测试,在每种情况下,每个表达式都被评估为true。这是违反直觉的:非常原始的系统似乎比更先进的系统工作得更好

在上述测试中,旧的浮点系统是如何得到期望的答案的?假设现代IEEE浮点运算有很好的理由不这样做,那么它能从中获得什么好处呢?或者换句话说,是什么问题导致旧的浮点系统被抛弃,即使它们能够表示1/10和1/3,而没有最麻烦的舍入错误

编辑:Simon Byrne正确地指出了我上面列出的确切测试,确实传递了IEEE浮点。我不知道我犯了什么错误,无法重现。但有一个失败了,刚刚在Python中尝试过:

>>> 0.1+0.1+0.1 == 0.3
False

这个精确的测试在苹果II上成功了,那么,旧系统是如何得到这个结果的?折衷是什么?

从数学上讲,不可能找到一个能精确表示所有分数的数字系统的基础,因为有无限多的素数。如果你想准确地存储分数,你必须分别存储分子和分母,这使得计算更加复杂

如果将分数存储为单个值,某些操作将引入小错误。如果重复执行这些操作,错误将累积并变得明显

解决此问题有两种方法:

  • 如果你能找到一个公分母,用它来缩放所有的值并使用整数。例如,使用整数美分而不是浮点美元

  • 适当时,将数字四舍五入。很多时候,只要打印一个或两个小于最大精度的浮点数就足够了

测试浮点数是否相等不能像测试整数那样进行。这有点像计算两组人的数量,测试两组人的大小是否相同,测试两瓶牛奶中的牛奶量是否相同。
对于“牛奶”测试,你必须说明数量可能有多大的不同,才能被视为“相等”


Apple II没有浮点硬件,它的基本功能是允许浮点计算。我猜他们在平等测试中包含了这样一个错误界限,或者他们使用了以10为基数的数字(BCD,见harold的评论)。

我猜他们可能只是幸运地得到了你碰巧选择的特定示例。例如,该语句在IEEE754 binary32算术中为true:

>>> import numpy as np
>>> np.float32(0.1) + np.float32(0.1) + np.float32(0.1) == np.float32(0.3)
True
基于此,Apple II没有提供硬件浮点,因此具体细节取决于软件提供的内容(听起来好像不同的软件提供了不同的实现)。如果它们碰巧使用了相同的24位有效位(或给出类似结果的另一个),那么您将看到相同的答案


更新:似乎表明Applesoft Basic确实使用了24位有效位(而不是像前面的链接所建议的那样25–24加隐式1),这可以解释为什么您看到与binary32算术相同的结果。

无法回答历史部分,但您可以将数字存储为非浮点数。Clojure有一个“分数”类,它将无理数存储为分数,因此在绝对必要时才对其进行取整。@Carcigenicate没错,但如果要有效地预测,将数字存储为分数确实需要任意精度,这导致了CPU和内存的需求,在我们使用浮点运算的大多数情况下,这是不可能的。您使用的是什么语言/平台
1.0/3*3==1
1.0/10*10==1
在使用IEEE754 binary64的任何语言/平台上都应该是正确的,这在当今是非常普遍的。@SimonByrne你说得对,刚才试过了,它们通过了,不能重现我犯的错误,编辑了一篇测试肯定失败的文章。BCD浮动过去在8位平台上很流行,但这些平台上没有FPU。根据我对harold的回复,BCD似乎不是这里的解释。相等性测试的错误范围可能是一种可能的解释,或者某种替代的舍入规则。我正试图找出如何区分它们。我的苹果II比IEEE 754算法要老。即使它使用的是二进制或十六进制浮点,舍入规则也可能不同,因此错误情况也会不同。@PatriciaShanahan Yep,仅此而已。现在,在我所知道的一个不同的错误案例中,旧系统实际上工作得更好。但是IEEE在新系统中投入了大量的精力。在哪些错误情况下,IEEE工作得更好,以证明更改的合理性?在BCD中,我猜
1/3+1/3+1/3==1
应该失败。如果您了解了它的工作原理,请为您的问题写一个答案;-)