Qual è il modo corretto / standard per verificare se la differenza è inferiore alla precisione della macchina?


36

Finisco spesso in situazioni in cui è necessario verificare se la differenza ottenuta è superiore alla precisione della macchina. Sembra che per questo scopo R ha una variabile a portata di mano: .Machine$double.eps. Tuttavia, quando passo al codice sorgente R per le linee guida sull'uso di questo valore, vedo più modelli diversi.

Esempi

Ecco alcuni esempi dalla statslibreria:

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

eccetera.

Domande

  1. Come si può comprendere il ragionamento che sta dietro tutti questi diversi 10 *, 100 *, 50 *e sqrt()modificatori?
  2. Esistono linee guida sull'utilizzo .Machine$double.epsper regolare le differenze dovute a problemi di precisione?



6
Pertanto, entrambi i post concludono che "il ragionevole grado di certezza" dipende dalla tua domanda. Come caso di studio, puoi controllare questo post su R-devel ; "Ah! 100 volte la precisione della macchina non tanto quando i numeri sono a doppia cifra." (Peter Dalgaard, membro del team R Core)
Henrik il

1
@ KarolisKoncevičius, non credo sia così semplice. Ha a che fare con gli errori generali presenti nella matematica in virgola mobile e quante operazioni vengono eseguite su di essi. Se si sta semplicemente confrontando con numeri in virgola mobile, utilizzare double.eps. Se si eseguono diverse operazioni su un numero in virgola mobile, è necessario regolare anche la tolleranza dell'errore. Questo è il motivo per cui all.equal ti dà un toleranceargomento.
Joseph Wood,

1
Dai un'occhiata anche all'implementazione della funzionalità Nextafter in R che ti darà il prossimo numero doppio più grande.
GKi

Risposte:


4

La precisione della macchina doubledipende dal suo valore attuale. .Machine$double.epsfornisce la precisione quando i valori sono 1. È possibile utilizzare la funzione C nextAfterper ottenere la precisione della macchina per altri valori.

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

L'aggiunta di valore aa valore bnon cambierà bquando aè la <= metà della sua precisione della macchina. Verifica se la differenza è più ridotta della precisione della macchina <. I modificatori potrebbero considerare casi tipici con quale frequenza un'aggiunta non ha mostrato cambiamenti.

In R la precisione della macchina può essere stimata con:

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

Ogni doublevalore rappresenta un intervallo. Per una semplice aggiunta, l'intervallo del risultato dipende dalla reange di ogni summand e anche dall'intervallo della loro somma.

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

Per una maggiore precisione Rmpfrpotrebbe essere utilizzato.

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

Nel caso in cui potesse essere convertito in numero intero gmppotrebbe essere utilizzato (ciò che è in Rmpfr).

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

Molte grazie. Sento che questa è una risposta molto migliore. Illustra bene molti punti. L'unica cosa che non mi è ancora chiara è: si possono inventare i modificatori (come * 9, ecc.) Da soli? E se è così come ...
Karolis Koncevičius il

Penso che questo modificatore sia come il livello di significatività nelle statistiche e aumenterà del numero di operazioni eseguite in combinazione con il rischio scelto per rifiutare un confronto corretto.
GKi,

3

Definizione di machine.eps: è il valore più basso  eps per il quale  1+eps non lo è 1

Come regola empirica (assumendo una rappresentazione in virgola mobile con base 2):
questo epsfa la differenza per l'intervallo 1 .. 2,
per l'intervallo 2 .. 4 la precisione è 2*eps
e così via.

Sfortunatamente, non esiste una buona regola empirica qui. È interamente determinato dalle esigenze del tuo programma.

In R abbiamo all.equal come un modo integrato per testare l'uguaglianza approssimativa. Quindi potresti usare forse qualcosa del genere (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 ha una serie di matcher in virgola mobile per confronti a doppia precisione, tra cui DoubleEqe DoubleNear. Puoi usarli in un array matcher come questo:

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

Aggiornare:

Le Ricette Numeriche forniscono una derivazione per dimostrare che l'uso di un quoziente di differenza unilaterale sqrtè una buona scelta della dimensione del gradino per approssimazioni a differenza finita dei derivati.

Il sito di articoli di Wikipedia Ricette numeriche, 3a edizione, Sezione 5.7, che è le pagine 229-230 (un numero limitato di visualizzazioni di pagina è disponibile all'indirizzo http://www.nrbook.com/empanel/ ).

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

Questa aritmetica in virgola mobile IEEE è una limitazione ben nota dell'aritmetica del computer ed è discussa in diversi punti:

. dplyr::near()è un'altra opzione per verificare se due vettori di numeri in virgola mobile sono uguali.

La funzione ha un parametro di tolleranza incorporato: tol = .Machine$double.eps^0.5che può essere regolato. Il parametro predefinito è uguale a quello predefinito per all.equal().


2
Grazie per la risposta. Al momento penso che questo sia troppo minimo per essere una risposta accettata. Non sembra rispondere alle due domande principali poste dal post. Ad esempio, afferma "è determinato dalle esigenze del tuo programma". Sarebbe bello mostrare uno o due esempi di questa affermazione - forse un piccolo programma e come la tolleranza può essere determinata da esso. Forse usando uno degli script R menzionati. all.equal()C'è anche il suo presupposto come tolleranza di default che c'è sqrt(double.eps)- perché è il default? È una buona regola pratica da usare sqrt()?
Karolis Koncevičius,

Ecco il codice che R utilizza per calcolare eps (estratto nel suo programma). Inoltre ho aggiornato la risposta con numerosi punti di discussione che avevo affrontato in precedenza. Spero che lo stesso ti aiuti a capire meglio.
Sreeram Nair,

Un +1 sincero per tutto lo sforzo. Ma allo stato attuale non riesco ancora ad accettare la risposta. Sembra un po 'fuori portata con molti riferimenti, ma in termini di risposta effettiva alle 2 domande postate: 1) come comprendere i modificatori 100x, 50x, ecc. Nella stats::sorgente R e 2) quali sono le linee guida; la risposta è piuttosto sottile. L'unica frase applicabile sembra essere il riferimento da "Ricette numeriche" sul fatto che sqrt () sia un buon default, il che è davvero sul punto, credo. O forse mi manca qualcosa qui.
Karolis Koncevičius,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.