วิธีที่ถูกต้อง / มาตรฐานในการตรวจสอบว่าความแตกต่างเล็กกว่าความแม่นยำของเครื่อง?


36

ฉันมักจะลงเอยในสถานการณ์ที่จำเป็นต้องตรวจสอบว่าความแตกต่างที่ได้รับนั้นสูงกว่าความแม่นยำของเครื่องหรือไม่ ดูเหมือนว่าเพื่อวัตถุประสงค์นี้ R .Machine$double.epsมีตัวแปรที่มีประโยชน์: อย่างไรก็ตามเมื่อฉันหันไปใช้ซอร์สโค้ด R เพื่อหาแนวทางเกี่ยวกับการใช้ค่านี้ฉันจะเห็นรูปแบบที่แตกต่างกันหลายแบบ

ตัวอย่าง

นี่คือตัวอย่างจากstatsห้องสมุด:

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]))

เป็นต้น

คำถาม

  1. วิธีการหนึ่งที่สามารถเข้าใจเหตุผลที่อยู่เบื้องหลังเหล่านั้นแตกต่างกันทั้งหมด10 *, 100 *, 50 *และsqrt()ปรับเปลี่ยน?
  2. มีแนวทางเกี่ยวกับการใช้.Machine$double.epsเพื่อปรับความแตกต่างเนื่องจากปัญหาความแม่นยำหรือไม่



6
ดังนั้นโพสต์ทั้งสองจึงสรุปได้ว่า "ระดับความเชื่อมั่นที่สมเหตุสมผล" ขึ้นอยู่กับใบสมัครของคุณ เป็นกรณีศึกษาคุณสามารถตรวจสอบโพสต์นี้ใน R-Devel ; "Aha! 100 เท่าของความแม่นยำของเครื่องจักรในจำนวนที่ไม่มากนักเมื่อตัวเลขนั้นเป็นตัวเลขสองหลัก" (Peter Dalgaard สมาชิกของทีม R Core)
Henrik

1
@ KarolisKoncevičiusฉันไม่คิดว่ามันง่ายขนาดนั้น มันเกี่ยวกับข้อผิดพลาดทั่วไปที่มีอยู่ในคณิตศาสตร์เลขทศนิยมและจำนวนการดำเนินการที่คุณดำเนินการกับมัน double.epsหากคุณเป็นเพียงการเปรียบเทียบกับตัวเลขทศนิยมใช้ หากคุณกำลังดำเนินการหลายอย่างกับหมายเลขจุดลอยตัวความคลาดเคลื่อนข้อผิดพลาดของคุณควรปรับด้วย นี่คือเหตุผลที่all.equalให้การtoleranceโต้แย้งแก่คุณ
โจเซฟวู้ด

1
ดูการใช้งานฟังก์ชั่น ถัดไปใน Rสิ่งที่จะทำให้คุณมีจำนวนสองเท่าที่ใหญ่กว่าถัดไป
GKi

คำตอบ:


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จะไม่เปลี่ยนแปลงbเมื่อaเป็น<= ครึ่งหนึ่งของมันของความแม่นยำเครื่อง การตรวจสอบว่าความแตกต่างนั้นดีกว่าความแม่นยำของเครื่อง<หรือไม่ ตัวดัดแปลงอาจพิจารณากรณีทั่วไปว่าการเพิ่มไม่แสดงการเปลี่ยนแปลงบ่อยเพียงใด

ใน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ค่าแสดงช่วง สำหรับการเพิ่มอย่างง่ายช่วงของผลลัพธ์ขึ้นกับ reange ของแต่ละ summand และช่วงของ sum

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 ฯลฯ ) ด้วยตนเองได้หรือไม่? และถ้าเป็นเช่นนั้น ...
Karolis Koncevičius

ฉันคิดว่าตัวดัดแปลงนี้เป็นเหมือนระดับนัยสำคัญในสถิติและจะเพิ่มขึ้นตามจำนวนของการดำเนินการที่คุณทำร่วมกับความเสี่ยงที่เลือกเพื่อปฏิเสธการเปรียบเทียบที่ถูกต้อง
GKi

3

นิยามของ machine.eps: มันเป็นค่าต่ำสุด  eps ที่  1+eps ไม่ได้ 1

ตามกฎของหัวแม่มือ (สมมติว่าเป็นตัวแทนจุดลอยตัวที่มีฐาน 2):
สิ่งนี้epsสร้างความแตกต่างสำหรับช่วง 1 .. 2,
สำหรับช่วง 2 .. 4 ความแม่นยำเป็น2*eps
เช่นนั้น

น่าเสียดายที่ไม่มีกฎง่ายๆที่นี่ ขึ้นอยู่กับความต้องการของโปรแกรมของคุณ

ใน R เรามีทั้งหมดเท่ากันเพื่อทดสอบความเสมอภาคโดยประมาณ ดังนั้นคุณสามารถใช้สิ่งที่ชอบ(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 จำลองมีจำนวนของลอย matchers จุดสำหรับการเปรียบเทียบความแม่นยำสองรวมทั้งและDoubleEq DoubleNearคุณสามารถใช้มันใน array matcher เช่นนี้:

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

ปรับปรุง:

สูตรอาหารเชิงตัวเลขจัดทำมาเพื่อแสดงให้เห็นว่าการใช้ความฉลาดทางผลต่างด้านเดียวsqrtเป็นทางเลือกที่ดีของขนาดขั้นตอนสำหรับการประมาณผลต่างอันตะของอนุพันธ์

เว็บไซต์บทความ Wikipedia สูตรตัวเลขรุ่น 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()หรือไม่?
Karolis Koncevičius

นี่คือรหัส R ใช้ในการคำนวณ eps (แยกลงในโปรแกรมของตัวเอง) นอกจากนี้ฉันยังได้อัปเดตคำตอบด้วยประเด็นการสนทนามากมายที่ฉันเคยทำมาก่อน หวังว่าสิ่งเดียวกันจะช่วยให้คุณเข้าใจดีขึ้น
Sreeram Nair

+1 จริงใจสำหรับทุกความพยายาม แต่ในสถานะปัจจุบันฉันยังคงไม่สามารถยอมรับคำตอบได้ ดูเหมือนว่าจะมีการอ้างอิงค่อนข้างมาก แต่ในแง่ของการตอบคำถามที่โพสต์จริง 2 คำถาม: 1) วิธีทำความเข้าใจตัวดัดแปลง 100x, 50x และอื่น ๆ ในstats::แหล่งR และ 2) อะไรคือแนวทาง; คำตอบนั้นค่อนข้างบาง ประโยคที่เกี่ยวข้องเท่านั้นดูเหมือนจะเป็นการอ้างอิงจาก "สูตรอาหารเชิงตัวเลข" เกี่ยวกับ sqrt () เป็นค่าเริ่มต้นที่ดีซึ่งเป็นจุดที่ฉันรู้สึก หรือบางทีฉันอาจจะพลาดบางสิ่งบางอย่างที่นี่
Karolis Koncevičius
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.