Cách chính xác / tiêu chuẩn để kiểm tra xem sự khác biệt nhỏ hơn độ chính xác của máy là gì?


36

Tôi thường kết thúc trong các tình huống cần kiểm tra xem sự khác biệt thu được có cao hơn độ chính xác của máy không. Có vẻ như cho mục đích này R có một biến tiện dụng : .Machine$double.eps. Tuy nhiên, khi tôi chuyển sang mã nguồn R để được hướng dẫn sử dụng giá trị này, tôi thấy nhiều mẫu khác nhau.

Ví dụ

Dưới đây là một vài ví dụ từ statsthư viện:

t.test.R

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

chisq.test.R

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

tích hợp.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

hoàng tử.R

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

Vân vân.

Câu hỏi

  1. Làm thế nào người ta có thể hiểu được lý do đằng sau tất cả những khác nhau 10 *, 100 *, 50 *sqrt()bổ?
  2. Có hướng dẫn về việc sử dụng .Machine$double.epsđể điều chỉnh sự khác biệt do các vấn đề chính xác?



6
Do đó, cả hai bài viết đều kết luận rằng "mức độ chắc chắn hợp lý" phụ thuộc vào ứng dụng của bạn. Như một trường hợp nghiên cứu, bạn có thể kiểm tra bài đăng này trên R-devel ; "Aha! Độ chính xác của máy gấp 100 lần không quá nhiều khi bản thân các số có hai chữ số." (Peter Dalgaard, thành viên của nhóm R Core)
Henrik

1
@ KarolisKoncevičius, tôi không nghĩ nó đơn giản. Nó có liên quan đến các lỗi chung có trong toán học dấu phẩy động và có bao nhiêu thao tác bạn thực hiện trên chúng. Nếu bạn chỉ đơn giản là so sánh với số dấu phẩy động, sử dụng double.eps. Nếu bạn đang thực hiện một số thao tác trên số dấu phẩy động, thì khả năng chịu lỗi của bạn cũng sẽ điều chỉnh. Đây là lý do tại sao all.equal cung cấp cho bạn một toleranceđối số.
Joseph Wood

1
Cũng đã xem Thực hiện chức năng tiếp theo trong R, cái gì sẽ cung cấp cho bạn số kép lớn hơn tiếp theo.
GKi

Câu trả lời:


4

Độ chính xác của máy doublephụ thuộc vào giá trị hiện tại của nó. .Machine$double.epsđưa ra độ chính xác khi các giá trị là 1. Bạn có thể sử dụng hàm C nextAfterđể lấy độ chính xác của máy cho các giá trị khác.

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

Thêm giá trị avới giá trị bsẽ không thay đổi bkhi a<= một nửa của nó của máy chính xác. Kiểm tra xem sự khác biệt có nhỏ hơn độ chính xác của máy hay không <. Người sửa đổi có thể xem xét các trường hợp điển hình về mức độ thường xuyên bổ sung không hiển thị thay đổi.

Trong R , độ chính xác của máy có thể được ước tính bằng:

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

Mỗi doublegiá trị đại diện cho một phạm vi. Đối với một bổ sung đơn giản, phạm vi của kết quả phụ thuộc vào sự trả thù của mỗi triệu hồi và cũng là phạm vi của tổng của chúng.

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

Đối với phân khu cao hơn Rmpfrcó thể được sử dụng.

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

Trong trường hợp nó có thể được chuyển đổi thành số nguyên gmpcó thể được sử dụng (những gì có trong Rmpfr).

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

Cảm ơn rất nhiều. Tôi cảm thấy như đây là một câu trả lời tốt hơn nhiều. Nó minh họa rất nhiều điểm độc đáo. Điều duy nhất vẫn còn chưa rõ ràng đối với tôi là - người ta có thể tự mình tìm ra các công cụ sửa đổi (như * 9, v.v.) không? Và nếu vậy thì thế nào ...
Karolis Koncevičius

