脚本编写者是否必须考虑舍入错误?

时间:2009-08-31 07:09:49

标签: dynamic-languages scientific-computing

我正在学习C,并且出现了保护数字和舍入错误的想法。脚本语言的实践者(我在这里想到Python和Perl)需要担心这些东西吗?如果他们正在做科学编程怎么办?

9 个答案:

答案 0 :(得分:10)

这取决于。 double在任何地方的表现都相同,所以如果你用双打做数学,你将会遇到任何语言的同样问题。如果你使用原生的任意精度类型,那么不,这不是问题。考虑:

use Math::BigFloat;
my $big   = Math::BigFloat->new("1_000_000_000_000_000_000_000");
my $small = Math::BigFloat->new("0.000000000000000000000000001"); 
print $big + $small;

(或者,如果你真的想隐瞒正在发生的事情:

use bignum;
print 1_000_000_000_000_000_000_000 + 0.000000000000000000000000001

正如所料,这会产生:

1000000000000000000000.000000000000000000000000001

同样如预期的那样,这不是在一条CPU指令中完成的。

答案 1 :(得分:7)

我不得不反对Lutz ......虽然您提到的舍入错误确实存在于Python / Perl / Ruby中,但它们与完全无关与C中实现的语言有关。问题比这更深刻。

与所有数据一样,浮点数在现代计算机上以二进制形式表示。正如存在具有周期性十进制表示的数字(例如,1/3 = 0.333333 ......),也存在具有周期性二进制表示的数字(例如,1/10 = 0.0001100110011 ......)。由于这些数字无法在(有限数量的)计算机内存中精确表示,因此涉及它们的任何计算都会引入错误。

这可以通过使用高精度数学库来解决,这些数学库将数字表示为分数的两个数字(即“numerator = 1,denominator = 10”)或字符串而不是使用本机二进制表示。但是,由于对作为其他东西存储的数字进行任何计算所涉及的额外工作,这些库必然会减慢必须通过它们的数学。

答案 2 :(得分:6)

Python中有几种类型的非整数:

x = 1 / 2

会给你标准浮点数。它的类型是float,它与C中的基本相同,它由硬件处理,它与世界上其他float的问题相同。

但是,还有小数型

from fractions import Fraction

x = Fraction(1, 2)

具有合理数字的精确算术。

如果您要执行舍入,但不满意计算机上有意义数字的数量,或者跨平台可能有所不同,十进制类型是您的朋友:

from decimal import Decimal

x = Decimal('0.5')

如果您愿意,您可以将其精度设置为100位数。或者将银行应用程序设置为2。

只要计算机很愚蠢,我们就可能需要这么多不同的类型。至少,根据Pythonic原则,Python要求您对数字中的内容进行明确选择

此外,一个很大的误解是,精确的算术不会导致舍入问题。任何时候你都可以精确地为用户做一些有用的事情 - 例如将其打印给用户或将多少美元添加到用户的银行账户---您会遇到舍入的“奇怪行为”。这是非整数算术所固有的。

答案 3 :(得分:4)

这取决于您如何表示您的数字,而不是您使用的语言。

例如,如果我在8051中编写所有代码,但实现了一个灵巧的有理数字库,那么舍入不是问题。 1/3仅等于1/3。

但是,如果我使用最新的时髦动态语言,并使用IEE754浮点数,那么IEEE754的所有限制都适用。

如果您需要关心所生成数字的详细信息,那么您需要了解它们的表示以及您选择的工具如何操纵它们。

更新

PDL是一个在Perl中进行科学计算的流行库。

答案 4 :(得分:2)

由于Python和Perl的底层解释器都是用C实现的,因此它们的行为类似于C程序。

对于Python,有SciPYNumPy用于科学计算。

答案 5 :(得分:1)

您可以使用外部模块对Python进行多次精确计算。 official web site中的“多精度数学”部分列出了其中的许多部分。

答案 6 :(得分:0)

嗯,你不能免受Ruby中的浮点错误的影响。例如:

irb(main):033:0> (2.01 * 1000).to_i
=> 2009
irb(main):034:0> ((2.01 * 1000.0) + 0.5).floor
=> 2010

答案 7 :(得分:0)

当然可以!

Python 2.6的一个例子:

>>> 1440.0 / 900.0
1.6000000000000001

正如lutz所说,由于脚本语言通常用C语言实现,因此它们继承了这些“功能”。用语言补偿它们无疑意味着在性能或可移植性方面进行某种权衡。

答案 8 :(得分:0)

当你进行科学编程时,无论你使用哪种编程语言或数字库,你总是要担心舍入错误。

证明:假设您想跟踪宇宙边界附近分子的运动。宇宙的大小约为930亿光年(据我们所知)。分子很小,所以你至少需要纳米级精度(10 ^ -6)。这是50个数量级。

出于某种原因,您需要旋转该分子。这涉及sin()cos()操作以及相乘。乘法不是问题,因为有效数字的数量只是两个操作数的长度的总和。但sin()怎么样?

您必须创建错误等式以确保保留足够的数字,以便最终结果具有已知的最大错误。我不知道任何“简单”的数字库可以自动执行此操作(例如,作为sin()调用的一部分)。这是您需要Matlab或类似内容的地方。