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 )
Una buena biblioteca portátil y de código abierto para aritmética de punto flotante de precisión arbitraria (y mucho más) es la NTL de Victor Shoup , que está disponible en forma de fuente C ++.
En un nivel inferior se encuentra la Biblioteca Bignum GNU Multiple Precision (GMP) , también un paquete de código abierto.
NTL se puede usar con GMP si se requiere un rendimiento más rápido, pero NTL proporciona sus propias rutinas básicas que ciertamente se pueden usar si "no le importa la velocidad". GMP afirma ser la "biblioteca bignum más rápida". GMP está escrito principalmente en C, pero tiene una interfaz C ++.
Agregado: Si bien la aritmética de intervalos puede proporcionar límites superiores e inferiores en la respuesta exacta de manera automatizada, esto no mide con precisión el error en un cálculo de precisión "estándar" porque el tamaño del intervalo generalmente crece con cada operación (ya sea en forma relativa o sentido de error absoluto).
La forma típica de encontrar el tamaño del error, ya sea para redondear errores o para errores de discretización, etc., es calcular un valor de precisión adicional y compararlo con el valor de precisión "estándar". Solo se necesita una precisión adicional modesta para determinar el tamaño del error en sí con una precisión razonable, ya que los errores de redondeo por sí solos son sustancialmente mayores en precisión "estándar" que en el cálculo de precisión adicional.
El punto puede ilustrarse comparando cálculos de precisión simple y doble. Tenga en cuenta que en C ++ las expresiones intermedias siempre se calculan en (al menos) doble precisión, por lo que si queremos ilustrar cómo sería un cálculo en precisión simple "pura", debemos almacenar los valores intermedios en precisión simple.
Fragmento de código C
La salida desde arriba (cadena de herramientas Cygwin / MinGW32 GCC):
Por lo tanto, el error se trata de lo que se espera al redondear 1/3 a precisión simple. No me importaría (sospecharía) que obtener más de un par de decimales en el error correcto, ya que la medición del error es por magnitud y no por exactitud.
fuente
GMP (es decir, la biblioteca de precisión múltiple GNU) es la mejor biblioteca de precisión arbitraria que conozco.
No conozco ninguna forma programática para medir el error en los resultados de una función arbitraria de coma flotante. Una cosa que podría intentar es calcular una extensión de intervalo de una función utilizando la aritmética de intervalos . En C ++, tendría que usar algún tipo de biblioteca para calcular las extensiones de intervalo; una de esas bibliotecas es la biblioteca aritmética de intervalos de refuerzo. Básicamente, para medir el error, proporcionaría como argumentos a sus intervalos de función que tienen un ancho de 2 veces el redondeo de la unidad (aproximadamente), centrado en los valores de interés, y luego su salida sería una colección de intervalos, el ancho de lo que le daría una estimación conservadora del error. Una dificultad con este enfoque es que la aritmética de intervalos utilizada de esta manera puede sobreestimar el error en cantidades significativas, pero este enfoque es el más "programático" que se me ocurre.
fuente
La estimación de errores rigurosa y automática se puede lograr mediante análisis de intervalos . Trabajas con intervalos en lugar de números. Por ejemplo, además:
El redondeo también se puede manejar rigurosamente, consulte Aritmética de intervalos redondeados .
Siempre y cuando su entrada consista en intervalos estrechos, las estimaciones son correctas y su cálculo es muy económico. Desafortunadamente, el error a menudo se sobreestima, vea el problema de dependencia .
No conozco ninguna biblioteca aritmética de intervalos de precisión arbitraria.
Depende de su problema en cuestión si la aritmética de intervalos puede satisfacer sus necesidades o no.
fuente
La biblioteca GNU MPFR es una biblioteca flotante de precisión arbitraria que tiene una alta precisión (en particular, el redondeo correcto para todas las operaciones, que no es tan fácil como parece) como uno de sus principales puntos de enfoque. Utiliza GNU MP debajo del capó. Tiene una extensión llamada MPFI que hace aritmética de intervalos, que, como sugiere la respuesta de Geoff, podría ser útil para fines de verificación: siga aumentando la precisión de trabajo hasta que el intervalo resultante se encuentre dentro de límites pequeños.
Sin embargo, esto no siempre funcionará; en particular no es necesariamente efectivo si está haciendo algo como la integración numérica, donde cada paso conlleva un "error" independiente de los problemas de redondeo. En ese caso, pruebe un paquete especializado como COSY infinity que lo hace muy bien usando algoritmos específicos para limitar el error de integración (y usando los llamados modelos Taylor en lugar de intervalos).
fuente
Me dicen que MPIR es una buena biblioteca para usar si está trabajando con Visual Studio:
http://mpir.org/
fuente