Se stai cercando un buon limite per il tuo errore di arrotondamento, non hai necessariamente bisogno di una libreria di precisione aribtrary. È possibile invece utilizzare l'analisi degli errori in esecuzione.
Non sono riuscito a trovare un buon riferimento online, ma è tutto descritto nella Sezione 3.3 del libro di Nick Higham "Precisione e stabilità degli algoritmi numerici". L'idea è abbastanza semplice:
- Fattorizza nuovamente il codice in modo da avere un'unica assegnazione di una singola operazione aritmetica su ciascuna riga.
- Per ogni variabile, ad esempio
x
, creare una variabile x_err
che viene inizializzata a zero quando x
viene assegnata una costante.
- Per ogni operazione, ad esempio
z = x * y
, aggiornare la variabile z_err
utilizzando il modello standard di aritmetica in virgola mobile e gli z
errori risultanti e in esecuzione x_err
e y_err
.
- Il valore restituito della tua funzione dovrebbe quindi anche avere un rispettivo
_err
valore associato ad essa. Questo è un dato dipendente dai dati sull'errore di arrotondamento totale.
La parte difficile è il passaggio 3. Per le operazioni aritmetiche più semplici, è possibile utilizzare le seguenti regole:
z = x + y
-> z_err = u*abs(z) + x_err + y_err
z = x - y
-> z_err = u*abs(z) + x_err + y_err
z = x * y
-> z_err = u*abs(z) + x_err*abs(y) + y_err*abs(x)
z = x / y
-> z_err = u*abs(z) + (x_err*abs(y) + y_err*abs(x))/y^2
z = sqrt(x)
-> z_err = u*abs(z) + x_err/(2*abs(z))
dove si u = eps/2
trova il roundoff dell'unità. Sì, le regole per +
e -
sono le stesse. Le regole per qualsiasi altra operazione op(x)
possono essere facilmente estratte usando l'espansione della serie Taylor del risultato applicato op(x + x_err)
. Oppure puoi provare a cercare su Google. O usando il libro di Nick Higham.
Ad esempio, considerare il seguente codice Matlab / Octave che valuta un polinomio nei coefficienti a
in un punto x
usando lo schema Horner:
function s = horner ( a , x )
s = a(end);
for k=length(a)-1:-1:1
s = a(k) + x*s;
end
Per il primo passo, abbiamo suddiviso le due operazioni in s = a(k) + x*s
:
function s = horner ( a , x )
s = a(end);
for k=length(a)-1:-1:1
z = x*s;
s = a(k) + z;
end
Introduciamo quindi le _err
variabili. Si noti che gli input a
e x
sono considerati esatti, ma potremmo anche richiedere all'utente di passare i valori corrispondenti per a_err
e x_err
:
function [ s , s_err ] = horner ( a , x )
s = a(end);
s_err = 0;
for k=length(a)-1:-1:1
z = x*s;
z_err = ...;
s = a(k) + z;
s_err = ...;
end
Infine, applichiamo le regole sopra descritte per ottenere i termini di errore:
function [ s , s_err ] = horner ( a , x )
u = eps/2;
s = a(end);
s_err = 0;
for k=length(a)-1:-1:1
z = x*s;
z_err = u*abs(z) + s_err*abs(x);
s = a(k) + z;
s_err = u*abs(s) + z_err;
end
Si noti che poiché non abbiamo a_err
o x_err
, ad esempio, si presume che siano zero, i rispettivi termini vengono semplicemente ignorati nelle espressioni di errore.
Et voilà! Ora abbiamo uno schema Horner che restituisce una stima dell'errore dipendente dai dati (nota: questo è un limite superiore dell'errore) insieme al risultato.
Come nota a margine, dal momento che stai usando C ++, potresti prendere in considerazione la possibilità di creare la tua classe per i valori a virgola mobile che porta con sé il _err
termine e sovraccaricare tutte le operazioni aritmetiche per aggiornare questi valori come descritto sopra. Per i codici di grandi dimensioni, questo può essere il percorso più semplice, sebbene computazionalmente meno efficiente. Detto questo, potresti essere in grado di trovare una tale classe online. Una rapida ricerca su Google mi ha dato questo link .
±ux(1±u)