Farkın makine hassasiyetinden daha küçük olup olmadığını kontrol etmenin doğru / standart yolu nedir?


36

Sıklıkla, elde edilen farkın makine hassasiyetinin üzerinde olup olmadığını kontrol etmek gerektiğinde ortaya çıkar. Bu amaçla Ar kullanışlı değişkeni için gibi görünüyor: .Machine$double.eps. Ancak bu değeri kullanma yönergeleri için R kaynak koduna döndüğümde birden çok farklı desen görüyorum.

Örnekler

statsKitaplıktan birkaç örnek :

t.test.R

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

chisq.test.R

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

integrate.R

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

lm.influence.R

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

princomp.R

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

vb.

Sorular

  1. İnsan nasıl bu farklı tüm arkasındaki mantık anlayabilir 10 *, 100 *, 50 *ve sqrt()değiştiricileri?
  2. .Machine$double.epsKesinlik sorunlarından kaynaklanan farklılıkları ayarlamak için kullanma hakkında yönergeler var mı?



6
Böylece, her iki gönderi de "makul derecede kesinlik" in uygulamanıza bağlı olduğu sonucuna varır. Bir örnek olay incelemesi olarak, bu yayını R-devel'de kontrol edebilirsiniz ; "Aha! Sayıların kendileri çift haneli olduğunda 100 kat daha fazla makine hassasiyeti." (Peter Dalgaard, R Core ekibinin üyesi)
Henrik

1
@ KarolisKoncevičius, bu kadar basit olduğunu sanmıyorum. Kayan nokta matematiğinde bulunan genel hatalar ve bunlarda kaç işlem yürüttüğünüz ile ilgilidir. Sadece kayan nokta sayılarıyla karşılaştırıyorsanız, tuşunu kullanın double.eps. Bir kayan noktalı sayı üzerinde birkaç işlem yapıyorsanız, hata toleransınız da ayarlanmalıdır. Bu yüzden all.equal size bir toleranceargüman verir .
Joseph Wood

1
Ayrıca, bir sonraki daha büyük çift sayısını verecek olan R'de nextafter işlevselliğinin uygulanmasına da göz atın .
GKi

Yanıtlar:


4

Makine hassasiyeti doublemevcut değerine bağlıdır. .Machine$double.epsdeğerler 1 olduğunda kesinlik verir. nextAfterMakinenin hassasiyetini diğer değerler için almak üzere C işlevini kullanabilirsiniz .

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

Değer ekleme adeğere bdeğişmeyecek bzaman aolduğu <= 's makine hassas yarısı. Farkın makine hassasiyetinden daha küçük olup olmadığını kontrol etmek ile yapılır <. Değiştiriciler, bir eklemenin ne sıklıkta değişiklik göstermediğini tipik durumlar olarak değerlendirebilir.

İçinde R makine hassasiyeti ile tahmin edilebilir:

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

Her doubledeğer bir aralığı temsil eder. Basit bir ekleme için, sonuç aralığı, her bir summandın yeniden canlandırılmasına ve aynı zamanda toplamlarının aralığına bağlıdır.

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

Daha yüksek hassasiyet Rmpfriçin kullanılabilir.

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

gmpTamsayıya dönüştürülebilmesi durumunda kullanılabilir (Rmpfr'de ne var).

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

Çok teşekkürler. Bunun daha iyi bir cevap olduğunu hissediyorum. Birçok noktayı güzel bir şekilde göstermektedir. Benim için hala belirsiz olan tek şey, bir kişi kendi başına değiştiriciler (* 9, vb.) Gelebilir mi? Ve eğer öyleyse nasıl ...
Karolis Koncevičius

Bu değiştiricinin istatistiklerdeki önem seviyesi gibi olduğunu ve doğru bir karşılaştırmayı reddetmek için seçilen riskle birlikte yaptığınız işlemlerin sayısı kadar artacağını düşünüyorum.
GKi

3

Bir machine.eps tanımı: en düşük değerdir  eps için  1+eps değil 1

Genel bir kural olarak (taban 2 ile bir kayan nokta temsili varsayarak):
Bu eps, 1 .. 2 aralığı için fark yaratır,
2 .. 4 aralığı için hassasiyet 2*eps
vb.

Ne yazık ki, burada iyi bir kural yoktur. Tamamen programınızın ihtiyaçlarına göre belirlenir.

R'de yaklaşık olarak eşitliği test etmek için yerleşik bir all olarak eşitiz. Yani belki şöyle bir şey kullanabilirsiniz(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 sahte, ve dahil olmak üzere çift kesinlikli karşılaştırmalar için bir dizi kayan nokta eşleştiriciye sahiptir . Bunları aşağıdaki gibi bir dizi eşleştiricide kullanabilirsiniz:DoubleEqDoubleNear

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

Güncelleme:

Sayısal Tarifler, tek taraflı bir fark katsayısının kullanıldığını, sqrt türevlerin sonlu fark yaklaşımları için adım boyutu için iyi bir seçim olduğunu göstermek için bir türev sağlar.

Sayfa 229-230 olan Wikipedia makale sitesi Sayısal Tarifler, 3. baskı, Bölüm 5.7 (sınırlı sayıda sayfa görüntüleme http://www.nrbook.com/empanel/ adresinde bulunabilir ).

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

Bu IEEE kayan noktalı aritmetik , bilgisayar aritmetiğinin iyi bilinen bir sınırlamasıdır ve birkaç yerde tartışılmaktadır:

. dplyr::near()kayan nokta sayılarının iki vektörünün eşit olup olmadığını test etmek için başka bir seçenektir.

Fonksiyonun yerleşik bir tolerans parametresi vardır: tol = .Machine$double.eps^0.5ayarlanabilir. Varsayılan parametre, için varsayılanla aynıdır all.equal().


2
Yanıtınız için teşekkürler. Şu anda bunun kabul edilmiş bir cevap olamayacak kadar az olduğunu düşünüyorum. Gönderinin iki ana sorusunu ele almıyor gibi görünüyor. Örneğin, "programınızın ihtiyaçları tarafından belirlenir" ifadesini kullanır. Bu ifadenin bir veya iki örneğini göstermek güzel olurdu - belki küçük bir program ve hoşgörü tarafından nasıl belirlenebilir. Belki de söz konusu R betiklerinden birini kullanmak. Ayrıca all.equal()varsayılan tolerans olarak kendi varsayımı vardır sqrt(double.eps)- neden varsayılan? Kullanmak iyi bir kural sqrt()mı?
Karolis Koncevičius

İşte R'nin eps'yi hesaplamak için kullandığı kod (kendi programına çıkarılır). Ayrıca cevabı, daha önce yaşadığım sayısız tartışma puanıyla güncelledim. Umarım aynı şey daha iyi anlamanıza yardımcı olur.
Sreeram Nair

Tüm çabalar için samimi bir +1. Ancak mevcut durumda hala cevabı kabul edemiyorum. Çok sayıda referansla biraz uzanıyor gibi görünüyor, ancak gönderilen 2 soruya gerçek cevap açısından: 1) R stats::kaynağında 100x, 50x, vb . cevap oldukça incedir. Uygulanabilir tek cümle sqrt () hakkında gerçekten sayısal olan iyi bir varsayılan olmak hakkında "Sayısal Tarifler" referans gibi görünüyor, hissediyorum. Ya da belki burada bir şey eksik.
Karolis Koncevičius
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.