Si está buscando un buen límite en su error de redondeo, no necesariamente necesita una biblioteca de precisión de aribtrary. En su lugar, puede usar el análisis de errores en ejecución.
No pude encontrar una buena referencia en línea, pero todo se describe en la Sección 3.3 del libro de Nick Higham "Precisión y estabilidad de los algoritmos numéricos". La idea es muy simple:
- Vuelva a factorizar su código para que tenga una sola asignación de una sola operación aritmética en cada línea.
- Para cada variable, por ejemplo
x
, cree una variable x_err
que se inicialice a cero cuando x
se le asigne una constante.
- Para cada operación, por ejemplo
z = x * y
, actualice la variable z_err
utilizando el modelo estándar de aritmética de punto flotante y los z
errores resultantes y de ejecución x_err
y y_err
.
- El valor de retorno de su función también debe tener un
_err
valor respectivo adjunto. Este es un límite dependiente de los datos en su error de redondeo total.
La parte difícil es el paso 3. Para las operaciones aritméticas más simples, puede usar las siguientes reglas:
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))
donde u = eps/2
es el redondeo de la unidad. Sí, las reglas para +
y -
son las mismas. Las reglas para cualquier otra operación op(x)
se pueden extraer fácilmente utilizando la expansión de la serie Taylor del resultado aplicado op(x + x_err)
. O puedes intentar buscar en Google. O usando el libro de Nick Higham.
Como ejemplo, considere el siguiente código de Matlab / Octave que evalúa un polinomio en los coeficientes a
en un punto x
usando el esquema de Horner:
function s = horner ( a , x )
s = a(end);
for k=length(a)-1:-1:1
s = a(k) + x*s;
end
Para el primer paso, dividimos las dos operaciones en 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
Luego presentamos las _err
variables. Tenga en cuenta que las entradas a
y x
se supone que son exactas, pero también podríamos requerir que el usuario pase los valores correspondientes para a_err
y 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
Finalmente, aplicamos las reglas descritas anteriormente para obtener los términos de error:
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
Tenga en cuenta que como no tenemos a_err
o x_err
, por ejemplo, se supone que son cero, los términos respectivos simplemente se ignoran en las expresiones de error.
Et voilà! Ahora tenemos un esquema de Horner que devuelve una estimación de error dependiente de los datos (nota: este es un límite superior del error) junto con el resultado.
Como nota al margen, dado que está utilizando C ++, puede considerar crear su propia clase para valores de punto flotante que conlleva el _err
término y sobrecargar todas las operaciones aritméticas para actualizar estos valores como se describió anteriormente. Para códigos grandes, esta puede ser la ruta más fácil, aunque computacionalmente menos eficiente. Dicho esto, es posible que pueda encontrar dicha clase en línea. Una búsqueda rápida en Google me dio este enlace .
± ux ( 1 ± u )