四次元口袋 2019-07-24 17:08 采纳率: 0%
浏览 639

为什么这些数字不相等?

下面的代码明显不对,但是到底哪里不对?

i <- 0.1
i <- i + 0.05
i## [1] 0.15if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")## i does not equal 0.15
  • 写回答

4条回答 默认 最新

  • weixin_38166163 2019-07-25 09:38
    关注

    一般情况下是编程语言问题
    由于不是所有的数字都可以用 IEEE 浮点算术表示(几乎所有的计算机都使用浮点算术表示十进制数并进行数学运算) ,所以你不会总是得到想要的结果。这是绝对的,因为有些值是简单的,有限的小数(如0.1和0.05)没有在计算机中准确地表示,所以对它们的算术结果可能不会给出与“已知”答案的直接表示相同的结果。
    比较标量
    在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] FALSE
    isTRUE(all.equal(0.1+0.05, 0.15))#[1] TRUE1-0.1-0.1-0.1==0.7#[1] FALSE
    isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))#[1] TRUE0.3/0.1 == 3#[1] FALSE
    isTRUE(all.equal(0.3/0.1, 3))#[1] TRUE0.1+0.1==0.15#[1] FALSE
    isTRUE(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的 machine 字段,我们可以找出任意给定计算机的最小可表示数是多少:

    ?.Machine
     #....
     #double.eps     the smallest positive floating-point number x 
     #such that 1 + x != 1. It equals base^ulp.digits if either 
     #base is 2 or rounding is 0; otherwise, it is 
     #(base^ulp.digits) / 2. Normally 2.220446e-16.
     #....
     .Machine$double.eps
     #[1] 2.220446e-16
     sprintf("%a",.Machine$double.eps)
     #[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,# tolerance = .Machine$double.eps ^ 0.5,# 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, 0.7, 3, 0.15)
    == 不会给出 "expected" 结果,而且 all.equal 不执行元素级别的操作

    a==b#[1] FALSE FALSE FALSE FALSE
    all.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] TRUE TRUE TRUE FALSE
    If a functional version of this is desired, it can be written
    如果需要一个函数式版本,可以这样写

    elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
    elementwise.all.equal(a, b)#[1]  TRUE  TRUE  TRUE FALSE
    

    或者,您可以复制 all.equal.numeric 的相关内部代码,并使用隐式向量化,而不是在更多的函数调用 all.equal:

    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 needs
    abs(a - b) < tolerance#[1]  TRUE  TRUE  TRUE FALSE
    
    评论

报告相同问题?

悬赏问题

  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?
  • ¥15 错误 LNK2001 无法解析的外部符号
  • ¥50 安装pyaudiokits失败
  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能
  • ¥15 jmeter脚本回放有的是对的有的是错的
  • ¥15 r语言蛋白组学相关问题