猿问
下载APP

为什么这些数字不相等?

为什么这些数字不相等?

以下代码显然是错误的。有什么问题?


i <- 0.1

i <- i + 0.05

i

## [1] 0.15

if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")

## i does not equal 0.15


慕后森
浏览 87回答 4
4回答

守着一只汪

一般(语言不可知)的原因由于并非所有数字都可以在IEEE浮点运算中完全表示(几乎所有计算机都用来表示十进制数并使用它们进行数学计算的标准),因此您并不总能得到预期的结果。这尤其正确,因为一些简单的有限小数(例如0.1和0.05)的值在计算机中没有完全表示,因此对它们的算术结果可能不会给出与“”的直接表示相同的结果。已知的“答案。这是众所周知的计算机算术限制,并在以下几个地方进行了讨论:R FAQ专门讨论它:R FAQ 7.31Patrick Burns的R Inferno致力于解决这个问题的第一个“圈子”(从第9页开始)大卫·戈德堡,“什么每台计算机科学家应该知道关于浮点运算,”&nbsp;ACM计算概观&nbsp;23,1(1991年至1903年),5-48&nbsp;DOI> 10.1145 / 103162.103163(修订版也可)浮点指南 - 每个程序员应该知道的浮点运算0.30000000000000004.com比较跨编程语言的浮点运算几个Stack Overflow问题包括为什么浮点数不准确?为什么十进制数不能用二进制表示?浮点数学是否破碎?“浮点数不准确”的规范重复(关于此问题的规范答案的元讨论)比较标量对此的标准解决方案R不是使用==,而是all.equal功能。或者更确切地说,all.equal如果存在任何差异,则提供大量有关差异的详细信息isTRUE(all.equal(...))。if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")产量i equals 0.15使用all.equal代替的更多示例==(最后一个示例应该表明这将正确显示差异)。0.1+0.05==0.15#[1] FALSEisTRUE(all.equal(0.1+0.05, 0.15))#[1] TRUE1-0.1-0.1-0.1==0.7#[1] FALSEisTRUE(all.equal(1-0.1-0.1-0.1, 0.7))#[1] TRUE0.3/0.1 == 3#[1] FALSEisTRUE(all.equal(0.3/0.1, 3))#[1] TRUE0.1+0.1==0.15#[1] FALSEisTRUE(all.equal(0.1+0.1, 0.15))#[1] FALSE更多细节,直接从类似问题的答案中复制:您遇到的问题是浮点在大多数情况下不能完全表示小数分数,这意味着您经常会发现完全匹配失败。当你说:当你说:1.1-0.2#[1] 0.90.9#[1] 0.9您可以通过十进制找出它的真实想法:sprintf("%.54f",1.1-0.2)#[1] "0.900000000000000133226762955018784850835800170898437500"sprintf("%.54f",0.9)#[1] "0.900000000000000022204460492503130808472633361816406250"您可以看到这些数字不同,但表示有点笨拙。如果我们用二进制(嗯,十六进制,等价)来看它们,我们会得到更清晰的图像:sprintf("%a",0.9)#[1] "0x1.ccccccccccccdp-1"sprintf("%a",1.1-0.2)#[1] "0x1.ccccccccccccep-1"sprintf("%a",1.1-0.2-0.9)#[1] "0x1p-53"您可以看到它们的区别2^-53,这很重要,因为这个数字是两个数值之间的最小可表示差异,其值接近1,因为这是。通过查看R的机器字段,我们可以找出任何给定的计算机这个最小的可表示数字是什么:&nbsp;?.Machine&nbsp;#....&nbsp;#double.eps&nbsp; &nbsp; &nbsp;the smallest positive floating-point number x&nbsp;&nbsp;#such that 1 + x != 1. It equals base^ulp.digits if either&nbsp;&nbsp;#base is 2 or rounding is 0; otherwise, it is&nbsp;&nbsp;#(base^ulp.digits) / 2. Normally 2.220446e-16.&nbsp;#....&nbsp;.Machine$double.eps&nbsp;#[1] 2.220446e-16&nbsp;sprintf("%a",.Machine$double.eps)&nbsp;#[1] "0x1p-52"您可以使用此事实创建一个“近似等于”函数,该函数检查差异是否接近浮点中的最小可表示数字。事实上,这已经存在:all.equal。?all.equal#....#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.#....#all.equal(target, current,#&nbsp; &nbsp; &nbsp; tolerance = .Machine$double.eps ^ 0.5,#&nbsp; &nbsp; &nbsp; scale = NULL, check.attributes = TRUE, ...)#....所以all.equal函数实际上是检查数字之间的差异是两个尾数之间最小差异的平方根。这个算法在称为非正规数的极小数字附近有点滑稽,但你不必担心这一点。比较矢量上面的讨论假设了两个单值的比较。在R中,没有标量,只有向量和隐式向量化是语言的强项。为了比较矢量元素的值,先前的原则成立,但实现略有不同。==向量化(进行逐元素比较),同时all.equal将整个向量作为单个实体进行比较。使用前面的例子a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)b <- c(0.15,&nbsp; &nbsp; &nbsp;0.7,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;3,&nbsp; &nbsp; &nbsp; &nbsp;0.15)==没有给出“预期的”结果,all.equal也没有按元素执行a==b#[1] FALSE FALSE FALSE FALSEall.equal(a,b)#[1] "Mean relative difference: 0.01234568"isTRUE(all.equal(a,b))#[1] FALSE相反,必须使用在两个向量上循环的版本mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)#[1]&nbsp; TRUE&nbsp; TRUE&nbsp; TRUE FALSE如果需要它的功能版本,可以编写它elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})这可以称为公正elementwise.all.equal(a, b)#[1]&nbsp; TRUE&nbsp; TRUE&nbsp; TRUE FALSE或者,all.equal您可以只复制相关的内部函数all.equal.numeric并使用隐式向量化,而不是包含更多的函数调用:tolerance = .Machine$double.eps^0.5# this is the default tolerance used in all.equal,# but you can pick a different tolerance to match your needsabs(a - b) < tolerance#[1]&nbsp; TRUE&nbsp; TRUE&nbsp; TRUE FALSE

吃鸡游戏

添加到Brian的评论(这是原因)你可以通过all.equal改为使用:# i <- 0.1# i <- i + 0.05# i#if(all.equal(i, .15)) cat("i equals 0.15\n") else cat("i does not equal 0.15\n")#i equals 0.15Per Joshua警告这里是更新的代码(谢谢Joshua):&nbsp;i <- 0.1&nbsp;i <- i + 0.05&nbsp;iif(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines&nbsp; &nbsp; cat("i equals 0.15\n")&nbsp;} else {&nbsp; &nbsp; cat("i does not equal 0.15\n")}#i equals 0.15

慕的地8271018

这是hackish,但很快:if(round(i,&nbsp;10)==0.15)&nbsp;cat("i&nbsp;equals&nbsp;0.15")&nbsp;else&nbsp;cat("i&nbsp;does&nbsp;not&nbsp;equal&nbsp;0.15")

汪汪一只猫

dplyr::near()是一个测试两个浮点数向量是否相等的选项。这是文档中的示例:sqrt(2)&nbsp;^&nbsp;2&nbsp;==&nbsp;2#>&nbsp;[1]&nbsp;FALSElibrary(dplyr)near(sqrt(2)&nbsp;^&nbsp;2,&nbsp;2)#>&nbsp;[1]&nbsp;TRUE该功能具有内置公差参数:tol = .Machine$double.eps^0.5可以调整。默认参数与默认参数相同all.equal()。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

R语言
我要回答