User Tag List

Página 1 de 2 12 ÚltimoÚltimo
Resultados 1 al 15 de 18

Tema: Problema con la precisión de los doubles

  1. #1

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts

    Problema con la precisión de los doubles

    Hola a todos:

    Si hay dos cosas que no soporto de la programación, es trabajar con físicas y con números decimales, y en ambos casos por lo mismo: por la precisión.
    Con las físicas es que nunca sabes cómo va a caer un objeto... o si ya ha caído, porque se me han dado casos en que un objeto no para de rebotar contra el suelo, y, aunque parece parado, se está moviendo constantemente milésimas de pixel.

    Bueno, pues el caso es que tengo un proyecto en el que manejo cantidades de tipo double. Me pasa lo típico: que tengo que hacer una resta, o una suma, o cualquier operación, y el resultado, en unas ocasiones muy específicas es un valor con un error infinitesimalmente pequeño (por ejemplo, 5 - 5 = 1.389392746957235E-16). Y claro, eso luego no es cero y le dice al usuario que no ha terminado el trabajo, o peor, que el resultado sea negativo y le diga que no tiene cantidad suficiente para realizar la operación, cuando este está viendo "disponible: 5Kg, seleccionado: 5Kg".

    ¿Existe alguna forma de forzar que las asignaciones de valores a variables se redondéen a 3 decimales, sin tener que poner en todo el código Math.Round(operación, 3) ? Es que es un peñazo de narices, y en (muchas) ocasiones, tendría que hacer la siguiente sustitución:
    cant += valor;
    cant = Math.Round( cant + valor, 3);

    Sí, soy un flojo ¿qué pasa?
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  2. #2

    Fecha de ingreso
    Jul 2006
    Ubicación
    Madriz
    Mensajes
    1,297
    Mencionado
    28 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    175
    Agradecer Thanks Received 
    322
    Thanked in
    Agradecido 172 veces en [ARG:2 UNDEFINED] posts
    Consejo sencillo, evita cosas como

    if (resultado == 0)

    Y emplea

    if (abs(resultado) < delta)

    Te quitarás muchos dolores de cabeza, especialmente con las físicas, interpolaciones y un largo etcétera.

  3. Los siguientes 4 usuarios agradecen a DarkDijkstra este post:

    Drumpi (21/06/2023), fbustamante (21/06/2023), hardyx (30/06/2023), swapd0 (21/06/2023)

  4. #3

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts
    Iba a comer, y yutú me ha recomendado este vídeo ^^U


    Understandin de floatin point nambers!
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  5. El siguiente usuario agradece a Drumpi este mensaje:

    josepzin (27/06/2023)

  6. #4

    Fecha de ingreso
    Aug 2003
    Mensajes
    681
    Mencionado
    5 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    12
    Agradecer Thanks Received 
    92
    Thanked in
    Agradecido 55 veces en [ARG:2 UNDEFINED] posts
    Cuando se trabaja con cantidades monetarias una solucion bastante aceptable es simplemente modelar con céntimos y enteros gorditos, como Long, así te ahorras problemas de redondeo y errores de precision. El problema es si necesitas fracciones de centimo, cosa que casi nunca sucede, que entonces hay que usar otras cosas.

  7. #5

    Fecha de ingreso
    Mar 2007
    Ubicación
    Barna
    Mensajes
    10,351
    Mencionado
    93 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    371
    Agradecer Thanks Received 
    1,795
    Thanked in
    Agradecido 949 veces en [ARG:2 UNDEFINED] posts
    Imagino que viendo el video ya habrás visto que esto no va a funcionar:

    ¿Existe alguna forma de forzar que las asignaciones de valores a variables se redondéen a 3 decimales, sin tener que poner en todo el código Math.Round(operación, 3) ? Es que es un peñazo de narices, y en (muchas) ocasiones, tendría que hacer la siguiente sustitución:
    cant += valor;
    cant = Math.Round( cant + valor, 3);
    La documentación ya te avisa: https://learn.microsoft.com/en-us/do...-and-precision Y eso es porque las variables en coma flotante no pueden representar todos los posibles números reales así que aproximan. Cosas de la memoria finita. En particular, la comparación "==" con números decimales funciona realmente mal en informática y hay que hacer algo como lo que dice DarkDijkstra: mirar si la diferencia es mejor que un umbral.

    Lo que también puedes hacer es lo que dice amkam. Modela unidades más pequeñas que no sean divisibles usando variables enteras: céntimos en un precio, minutos de una jornada laboral, gramos de producto en vez de kilos... y haces el cambio a euros/horas/kilos solo en el momento de mostrarlo en pantalla, pero los cálculos internos son en la unidad indivisible. Así los cálculos son todos con enteros y no hay problema de redondeo.

    Una solución equivalente a esto último es usar variables en coma fija, aunque no sé cómo hacerlo en C#.
    Última edición por juanvvc; 28/06/2023 a las 12:47
    "Todo es absolutamente falso, salvo alguna cosa"

  8. #6

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts
    Yo suelo usar double, más que nada, porque es el formato usado en la BBDD.
    El problema viene porque tenemos clientes empeñados en usar toneladas como unidad base de medida, y cuando empiezan a contar sacos de 25Kg, pues muchas veces, ese redondeo me fastidia, porque al quitar 0'025T a 0'05T, pues me quedan 0'024444444445T, y ya la hemos liado parda.
    Lo que he tenido que hacer es redondear todos los resultados de las operaciones a 3 decimales, y en lugar de hacer "cant -= selec", pues he tenido que hacer "cant = Math.Round(cant - select, 3)", si no, pues me daba ese error.

    Y así, pues con 40 operaciones más por todo el código.
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  9. #7

    Fecha de ingreso
    Jul 2006
    Ubicación
    Madriz
    Mensajes
    1,297
    Mencionado
    28 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    175
    Agradecer Thanks Received 
    322
    Thanked in
    Agradecido 172 veces en [ARG:2 UNDEFINED] posts
    No hacía eso DIV con los ángulos? Me suena (así de memoria) que usaban "miligrados" o algo similar, y hale, a correr con enteros.

    Y oye, si ya te quieres reír, junta floats con enteros y haz castings así a lo loco, igual te sale la raíz cuadrada de tu número : P

    Código:
    float Q_rsqrt( float number )
    {
     long i;
     float x2, y;
     const float threehalfs = 1.5F;
    
     x2 = number * 0.5F;
     y  = number;
     i  = * ( long * ) &y;                       // evil floating point bit level hacking
     i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
     y  = * ( float * ) &i;
     y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
    // y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
    
     return y;
    }
    (Función que hace la raíz cuadrada "rápida" de un float, sacado del código del Quake III)

    Hay mucha magia negra con la coma flotante, no sé si merece la pena perder cordura por aprenderlo en detalle.

  10. #8

    Fecha de ingreso
    Mar 2007
    Ubicación
    Barna
    Mensajes
    10,351
    Mencionado
    93 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    371
    Agradecer Thanks Received 
    1,795
    Thanked in
    Agradecido 949 veces en [ARG:2 UNDEFINED] posts
    sí, a mí también me sonaba que DIV usaba mili-unidades pero no recordaba dónde así que no lo he comentado

    Drumpi, recuerda que lo de "cant = Math.Round(cant - select, 3)" a veces no funcionará, ya te lo avisa en la documentación. Simplemente, hay números que no se pueden representar en coma flotante hagas lo que hagas. Ahí tienes un pequeño bug potencial que saltará en el momento más inoportuno.
    Última edición por juanvvc; 28/06/2023 a las 13:04
    "Todo es absolutamente falso, salvo alguna cosa"

  11. #9

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts
    Correcto, DIV usaba milésimas de grado como unidad para rotaciones y demás, más que nada porque no tenía tipos de datos en coma flotante, sólo BYTE, WORD, INT y CHAR, ni siquiera STRING, creo recordar... y puede que PTR.
    La única concesión que se permitía era el número PI, que es una constante del sistema, para hacer operaciones con radianes (que, mágicamente, funcionaban sin hacer conversiones).

    Sí, he visto en el vídeo que hay números que no existen, pero creo que me puedo permitir redondeos a 3 decimales ¿no? Creo que Épsilon queda muy lejos de 0'001. Es que si no, lo único que me queda es decirle al cliente que deje de ser cabezota, y que use Kg en sus documentos, que ya la APP se encarga del resto (lo de la conversión de unidades de medida es un quebradero de cabeza, ya que depende de los datos de la BBDD, no se pueden usar "conversiones estándar", y por tanto, la APP no "sabe" hacer conversiones... y aún así, la APP no tiene inconveniente en usar T o Kg).
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  12. #10

    Fecha de ingreso
    Jan 2008
    Ubicación
    Madrid
    Mensajes
    4,451
    Mencionado
    14 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    87
    Agradecer Thanks Received 
    161
    Thanked in
    Agradecido 94 veces en [ARG:2 UNDEFINED] posts
    Los números en coma flotante son un dolor de cabeza si no andas con cuidado, incluso con cuidado también son un dolor de cabeza.

    Yo lo que haría en tu caso del cliente que quiere usar toneladas (imagino que en la entrada de datos), sería una de dos:
    - Aceptar las unidades en toneladas en la entrada y convertirlas a gramos por ejemplo, asi usas un long que soporta montones de números y cero problemas de precisión.
    - Si es obligatorio usar decimales por alguna oscura razón del proyecto, usar decimales, pero también usar una unidad menor como Kilos o gramos internamente.

    Ten en cuenta que usando una unidad muy grande (toneladas) los errores de precisión son mayores. Por eso siempre usar la unidad más pequeña. Por ejemplo, para medir las habitaciones de un piso usamos metros y decimales. Meeeeeeh! error!!!. Usa milímetros y valores enteros, asi nunca vas a perder precisión. Bueno realmente usa la unidad con la que sepas que debajo de ella te importa poco la precisión. Si no te importa perder centímetros o milímetros, pues bien usa metros decimales.

    Para convencer a cliente, dile que 1 + 1 son 3. Y te dirá ¿qué dices loco? Y le repites, uno mas uno no son dos, son tres te lo juro.
    Y le enseñas esto: 1.999999999 + 1.000000001 = 3.0. Y le dices, esto es lo que pasa si no usas las unidades correctas.
    Última edición por hardyx; 30/06/2023 a las 17:04

  13. #11

    Fecha de ingreso
    Sep 2006
    Ubicación
    Malaga
    Mensajes
    7,557
    Mencionado
    47 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    1,661
    Agradecer Thanks Received 
    1,929
    Thanked in
    Agradecido 1,293 veces en [ARG:2 UNDEFINED] posts
    Como dicen depende de la precision que tengas, pero yo haria que internamente trabajase en gramos (si la entrada en es toneladas dudo mucho que necesitres mas precision) representados por un int64_t, depues con unas funciones para imprimir el resultado en toneladas o kilos te vale.
    No es lo mismo tener diez años de experiencia, que tener un año de experiencia diez veces.


    It is an undisputed truth that the Atari ST gets the best out of coders. No dedicated hardware, just the CPU and a frame buffer! Some call it Spartan, others name it Power Without The Price, and a select few say `challenge accepted'! --- by spkr from smfx

  14. #12

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts
    Sí, la solución esa sería la ideal, pero tiene una pega: estáis pensando en unidades de medida del sistema métrico decimal.
    En un ERP, las unidades de medida (UDM) no se limitan a metros o kilos. Por ejemplo, para temas de peso, se puede establecer un grupo de UDM, cuya unidad principal sea el Kg (no el gramo), y luego definir una UDM llamada "gramo", que corresponde a 0'001Kg y "tonelada" que son 1000Kg, pero también "bolsa100g" que equivale a 0'1Kg, "saco" que equivale a 50Kg... y en una factura puedo tener, de pronto, 50Kg de peras, 2 sacos, y 100 bolsas, y como la APP no sabe hacer conversiones de UDM (porque, por diseño, decidimos no solicitar la tabla de conversiones para cada operación, ya que la APP es un terminal "tonto" que depende el 85% del tiempo del servidor), pues aquí tenemos el problema.

    O sea, que las UDM no existen, son los padres... digooo, las tablas de conversiones.
    Y eso cuando no hablamos de otro tipo de medidas más "abstractas" como "unidades" (el que usan los supermercados para sus facturas), "sobres", "minutos" o cualquier otra que se os pueda ocurrir.

    ¿Queréis complicar más la cosa? pues el ERP os la complica: existen UDM por defecto para ventas, para compras y para inventario, y según si estamos haciendo una factura, una recepción de mercancía o un traslado de almacén, cada tipo de documento usa una UDM diferente. Al ERP la UDM le da igual, pero cuando yo tengo que hacer una factura de un traslado (por decir algo), me entran los sudores fríos.
    De ahí la importancia de que el cliente sea consciente de esto, y elija las UDM más adecuadas. Pero lo dicho, si nuestro cliente lleva trabajando en T desde que Curro se quitó el traje, no lo vas a convencer de que se cambie ahora, por lo que el marrón recae en el "pringao" que realiza el diseño, y luego, en el "currifichante" que se encarga de generar el código (servidor). El código debe ser robusto, no importa lo extravagante que sea el cliente.

    Y yo que pensaba que programar videojuegos era complicado ^^U
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  15. #13

    Fecha de ingreso
    Sep 2006
    Ubicación
    Malaga
    Mensajes
    7,557
    Mencionado
    47 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    1,661
    Agradecer Thanks Received 
    1,929
    Thanked in
    Agradecido 1,293 veces en [ARG:2 UNDEFINED] posts
    Cita Iniciado por Drumpi Ver mensaje
    por diseño, decidimos no solicitar la tabla de conversiones para cada operación, ya que la APP es un terminal "tonto" que depende el 85% del tiempo del servidor), pues aquí tenemos el problema.
    Pues a cambiar eso, de todas formas creo que lo suyo seria pedir la tabla al arrancar la aplicacion (inicio del dia) y no en cada operación.

    No creo que sea buena idea arrastrar una decision "mala" si te complica todas las demas operaciones.
    No es lo mismo tener diez años de experiencia, que tener un año de experiencia diez veces.


    It is an undisputed truth that the Atari ST gets the best out of coders. No dedicated hardware, just the CPU and a frame buffer! Some call it Spartan, others name it Power Without The Price, and a select few say `challenge accepted'! --- by spkr from smfx

  16. #14

    Fecha de ingreso
    Sep 2005
    Mensajes
    15,185
    Mencionado
    248 Post(s)
    Tagged
    1 Tema(s)
    Agradecer Thanks Given 
    671
    Agradecer Thanks Received 
    1,852
    Thanked in
    Agradecido 1,269 veces en [ARG:2 UNDEFINED] posts
    A ver, es una APP móvil, la información no puede quedarse almacenada toda la sesión. Ya es contraproducente que se quede guardada en memoria durante el tiempo que se trabaje en una página (una página es una pantalla, todo lo que se ve antes de darle a "siguiente"), porque si alguien modifica la tabla de conversión de UDM, no habría forma de actualizar los datos sin salir y volver a entrar a la misma página, perdiendo los datos que ya se han modificado.
    Por lo general, cada vez que paso de una página a otra, intento refrescar aquellos datos que hayan podido ser modificados en la BBDD, aunque tenga que volver a cargar los datos que ya cargué en la anterior (es algo que me enseñaron ya cuando hacía aplicaciones Java).

    Sí que debería replantearlo, de hecho, me lo he planteado más de una vez, pero el proyecto ahora mismo es enorme, y no me dan tiempo suficiente para arreglar cosas que se van quedando en el tintero (que funcionan, pero que se podría hacer mejor). Aparte, que no sé qué otras cosas hay por ahí que se puedan "romper". Hay un sistema de códigos que van asociados tanto a los artículos como a sus UDM, y si meto ahora conversión de datos, habría que replantear si la búsqueda con esos códigos implica, además, buscar artículos en diferentes UDM... Se complica mucho la cosa.

    Ahora mismo veo cien veces más sencillo lidiar con decimales que replantear la estructura básica de unidades de la APP.
    PROYECTOS REALIZADOS: FrikiMusic, Motor Scroll Tileado v3.2, Venturer2X (GP2X/WIZ), Echo, Screen Break Time
    PROYECTOS EN MARCHA (algunos): Bennu GP2X: 95% (necesito ayuda) ¡Antes de Halloween!: 92% SpaceH2H: 8%

  17. #15

    Fecha de ingreso
    Sep 2006
    Ubicación
    Malaga
    Mensajes
    7,557
    Mencionado
    47 Post(s)
    Tagged
    0 Tema(s)
    Agradecer Thanks Given 
    1,661
    Agradecer Thanks Received 
    1,929
    Thanked in
    Agradecido 1,293 veces en [ARG:2 UNDEFINED] posts
    Una representación grafica
    No es lo mismo tener diez años de experiencia, que tener un año de experiencia diez veces.


    It is an undisputed truth that the Atari ST gets the best out of coders. No dedicated hardware, just the CPU and a frame buffer! Some call it Spartan, others name it Power Without The Price, and a select few say `challenge accepted'! --- by spkr from smfx

  18. Los siguientes 2 usuarios agradecen a swapd0 este post:

    Drumpi (10/07/2023), josepzin (09/07/2023)

Página 1 de 2 12 ÚltimoÚltimo

Etiquetas para este tema

Permisos de publicación

  • No puedes crear nuevos temas
  • No puedes responder temas
  • No puedes subir archivos adjuntos
  • No puedes editar tus mensajes
  •