第一轮回 浮点陷阱

本文深入探讨了浮点数运算中的精度问题及其在编程过程中的影响,通过比较不同历史时期的代码实现,揭示了浮点数错误的本质,并介绍了如何在现代编程语言R中优雅地处理此类问题。文章还强调了理解数值计算的局限性和正确使用比较操作的重要性。
摘要由CSDN通过智能技术生成

在穿过黄泉路之后,我们到了第一站:善良异教徒之家.这些人供奉于”对浮点数无知之神”,这些人认为:

                                               .1 == .3 / 3
 

是正确的.

他们同样确切地认为: 

                                          seq(0, 1, by=.1) == .3

只有一个值是真值.

但是你不应该认为:               

                              unique(c(.3, .4 - .1, .5 - .2, .6 - .3, .7 - .4))

的长度为1.

我的第一个程序写于石器时代,一段关于求二次方程的代码.当时代码的媒介还是纸带.而打孔机上是没有后退键的---一旦你打下了洞,洞就在那,无法修改.当你在纸带的最后犯了点小错误,你就得扔掉,然后重新打洞,打洞,打洞.我对这些都信手拈来的.

 

煞费苦心地请求额外的一些纸带充满乐趣,短暂的快乐.接下来就是把这些纸带放入一个由计算器系统监控的篮子里.几个小时后,程序的输出会放在归档文件中.当然,程序中会有错误的.经过又一段时间的煎熬(比之前的相对短一点啦),我的纸带又放入篮子里了.

 

没几次和计算机交互,我就会意识到计算机仅仅会告诉我程序中的第一个错误.终于,在第三天,结果中不再有错误提示信息.但是,这是一个答案---一个错误的答案.我的程序是一个简单的二次方程求解,答案很简单,就是2和3,可是程序跑出来的结果是:1.999997和3.000001.我经过了这么多的曲折,却得到了一个错误的答案.

 

我现在可以很快地写出一个二次方程式求解:

> quadratic.formula <- function (a, b, c)
{
  rad <- b^2 - 4 * a * c
  if(is.complex(rad) || all(rad >= 0)) 
  {
    rad <- sqrt(rad)
  }   
  else 
  {
   rad <- sqrt(as.complex(rad))
  }
  cbind(-b - rad, -b + rad) / (2 * a)
}

> quadratic.formula(1, -5, 6)
    [,1] [,2]
[1,] 2    3

> quadratic.formula(1, c(-5, 1), 6)
     [,1]            [,2]
[1,] 2.0+0.000000i   3.0+0.000000i
[2,] -0.5-2.397916i  -0.5+2.397916i</span>

这个程序比我之前写的那个更有普遍意义,更能说明得到正确答案2和3的问题.R仅仅是输出,所以大多数数值错误是被隐藏起来的.我们可以通过减去正确答案来看它是怎么错误的.

> quadratic.formula(1, -5, 6) - c(2, 3)
    [,1] [,2]
[1,] 0    0

好极了,这种情况下我们得到的答案是正确的.但是如果我们将问题稍微改变一点,错误就出现了:

> quadratic.formula(1/3, -5/3, 6/3)
    [,1] [,2]
[1,] 2    3

> print(quadratic.formula(1/3, -5/3, 6/3), digits=16) [,1] [,2]
[1,] 1.999999999999999 3.000000000000001

> quadratic.formula(1/3, -5/3, 6/3) - c(2, 3)
     [,1]          [,2]
[1,] -8.881784e-16 1.332268e-15


R输出答案有时是美好的祝福,同时也是一个诅咒.R在隐藏数值错误方面非常棒,以至于我们很容易忘记错误的存在.不要忘记.


无论何时,只要你在做浮点运算,即使是很简单的,你都应该假定这里将会有数值错误.如果恰好没有错误,将它视为快乐的意外---不是你原因.你可以用函数 all.equal()代替’==’ 去检查浮点数是否相等.


如果有时一个逻辑整数被计算过,你需要用函数round()来确保它真的是一个整数.


不要把数值错误和一个错误搞混了,一个错误是一次计算被错误地执行了.数值错误是由于有限的数字表达(输出)造成的明显的噪声.例如:1/3被表达成33%.


我们再看了善良异教徒信仰的另一面---输出的即是全部.

> 7/13 - 3/31
[1] 0.4416873


R的输出---默认情况下---是一个手工的截取,不是计算机运行结果的全部.

> print(7/13 - 3/31, digits=16)
[1] 0.4416873449131513


很多总结类函数甚至对它们的输出限制的更多.

> summary(7/13 - 3/31)
Min.    1st Qu.  Median  Mean    3rd Qu.  Max.
0.4417  0.4417   0.4417  0.4417  0.4417   0.4417</span>

因有限的算术表达造成的数值错误不仅使我们对问题感到困惑,有时也会让我们对问题感到困惑.在数学中,矩阵的秩是一些明确的整数.在计算中,矩阵的秩是一个模糊的概念.由于特征值不必是明确的零或者非零,秩不必是一个确定的数.


我们下降到第一站的边缘,这里由米诺斯守卫,他咬着自己的牙齿.他的尾巴在自己周围摇动着,次数标示着他面前的罪人的级别。


完.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值