Precision difference: Octave vs. C++ and C++ decimal ceil function.

Me he topado con este problema: “El “mismo” código en Octave y en C++ arroja diferentes resultados.”

Seguramente soy un torpe y el problema está en mi “traducción” del código, pero es un problema que me sé de carrerilla -generar coordenadas de perfiles NACA- y que no tiene mucho misterio… pero lo dejo en la red por si alguien más ha tenido experiencias similares.

Octave por defecto muestra los resultados en su formato corto, eso ya lo he tenido en cuenta, se puede cambiar sin más que poner en la cabecera del script el formato deseado, en este caso “format long”.

Llegué a pensar que Octave redondeaba al sexto decimal a la hora de operar en su formato corto y de ahí que derivasen las diferencias entre Octave y el formato float de C++ que tiene más decimales. Nada.

Al contrario tampoco ha resultado la pirueta, más de lo mismo operando con double e incluso con Python, que da más decimales aún.

Creo que el error en concreto aumenta mucho cuando aplico una matriz de giro, por lo que sospecho que debe estar en las funciones trigonométricas, no se me ocurre otra cosa y ya no sé que pensar. Por la red he leído que si las librerías de intel y LAPACK, que si armadillo… pero nadie tiene nada claro y no tengo ganas de incluir librerías no estándar para hacer unos cálculos simples.

Por probar se me ha ocurrido redondear el código en C++ al sexto decimal para ver si el resultado es más similar al de Octave, pero me ha sorprendido ver que no hay (o no se de su existencia) una función para redondear decimales a la precisión deseada. Ahí va mi intento:


inline float decCeil(float value)
{
     int precision = 5;
     float sup,inf, result;
     inf = ceil(value*pow(10,precision)) -1;
     sup = value*pow(10, precision);
 
     if( sup - inf < 0.5)
     {
          result = inf/pow(10,precision);
     }
     else
     {
          result = ceil(sup)/pow(10, precision);
     }
     
     if(result == 0.0)
     {
          return 0.0;
     }
     return result;
}

Aquí están algunas de las diferencias, hay algunos casos en los que el resultado es el mismo si se redondea adecuadamente, pero al parecer eso es algo que Octave hace de manera natural y C++ no, ahora me explico mejor:

ans =

   1-0.00000
   2-0.00408
   3-0.02212
   4-0.05382
   5-0.09848
   6-0.15490 -> en C++ es: 0.154898
   7-0.22146
   8-0.29613
   9-0.37659
   10-0.45943
   11-0.54286
   12-0.62489
   13-0.70327
   14-0.77585
   15-0.84070
   16-0.89614
   17-0.94074
   18-0.97340
   19-0.99331
   20-1.00000

C++ output
xu[1]: 0
xu[2]: 0.00408304
xu[3]: 0.0221203
xu[4]: 0.053825
xu[5]: 0.098477
xu[6]: 0.154898
xu[7]: 0.221458
xu[8]: 0.296132
xu[9]: 0.376591
xu[10]: 0.459433
xu[11]: 0.542862
xu[12]: 0.624897
xu[13]: 0.703268
xu[14]: 0.775848
xu[15]: 0.840702
xu[16]: 0.896139
xu[17]: 0.940742
xu[18]: 0.973398
xu[19]: 0.99331
xu[20]: 1
Esta entrada fue publicada en básico, C/C++ y etiquetada , . Guarda el enlace permanente.

4 respuestas a Precision difference: Octave vs. C++ and C++ decimal ceil function.

  1. Juanlu001 dijo:

    Hm, respecto al tema del redondeo aquí va una pregunta de SO:

    http://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c

    En cuanto a este tema de la precisión, tiene que ser algo muy sencillo y elemental. No creo que haga falta incluir librerías no estándar.

    ¿Los tres resultados difieren? Intentaría también utilizar un depurador, o en el peor de los casos sentencias print. Si es un error de precisión o redondeo se va a ver nada más empiece el programa o al cabo de pocas iteraciones. Esto es lo primerísimo que probaría.

    Después, iría a ver si hay algún problema con la forma de realizar las operaciones de cada programa. Octave usa LAPACK, así que puedes echar un ojo a las opciones de compilación a ver qué precisión está utilizando http://www.network-theory.co.uk/docs/octave3/octave_311.html. NumPy se puede enlazar a LAPACK (y SciPy creo que lo usa por defecto), así que también puedes echarle un ojo a la configuración http://www.scipy.org/Installing_SciPy/BuildingGeneral. Porque no has usado Python sin NumPy, ¿verdad?

    Esto es todo lo que se me ocurre de momento. ¿Alguna idea más?

    • Samuel dijo:

      Esta es interesante, ¿verdad?

      PARTE I;
      De acuerdo contigo en lo de las librerías, aunque éstas sí están operando detrás de Octave como tú bien dices, LAPACK por ejemplo.

      He usado depurador en C++ y antes ya había comparado los datos a lo bruto -a base de cout y print- con los de Octave en cada paso.
      No hay iteraciones, por lo que el caso es todavía más simple solo operaciones con datos calculados previamente en la rutina, o debería serlo.

      No he usado Python sin Numpy. Las opciones de compilación sí que han podido ser un problema, Octave para 32 y las librerías LAPACK en 64 bits (instaladas manualmente, junto con el compilador de Fortran para 64 bits), pero hay que ser muy rebuscado para llegar hasta tan bajo ¿No crees? Aunque visto lo visto…

      Desconozco si Octave ha instado sus librerías de LAPACK o está usando las que yo instalé en el sistema, tengo que mirarlo.

      PARTE II;
      Bien, los resultados son iguales si se redondea adecuadamente, ¿cierto? el problema es que si redondeo manualmente los resultados de C++ en cada paso el error se propaga en la siguiente operación. Eso me lleva a pensar que lo único que redondea Octave es la salida de datos y que internamente trabaja con muchos más decimales. Si eso es así, el error lo debo buscar en la falta de precisión de C++ ¿debo entonces trabajar con formatos de datos mayores (long) en C++ para que al final de la rutina los resultados concuerden?

      Eso es lo siguiente que voy a probar. Gracias por el cable!

      • Juanlu001 dijo:

        PARTE I
        ¿Qué sistema operativo usas? Posiblemente esas diferencias de configuración tengan algo que ver, pero no lo sé.

        PARTE II
        En teoría los float en Python son double de C (http://docs.python.org/2/library/stdtypes.html#numeric-types-int-float-long-complex). Si tienes dudas de la precisión de Octave, ¿esto vale de algo?

        octave:1> eps = 1.0
        eps = 1
        octave:2> while 1
        > if 1.0 + eps == 1.0
        > break;
        > endif
        > eps = eps / 2;
        > endwhile
        octave:3> eps
        eps = 1.1102e-16
        octave:4> output_precision (17)
        octave:5> eps
        eps = 1.1102230246251565e-16

        >>> eps = 1.0
        >>> while True:
        … if 1.0 + eps == 1.0:
        … break
        … eps = eps / 2

        >>> eps
        1.1102230246251565e-16

        $ cat < prec.cpp
        > #include
        > #include
        > using namespace std;
        >
        > int main()
        > {
        > double r, eps;
        > eps = 1.0;
        > r = 1.0;
        > while (r + eps != r) {
        > eps = eps / 2;
        > }
        > cout << setprecision (17) << eps < return 0;
        > }
        > EOF
        $ g++ prec.cpp
        $ ./a.out
        1.1102230246251565e-16

      • Juanlu001 dijo:

        La parte de C++ de mi comentario está mal, pero tú ya sabes por qué😛

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s