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_errche viene inizializzata a zero quando xviene assegnata una costante.
- Per ogni operazione, ad esempio
z = x * y, aggiornare la variabile z_errutilizzando il modello standard di aritmetica in virgola mobile e gli zerrori risultanti e in esecuzione x_erre y_err.
- Il valore restituito della tua funzione dovrebbe quindi anche avere un rispettivo
_errvalore 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/2trova 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 ain un punto xusando 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 _errvariabili. Si noti che gli input ae xsono considerati esatti, ma potremmo anche richiedere all'utente di passare i valori corrispondenti per a_erre 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_erro 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 _errtermine 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)