python 不等于None 不等于空_眼见不为实:为什么29不等于29?

今天有一个小伙伴给我发来了一个截图如下。

93f23a91dfb95ce83ed1ef9e21e309fe.png

我下意识的说了一句:0.58*50不等于29。

8d2fed3d8dfef6cde4661b57bdc4fce6.png

感概,有时候,眼见也不一定为实。这就是一个浮点运算导致的「算不准」问题。

在历史上,其实已经有过类似因为浮点运算导致的事故。

1994 年,英特尔的奔腾微处理器芯片的浮点计算单元出现了一个 Bug。在某些情况下,浮点除法并不能返回正确的结果。例如 4195835.0/3145727.0 产生的是 1.33374 而不是 1.33382。虽然这个 Bug 可能对绝大多数人没有影响,但是市面上 500 万左右流通的芯片都存在该缺陷,随着整个事件的发酵,最终这个 Bug 给 intel 造成了 4.75 亿美元的损失。这次事件也被称为「Pentium FDIV bug」而载入了 Bug 史册。

da04c0534f959b8537578d224cf35042.png

R 语言中的实际示例

我们分别在 R 中计算 0.58*50,0.29*100,然后让他们和 29 互相比一比。

29)

结果如下所示:

29)

我们看到屏幕上显示的 a ,  b和 c 都是29,但是如果用==进行一下比较,发现0.58*500.29*100竟然都不等于29。嗯,很迷。

浮点数的精度和计算问题,在任何编程语言里都类似。

浮点数在计算机中不能以任意精读存储,都会有一个准确性的限制,通过有限的连续字符来保存浮点数。

目前常见的浮点格式包括:单精度,双精度和扩展双精度。他们的准确性从前到后依次提高,其中单精度可以用于一般计算,双精度用于科学计算。在 R 中就是采用了双精度来对浮点数进行保存。如下所示:

> (d 10)
[1] 10
> class(d)
[1] "numeric"
> typeof(d)
[1] "double"

我们让 d 等于 10,通过 class 可以看出它是一个数值型变量,如果用 typeof 查看,就会发现他的存储模式(storage mode)是一个双精度浮点型数字。

这里又涉及到了存储模式和类型的问题。在R中class返回的是一个对象的高级类,而typeof返回的是对象的内部低级类。如果用calss查看一个数据框,其返回结果就是他的类(calss)为数据框,如果使用typeof查看,返回的结果就是内部类型为列表,如下所示。(嗯,想起了果子老师课上讲的,data.frame 就是一种特殊的 list)

> class(iris)
[1] "data.frame"
> typeof(iris)
[1] "list"

继续回来说浮点数的比较。因为精度问题,在R语言中使用 == 进行浮点数的比较是非常危险的。

就像文章开头的例子,此时假设你有一个 if 语句,想进行一下结果的判断。如果判断条件是 0.29*100 == 29,就会返回 F。因为 0.29*100 的实际值在计算机中比 29 要小一些。当你使用 as.interger去处理的时候,结果为28。

0.29*

这也就是为什么rep(1,29)有29个数字,但是rep(1,0.29*100)有28个数字。

如何避免浮点数计算的坑

如何解决这个问题呢?在 R 语言中有一个函数叫做 all.equal,虽然名字是 equal,但是他其实是 almost equal。

这让我不禁想到了一款果子和洲更(当然还有我)都在用全面屏 Almost 手机。

449e478ffa2f2af16c6e18736cf06b3a.png

all.equal() 的功能是 Test If Two Objects Are (Nearly) Equal。也就是它可以略微忍受两个对象之间有一丢丢差别。如下所示:

29)

你可能会问,这个一丢丢究竟是多少呢?

通过帮助文档,我们可以发现这个一丢丢实际是tolerance = sqrt(.Machine$double.eps)。如果实际运行一下会发现.Machine$double.eps 等于2.220446e-16,那么sqrt(.Machine$double.eps)就是 1.490116e-08。

这里的.Machine是一个存储了 R 正在运行机器的数值特性的变量,其中包括了最小正浮点数(double.eps)。因此,以后想进行类似的比较,记得使用all.equal()。另外,多说一句,在 if 判断中不要直接使用它,而是写成 isTRUE(all.equal(....))

回到最开始的问题,如果确实需要 rep(1,rep(1,0.29*100))返回 29 个数字,就可能需要用到取整。

在 R 中有3种常见的取整,分别是:

  • 向下取整:floor()

  • 向上取整:ceiling()

  • 四舍五入:round()

此外还有一种不常用的「向0看齐」取整方法trunc()

他们的差别如下:

4, by = 

因此通过rep(1,round(0.58*50))就可以得到和rep(1,29)一致的结果了。


参考资料:请点击阅读原文,有一个特别长的文档,叫作"Frequently Asked Questions on R"。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值