首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免精度损失的最佳算法?

问避免精度损失的最佳算法?
EN

Stack Overflow用户
提问于 2009-02-02 03:39:56
回答 4查看 3.2K关注 0票数 7

我最近收到一份家庭作业,要求我们采用在计算机上执行时可能会造成精度损失的表达式,并对其进行修改,以避免这种损失。

不幸的是,做这件事的方向还不是很清楚。通过观察正在执行的各种示例,我知道有一些方法可以做到这一点:使用泰勒级数,如果涉及平方根,则使用共轭,或者在两个分数相减时找到一个公分母。

然而,我很难准确地注意到什么时候会出现精度损失。到目前为止,我唯一能确定的是,当你减去两个接近相同的数字时,精度会下降,因为高位数字很重要,而且你会在四舍五入时失去这些数字。

我的问题是,我应该寻找的其他一些常见情况是什么,以及什么被认为是处理它们的“好”方法?

例如,这里有一个问题:

代码语言:javascript
复制
f(x) = tan(x) − sin(x)  when x ~ 0

在这三种选择中,最好和最差的算法是什么:

代码语言:javascript
复制
(a) (1/ cos(x) − 1) sin(x),
(b) (x^3)/2
(c) tan(x)*(sin(x)^2)/(cos(x) + 1).

我知道当x接近零时,tan(x)和sin(x)几乎相同。我不明白为什么这些算法中的任何一个在解决问题时是更好或更差。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-02-02 03:50:57

另一个通常使用的经验法则是:当添加一长串数字时,从最接近零的数字开始添加,并以最大的数字结束。

解释为什么这是好的是有点棘手。当你将小数和大数相加时,它们有可能会被完全丢弃,因为它们比大数的当前尾数中的最低位数小。以这种情况为例:

代码语言:javascript
复制
a = 1,000,000;
do 100,000,000 time:
   a += 0.01;

如果0.01小于最低尾数位,则循环不执行任何操作,最终结果是== 1,000,000,但如果您这样做:

代码语言:javascript
复制
a = 0;
do 100,000,000 time:
   a += 0.01;
a += 1,000,000;

比起小数字慢慢增长,你更有可能最终得到接近== 2,000,000的值,这是正确的答案。

这当然是一个极端的例子,但我希望您能理解。

票数 5
EN

Stack Overflow用户

发布于 2009-02-02 03:53:36

当我还是一名大学生的时候,我不得不上一门数学课,那是非常痛苦的。无论如何,IEEE 754是现代CPU通常实现的浮点标准。理解它的基础知识是很有用的,因为这会给你很多关于什么不应该做的直觉。它的简单解释是,计算机以类似于基数2科学记数法的形式存储浮点数,指数和尾数具有固定数量的数字(位)。这意味着一个数字的绝对值越大,它就越不能精确地表示出来。对于IEEE754中的32位浮点数,一半可能的位模式表示介于-1和1之间,即使最多10^38的数字可以用32位浮点数表示。对于大于2^24 (大约1670万)的值,32位浮点型不能精确地表示所有整数。

这对您来说意味着您通常希望避免以下情况:

当最终答案预期较小时,具有中间值的

  1. 较大。将小数转换为大数,或将大数转换为
  2. Adding/subtracting。例如,如果您编写了如下内容:

for(float index = 17000000;index < 17000001;index++) {}

这个循环永远不会终止,因为17,000,000 +1被四舍五入为17,000,000。如果你有这样的东西:

代码语言:javascript
复制
float foo = 10000000 - 10000000.0001

由于舍入误差,foo的值应该是0,而不是-0.0001。

票数 4
EN

Stack Overflow用户

发布于 2009-12-08 23:04:10

我的问题是,我应该寻找的其他一些常见情况是什么,以及什么是处理它们的“好”方法?

有几种方式可能会造成严重的甚至灾难性的精度损失。

最重要的原因是浮点数的位数有限,而e.g..doubles的位数是53位。这意味着如果你有“无用的”数字,这些数字不是解决方案的一部分,但必须存储,你就会失去精度。

例如(我们使用decimal类型进行演示):

2.598765000000000000000000000100 -

2.598765000000000000000000000099

有趣的部分是100-99 =1的答案。因为2.598765在两种情况下都是相等的,所以它不会改变结果,但会浪费8位数字。更糟糕的是,因为计算机不知道这些数字是无用的,所以它被迫存储它,并在它之后填满21个0,浪费了所有29个数字。不幸的是,没有办法绕过它的差异,但还有其他情况,例如exp(x)-1,这是一个在物理中经常出现的函数。

0附近的exp函数几乎是线性的,但它强制将1作为前导数字。因此,对于12个有效数字exp(0.001)-1 = 1.00100050017 -1= 1.00050017e-3

如果我们改用函数expm1(),请使用泰勒级数:

1+x +x^2/2 +x^3/6... -1 =

X +x^2/2 +x^3/6 =:expm1(x)

expm1(0.001) = 1.00500166667e-3

好多了。

第二个问题是具有非常陡峭的斜率的函数,如π/2附近的x的正切。tan(11)的斜率为50000,这意味着之前由舍入误差引起的任何小偏差都将被放大50000倍!或者你有奇点,如果结果接近0/0,这意味着它可以有任何值。

在这两种情况下,您都创建了一个替代函数,从而简化了原始函数。强调不同的解决方案方法是没有用的,因为如果不经过培训,您根本看不到问题所在。

一本值得学习和训练的好书: Forman S. Acton: Real Computing made

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/502122

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档