检查差异是否小于机器精度的正确/标准方法是什么?


36

我经常遇到必须检查所获得的差异是否超出机器精度的情况。为此,R似乎有一个方便的变量:.Machine$double.eps。但是,当我转向R源代码获取有关使用此值的准则时,会看到多种不同的模式。

例子

以下是stats库中的一些示例:

检验

if(stderr < 10 *.Machine$double.eps * abs(mx))

chisq.test

if(abs(sum(p)-1) > sqrt(.Machine$double.eps))

整合

rel.tol < max(50*.Machine$double.eps, 0.5e-28)

影响力

e[abs(e) < 100 * .Machine$double.eps * median(abs(e))] <- 0

PRINCOMR

if (any(ev[neg] < - 9 * .Machine$double.eps * ev[1L]))

等等

问题

  1. 一个如何理解这些不同背后的理由10 *100 *50 *sqrt()修饰?
  2. 是否有关于.Machine$double.eps用于调整由于精度问题引起的差异的准则?



6
因此,这两篇文章都得出结论,“合理的确定性”取决于您的申请。作为案例研究,您可以在R-devel上查看此文章;“啊哈!当数字本身为两位数时,机器精度不是100倍。” (Peter Dalgaard,R Core小组成员)
Henrik

1
@KarolisKoncevičius,我认为不是那么简单。它与浮点数学中存在的一般错误以及您对它们执行的操作次数有关。如果您只是与浮点数进行比较,请使用double.eps。如果您要对浮点数执行多项操作,则您的容错能力也应进行调整。这就是all.equal为您提供tolerance参数的原因。
约瑟夫·伍德

1
还可以查看R中nextafter功能的实现, 这将为您提供下一个更大的双数。
GKi

Answers:


4

机器的精度double取决于其当前值。.Machine$double.eps给出值为1时的精度。您可以使用C函数nextAfter获取其他值的机器精度。

library(Rcpp)
cppFunction("double getPrec(double x) {
  return nextafter(x, std::numeric_limits<double>::infinity()) - x;}")

(pr <- getPrec(1))
#[1] 2.220446e-16
1 + pr == 1
#[1] FALSE
1 + pr/2 == 1
#[1] TRUE
1 + (pr/2 + getPrec(pr/2)) == 1
#[1] FALSE
1 + pr/2 + pr/2 == 1
#[1] TRUE
pr/2 + pr/2 + 1 == 1
#[1] FALSE

如果是机器精度的一半,则为a值增加值b不会改变使用进行检查差异是否比机器精度小。修饰符可能会考虑典型情况,即添加项未显示更改的频率。ba<= <

R中,可以通过以下方式估算机器精度:

getPrecR <- function(x) {
  y <- log2(pmax(.Machine$double.xmin, abs(x)))
  ifelse(x < 0 & floor(y) == y, 2^(y-1), 2^floor(y)) * .Machine$double.eps
}
getPrecR(1)
#[1] 2.220446e-16

每个double值代表一个范围。对于一个简单的加法,结果的范围取决于每个被加数的范围以及它们的和的范围。

library(Rcpp)
cppFunction("std::vector<double> getRange(double x) {return std::vector<double>{
   (nextafter(x, -std::numeric_limits<double>::infinity()) - x)/2.
 , (nextafter(x, std::numeric_limits<double>::infinity()) - x)/2.};}")

x <- 2^54 - 2
getRange(x)
#[1] -1  1
y <- 4.1
getRange(y)
#[1] -4.440892e-16  4.440892e-16
z <- x + y
getRange(z)
#[1] -2  2
z - x - y #Should be 0
#[1] 1.9

2^54 - 2.9 + 4.1 - (2^54 + 5.9) #Should be -4.7
#[1] 0
2^54 - 2.9 == 2^54 - 2      #Gain 0.9
2^54 - 2 + 4.1 == 2^54 + 4  #Gain 1.9
2^54 + 5.9 == 2^54 + 4      #Gain 1.9

对于更高的精度Rmpfr,可以使用。

library(Rmpfr)
mpfr("2", 1024L)^54 - 2.9 + 4.1 - (mpfr("2", 1024L)^54 + 5.9)
#[1] -4.700000000000000621724893790087662637233734130859375

如果可以将其转换为整数(gmp可以使用Rmpfr中的内容)。

library(gmp)
as.bigz("2")^54 * 10 - 29 + 41 - (as.bigz("2")^54 * 10 + 59)
#[1] -47

非常感谢。我觉得这是一个更好的答案。它很好地说明了很多观点。对我来说仍然唯一不清楚的是-可以自己提出修饰符(例如* 9等)吗?如果是的话,如何...
KarolisKoncevičius

我认为此修饰符就像统计中的显着性水平,并且会因您选择拒绝拒绝正确比较的风险而结合执行的操作次数增加。
GKi

3

一个machine.eps的定义:它是最低值,  eps 为这  1+eps 不 1

作为一个经验法则(假设与基座2浮点表示):
eps使得用于..的范围内1 2的差,
对于范围2。4精度2*eps
等。

不幸的是,这里没有良好的经验法则。这完全取决于程序的需求。

在R中,我们将all.equal作为测试近似相等性的内置方式。因此,您可以使用类似(x<y) | all.equal(x,y

i <- 0.1
 i <- i + 0.05
 i
if(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines
    cat("i equals 0.15\n") 
} else {
    cat("i does not equal 0.15\n")
}
#i equals 0.15

Google模拟具有许多用于双精度比较的浮点匹配器,包括DoubleEqDoubleNear。您可以像这样在数组匹配器中使用它们:

ASSERT_THAT(vec, ElementsAre(DoubleEq(0.1), DoubleEq(0.2)));

更新:

数值食谱提供了派生方法,可以证明使用单边差分商, sqrt是微分的有限差分近似的步长选择。

维基百科文章网站《数字食谱》第3版,第5.7节,第229-230页(可以从http://www.nrbook.com/empanel/获得有限的页面浏览量)。

all.equal(target, current,
           tolerance = .Machine$double.eps ^ 0.5, scale = NULL,
           ..., check.attributes = TRUE)

这些IEEE浮点运算是计算机运算的众所周知的局限性,并在以下几个地方进行了讨论:

dplyr::near()是测试浮点数的两个向量是否相等的另一种选择。

该功能具有内置的公差参数:tol = .Machine$double.eps^0.5可以调整。默认参数与的默认参数相同all.equal()


2
感谢您的回复。目前,我认为这太小了,无法接受。似乎并没有解决帖子中的两个主要问题。例如,它指出“由程序的需求决定”。最好显示一个或两个此语句的示例-也许是一个小程序,以及如何确定公差。也许使用提到的R脚本之一。还有all.equal()作为默认容差的自己的假设sqrt(double.eps)-为什么它是默认容差?使用它是一个好的经验法则sqrt()吗?
卡洛里斯·孔切维奇(KarolisKoncevičius)

这是R用于计算eps的代码(提取到其自己的程序中)。另外,我还通过很多讨论点来更新“答案”。希望同样能帮助您更好地理解。
Sreeram Nair

真诚的+1为所有的努力。但是在当前状态下,我仍然无法接受答案。在很多参考文献的帮助下,它似乎有点超出范围,但实际上是对两个已发布问题的答案:1)如何理解R stats::源中的100x,50x等修饰符,以及2)指导原则是什么?答案很薄。唯一适用的句子似乎是“数值食谱”中关于sqrt()的默认引用,我认为这确实很重要。也许我在这里错过了一些东西。
卡洛里斯·孔切维奇(KarolisKoncevičius)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.