我最近收到一份家庭作业,要求我们采用在计算机上执行时可能会造成精度损失的表达式,并对其进行修改,以避免这种损失。
不幸的是,做这件事的方向还不是很清楚。通过观察正在执行的各种示例,我知道有一些方法可以做到这一点:使用泰勒级数,如果涉及平方根,则使用共轭,或者在两个分数相减时找到一个公分母。
然而,我很难准确地注意到什么时候会出现精度损失。到目前为止,我唯一能确定的是,当你减去两个接近相同的数字时,精度会下降,因为高位数字很重要,而且你会在四舍五入时失去这些数字。
我的问题是,我应该寻找的其他一些常见情况是什么,以及什么被认为是处理它们的“好”方法?
例如,这里有一个问题:
f(x) = tan(x) − sin(x) when x ~ 0在这三种选择中,最好和最差的算法是什么:
(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)几乎相同。我不明白为什么这些算法中的任何一个在解决问题时是更好或更差。
发布于 2009-02-02 03:50:57
另一个通常使用的经验法则是:当添加一长串数字时,从最接近零的数字开始添加,并以最大的数字结束。
解释为什么这是好的是有点棘手。当你将小数和大数相加时,它们有可能会被完全丢弃,因为它们比大数的当前尾数中的最低位数小。以这种情况为例:
a = 1,000,000;
do 100,000,000 time:
a += 0.01;如果0.01小于最低尾数位,则循环不执行任何操作,最终结果是== 1,000,000,但如果您这样做:
a = 0;
do 100,000,000 time:
a += 0.01;
a += 1,000,000;比起小数字慢慢增长,你更有可能最终得到接近== 2,000,000的值,这是正确的答案。
这当然是一个极端的例子,但我希望您能理解。
发布于 2009-02-02 03:53:36
当我还是一名大学生的时候,我不得不上一门数学课,那是非常痛苦的。无论如何,IEEE 754是现代CPU通常实现的浮点标准。理解它的基础知识是很有用的,因为这会给你很多关于什么不应该做的直觉。它的简单解释是,计算机以类似于基数2科学记数法的形式存储浮点数,指数和尾数具有固定数量的数字(位)。这意味着一个数字的绝对值越大,它就越不能精确地表示出来。对于IEEE754中的32位浮点数,一半可能的位模式表示介于-1和1之间,即使最多10^38的数字可以用32位浮点数表示。对于大于2^24 (大约1670万)的值,32位浮点型不能精确地表示所有整数。
这对您来说意味着您通常希望避免以下情况:
当最终答案预期较小时,具有中间值的
for(float index = 17000000;index < 17000001;index++) {}
这个循环永远不会终止,因为17,000,000 +1被四舍五入为17,000,000。如果你有这样的东西:
float foo = 10000000 - 10000000.0001由于舍入误差,foo的值应该是0,而不是-0.0001。
发布于 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
https://stackoverflow.com/questions/502122
复制相似问题