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

发布于 2024-08-02 22:46:35 字数 84 浏览 5 评论 0原文

我正在学习 C,出现了保护数字和舍入误差的想法。脚本语言(我这里指的是Python和Perl)的从业者需要担心这些东西吗?如果他们正在进行科学编程怎么办?

I'm studying C, and the idea of guard digits and rounding errors came up. Do practitioners of scripting languages (I'm thinking of Python and Perl here) need to worry about this stuff? What if they are doing scientific programming?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

怂人 2024-08-09 22:46:35

这取决于。 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 指令中完成的。

It depends. doubles behave the same everywhere, so if you do math with doubles, you are going to have the same problem with any language. If you use a native arbitrary precision type, then no, it's not a problem. Consider:

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;

(Or, if you really want to hide what's going on:

use bignum;
print 1_000_000_000_000_000_000_000 + 0.000000000000000000000000001

)

As expected, this yields:

1000000000000000000000.000000000000000000000000001

Also as expected, this is not done in one CPU instruction.

岛歌少女 2024-08-09 22:46:35

我不得不不同意 Lutz 的观点……虽然你提到的舍入错误确实存在于 Python/Perl/Ruby 中,但它们与 C 中实现的语言完全无关。问题更深入比那个。

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

这可以通过使用高精度数学库来解决,该数学库将数字表示为分数的两个数字(即“分子 = 1,分母 = 10”)或字符串,而不是使用本机二进制表示形式。然而,由于对作为其他内容存储的数字进行任何计算需要额外的工作,这些库必然会减慢必须经过它们的任何数学运算。

I would have to disagree with Lutz... While the rounding errors you mentioned do exist in Python/Perl/Ruby, they have absolutely nothing to do with the languages being implemented in C. The problem goes deeper than that.

Floating-point numbers, like all data, are represented in binary on modern computers. Just as there are numbers with periodic decimal representations (e.g., 1/3 = 0.333333...), there are also numbers with periodic binary representations (e.g., 1/10 = 0.0001100110011...). Since these numbers cannot be exactly represented in (a finite amount of) computer memory, any calculations involving them will introduce error.

This can be worked around by using high-precision math libraries, which represent the numbers either as the two numbers of a fraction (i.e., "numerator = 1, denominator = 10") or as string instead of using a native binary representation. However, because of the extra work involved in doing any calculations on numbers that are being stored as something else, these libraries necessarily slow down any math that has to go through them.

无力看清 2024-08-09 22:46:35

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 要求您对您想要从数字中得到什么做出明确的选择

此外,认为精确的算术不会导致舍入问题是一个很大的误解。每当您对精确值进行舍入以对用户做一些有用的事情时(例如,将其打印给用户或将那么多美元添加到用户的银行帐户中),您都会遇到舍入的“奇怪行为”。这是非整数算术所固有的。

There are several types of non-integer numbers in Python:

x = 1 / 2

would give you the standard float. Its type is float, it's essentially the same as in C, it's handled by the hardware, and it has the same problems as every other float in the world.

However, there is also fractional type:

from fractions import Fraction

x = Fraction(1, 2)

which has exact arithmetics with rational numbers.

In the case you want to perform rounding, but are not satisfied with the number of meaningful digits on your computer, or the fact that it could be different across platforms, decimal type is your friend:

from decimal import Decimal

x = Decimal('0.5')

You'll be able to set its precision to, say, 100 digits, if you want to. Or set it to 2 for bank applications.

As long as computers are stupid, we'll probably need this many different types. At least, in accordance with Pythonic principles, Python requires you to make an explicit choice about what you want from your numbers.

Moreover, it's a big misunderstanding that exact arithmetics doesn't lead to problems with rounding. Any time you round exact value to do something useful for a user to it --- e.g. print it to the user or add that many dollars to user's bank account --- you encounter "strange behavior" of rounding. This is inherent to non-integer arithmetics.

甩你一脸翔 2024-08-09 22:46:35

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

例如,如果我用 8051 汇编编写所有代码,但实现了一个灵活的有理数库,那么四舍五入就不是问题。 1/3只等于1/3。

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

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

更新:

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

It depends on how you represent your numbers, not the language you use.

For example, if I write all my code in 8051 assember, but have implemented a slick rational number library, then round off isn't a problem. 1/3 is only equal to 1/3.

However if I am using the latest snazzy dynamic language, and it uses IEE754 floats, then all the limitations of IEEE754 apply.

If you need to care about the details of the numbers you generate, then you need to understand their representation and how they are manipulated by your choice of tools.

Update:

PDL is a popular library for doing scientific computing in Perl.

鱼忆七猫命九 2024-08-09 22:46:35

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

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

Since the underlying intepreter of both CPython and Perl are implemented in C, they behave like a C program.

For Python there is SciPY and NumPy for scientific computation.

梅倚清风 2024-08-09 22:46:35

您可以使用 Python 和外部模块进行多种精度计算。 官方网站中的多精度数学部分列出了其中的许多内容。

You can do multiple precision calculations with Python, with external modules. The Multi Precision Math section in the official web site lists many of them.

君勿笑 2024-08-09 22:46:35

好吧,Ruby 中的浮点错误也无法幸免。例如:

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

Well, you're not immune to floating point errors in Ruby. For example:

irb(main):033:0> (2.01 * 1000).to_i
=> 2009
irb(main):034:0> ((2.01 * 1000.0) + 0.5).floor
=> 2010
稀香 2024-08-09 22:46:35

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

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

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

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

When you do scientific programming, you'll always have to worry about rounding errors, no matter which programming language or numeric library you use.

Proof: Say you want to track the movement of a molecule near the border of the universe. The size of the universe is about 93 billion light-years (as far as we know). A molecule is pretty tiny, so you'll want at least nanometer precision (10^-6). That's 50 orders of magnitude.

For some reason, you need to rotate that molecule. That involves sin() and cos() operations and a multiply. The multiply is not an issue since the number of valid digits is simply the sum of the length of both operands. But how about sin()?

You must create the error equation to be sure that you keep enough digits so that the final result will have a know maximum error. I don't know any "simple" numeric library which can do this operation automatically (say, as part of the call to sin()). This is where you need Matlab or something similar.

全部不再 2024-08-09 22:46:35

当然有!

Python 2.6 的一个例子:

>>> 1440.0 / 900.0
1.6000000000000001

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

Sure they do!

An example from Python 2.6:

>>> 1440.0 / 900.0
1.6000000000000001

As lutz says, since scripting languages are often implemented in C, they inherit these "features". Compensating for them in the language would undoubtedly mean some kind of trade-off in performance or portability.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文