Análisis de error numérico en la función C ++


20

Supongamos que tengo una función que toma como entrada varios valores de punto flotante (simple o doble), hace algunos cálculos y produce valores de punto flotante de salida (también simple o doble). Estoy trabajando principalmente con MSVC 2008, pero también planeo trabajar con MinGW / GCC. Estoy programando en C ++.

¿Cuál es la forma típica de medir mediante programación cuánto error hay en los resultados? Suponiendo que necesito usar una biblioteca de precisión arbitraria: ¿cuál es la mejor biblioteca de este tipo si no me importa la velocidad?

Respuestas:


17

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:

  1. Vuelva a factorizar su código para que tenga una sola asignación de una sola operación aritmética en cada línea.
  2. Para cada variable, por ejemplo x, cree una variable x_errque se inicialice a cero cuando xse le asigne una constante.
  3. Para cada operación, por ejemplo z = x * y, actualice la variable z_errutilizando el modelo estándar de aritmética de punto flotante y los zerrores resultantes y de ejecución x_erry y_err.
  4. El valor de retorno de su función también debe tener un _errvalor 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/2es 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 aen un punto xusando 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 _errvariables. Tenga en cuenta que las entradas ay xse supone que son exactas, pero también podríamos requerir que el usuario pase los valores correspondientes para a_erry 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_erro 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 _errté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 .

±tuX(1±tu)


1
+1 para este análisis, porque es interesante. Me gusta el trabajo de Higham. Lo que me preocupa es que requerir que un usuario escriba ese código adicional a mano (en lugar de la aritmética de intervalos semiautomática) podría ser propenso a errores a medida que aumenta el número de operaciones numéricas.
Geoff Oxberry

1
@ GeoffOxberry: estoy completamente de acuerdo con el problema de la complejidad. Para códigos más grandes, recomendaría escribir una clase / tipo de datos que sobrecargue las operaciones en dobles, como tener que implementar cada operación correctamente una sola vez. Estoy bastante sorprendido de que no haya algo así para Matlab / Octave.
Pedro

Me gusta este análisis, pero dado que el cálculo de los términos de error también se realiza en coma flotante, ¿no serán imprecisos esos términos de error debido a errores de coma flotante?
plasmacel

8

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

    float fa,fb;
    double da,db,err;
    fa = 4.0;
    fb = 3.0;
    fa = fa/fb;
    fa -= 1.0;

    da = 4.0;
    db = 3.0;
    da = da/db;
    da -= 1.0;

    err = fa - da;
    printf("Single precision error wrt double precision value\n");
    printf("Error in getting 1/3rd is %e\n",err);
    return 0;

La salida desde arriba (cadena de herramientas Cygwin / MinGW32 GCC):

Single precision error wrt double precision value
Error in getting 1/3rd is 3.973643e-08

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.


Su enfoque es definitivamente matemáticamente sólido. Creo que la compensación está en rigor; las personas que son pedantes con respecto al error señalarán el rigor de la aritmética de intervalos, pero sospecho que en muchas aplicaciones, calcular con mayor precisión sería suficiente, y las estimaciones de error resultantes probablemente serían más estrictas, como usted señala.
Geoff Oxberry

Este es el enfoque que estaba imaginando que usaría. Podría probar algunas de estas técnicas diferentes para ver cuál es la más adecuada para mi aplicación. ¡La actualización del ejemplo de código es muy apreciada!
user_123abc

7

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.


Ah, acabo de notar la aritmética de intervalos mencionada en tu respuesta ... ¡Votada!
Ali

2
Richard Harris escribió una excelente serie de artículos en la revista ACCU Overload sobre Floating Point Blues . Su artículo sobre aritmética de intervalos está en Overload 103 ( pdf , p19-24).
Mark Booth

6

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:

[a,b] + [c,d] = [min(a+c, a+d, b+c, b+d), max (a+c, a+d, b+c, b+d)] = [a+c, b+d]

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.


4

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


Estoy de acuerdo; La integración numérica es definitivamente un caso en el que la aritmética de intervalos ingenuos puede salir mal. Sin embargo, incluso los modelos Taylor usan aritmética de intervalos. Estoy familiarizado con el trabajo de Makino y Berz, y creo que usan el modelo Taylor en el sentido de RE Moore, aunque también usan trucos que involucran lo que llaman "álgebra diferencial".
Geoff Oxberry

@ GeoffOxberry: Sí, creo que este álgebra diferencial es algo para obtener un límite en el error en el paso de integración.
Erik P.

0

Me dicen que MPIR es una buena biblioteca para usar si está trabajando con Visual Studio:

http://mpir.org/


Bienvenido a SciComp.SE! ¿Podría agregar algunos detalles sobre cómo se puede usar esta biblioteca para medir el error de los cálculos de coma flotante?
Christian Clason

Intentaré; En realidad, todavía no he configurado MPIR en mi computadora. He configurado GMP y MPFR.
fishermanhat
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.