Який правильний / стандартний спосіб перевірити, чи різниця менша від точності машини?


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 ; "Ага! 100-кратна машинна точність не у всіх, коли самі цифри є двозначними". (Пітер Далгаард, член команди R Core)
Генрік

1
@ KarolisKoncevičius, я не думаю, що це так просто. Це пов'язано із загальними помилками математики з плаваючою комою та кількістю операцій, які ви виконуєте над ними. Якщо ви просто порівнюєте числа з плаваючою комою, використовуйте double.eps. Якщо ви виконуєте кілька операцій з номером з плаваючою комою, то ваше відхилення також повинно коригуватися. Ось чому all.equal дає вам toleranceаргументи.
Джозеф Вуд

1
Подивіться також на реалізацію функцій nextafter в 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значення представляє діапазон. Для простого додавання, діапазон результату залежить від перенастроювання кожної суми, а також діапазону їх суми.

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 тощо)? А якщо так, як…
Кароліс Концевічус

Я думаю, що цей модифікатор схожий на рівень значущості в статистиці і збільшиться на кількість операцій, які ви зробили в поєднанні з обраним ризиком, щоб відхилити правильне порівняння.
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 макет має ряд відповідників з плаваючою комою для порівняння подвійної точності, включаючи DoubleEqта DoubleNear. Ви можете використовувати їх у відповіднику масиву таким чином:

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()?
Кароліс Концевічус

Ось код, який R використовує для обчислення епс (витягнутий у власну програму). Також я оновив відповідь численними питаннями обговорення, які я раніше проходив. Сподіваюся, те саме допоможе вам зрозуміти краще.
Sreeram Nair

Щирий +1 за всі зусилля. Але в поточному стані я все ще не можу прийняти відповідь. Здається, це трохи неперевершило з великою кількістю посилань, але з точки зору фактичної відповіді на 2 розміщені питання: 1) як зрозуміти модифікатори 100x, 50x тощо в stats::джерелі R , і 2) які вказівки; відповідь досить тонка. Я вважаю, що єдиним застосовним реченням є посилання з "Числових рецептів" про те, що sqrt () є хорошим за замовчуванням, що дійсно до речі, я вважаю. А може, я щось тут пропускаю.
Кароліс Концевічус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.