Tôi nghĩ rằng công cụ sửa đổi này giống như mức ý nghĩa trong thống kê và sẽ tăng theo số lượng hoạt động bạn đã thực hiện kết hợp với rủi ro đã chọn để từ chối so sánh chính xác.
GKi

3

Định nghĩa của một machine.eps: nó là giá trị thấp nhất  eps mà  1+eps không được 1

Như một quy tắc chung (giả sử một biểu diễn dấu phẩy động với cơ sở 2):
Điều này epstạo ra sự khác biệt cho phạm vi 1 .. 2,
cho phạm vi 2 .. 4 độ chính xác là 2*eps
như vậy.

Thật không may, không có quy tắc tốt ở đây. Nó hoàn toàn được xác định bởi nhu cầu của chương trình của bạn.

Trong R, chúng ta có all.equal như một cách được xây dựng để kiểm tra sự bằng nhau gần đúng. Vì vậy, bạn có thể sử dụng có thể một cái gì đó như (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 mock có một số công cụ đối sánh dấu phẩy động để so sánh chính xác gấp đôi, bao gồm DoubleEqDoubleNear. Bạn có thể sử dụng chúng trong một trình so khớp mảng như thế này:

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

Cập nhật:

Công thức toán số cung cấp một dẫn xuất để chứng minh rằng sử dụng thương số chênh lệch một phía, sqrtlà một lựa chọn tốt về kích thước bước cho các xấp xỉ hữu hạn của các đạo hàm.

Trang web bài viết Wikipedia Công thức số, ấn bản thứ 3, Phần 5.7, là trang 229-230 (số lượt xem trang hạn chế có sẵn tại http://www.nrbook.com/empanel/ ).

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

Các số học dấu phẩy động của IEEE này là một giới hạn nổi tiếng của số học máy tính và được thảo luận ở một số nơi:

. dplyr::near()là một tùy chọn khác để kiểm tra nếu hai vectơ số dấu phẩy động bằng nhau.

Hàm này có một tham số dung sai tích hợp: tol = .Machine$double.eps^0.5có thể điều chỉnh được. Tham số mặc định giống như mặc định cho all.equal().


2
Cảm ơn vì sự trả lời. Hiện tại tôi nghĩ rằng điều này là quá tối thiểu để là một câu trả lời được chấp nhận. Nó dường như không giải quyết hai câu hỏi chính từ bài viết. Ví dụ, nó ghi "nó được xác định bởi nhu cầu của chương trình của bạn". Sẽ thật tuyệt khi trình bày một hoặc hai ví dụ về tuyên bố này - có thể là một chương trình nhỏ và mức độ chịu đựng có thể được xác định bởi nó. Có thể sử dụng một trong các tập lệnh R được đề cập. Cũng all.equal()có giả định riêng của nó là dung sai mặc định có sqrt(double.eps)- tại sao nó là mặc định? Đó có phải là một quy tắc tốt để sử dụng sqrt()?
Karolis Koncevičius

Dưới đây là mã R sử dụng để tính toán eps (được trích xuất vào chương trình riêng của nó). Ngoài ra tôi đã cập nhật Câu trả lời với nhiều điểm thảo luận mà tôi đã trải qua trước đó. Hy vọng điều tương tự sẽ giúp bạn hiểu rõ hơn.
Sreeram Nair

+1 chân thành cho tất cả nỗ lực. Nhưng ở trạng thái hiện tại tôi vẫn không thể chấp nhận câu trả lời. Có vẻ như hơi xa tầm với rất nhiều tài liệu tham khảo, nhưng về mặt câu trả lời thực tế cho 2 câu hỏi được đăng: 1) làm thế nào để hiểu các công cụ sửa đổi 100x, 50x, v.v. trong stats::nguồn R và 2) hướng dẫn là gì; Câu trả lời khá mỏng. Câu duy nhất có thể áp dụng dường như là tài liệu tham khảo từ "Công thức số" về sqrt () là một mặc định tốt, điều này thực sự đúng, tôi cảm thấy. Hoặc có lẽ tôi đang thiếu một cái gì đó ở đây.
Karolis Koncevičius
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.