1

Si tenemos dos valores tales como a = 0.999999... y b = 1.0, es decir que son representaciones decimales del mismo número real 1, no obstante la expresión x == y será evaluada como false... el siguiente código funciona, pero si uso un bucle para imprimir por pantalla la altura del objeto en función del tiempo t entonces la altura h acaba siendo ligeramente negativa en lugar de devolverme cero...

//
// describir la trayectoria de un objeto que cae desde una  
// altura de 100 metros hasta que se detiene en el suelo
//

#include <iostream>
#include <cmath>

constexpr double altura_sub0{ 100.0 };
constexpr double g{ 9.81 };

int main()
{
    double t_iter{ 0.0 };
    double t{ sqrt(altura_sub0 / (g / 2)) };
    double h{ altura_sub0 };
    h = altura_sub0 - g*t*t / 2;
    if (h == 0.0)
        std::cout << "resultado correcto de h = 0 en tiempo "
        << t << " segundos." << std::endl;
    else
        std::cout << "fallo!, tras " << t << " segundos el objeto"
        "se encuentra en h = " << h << std::endl;
 }
  • 1
    Relacionada: http://es.stackoverflow.com/questions/197/por-qu%C3%A9-mis-programas-no-pueden-hacer-c%C3%A1lculos-aritm%C3%A9ticos-correctamente – Trauma Dec 23 '16 at 06:53
  • 2
    [Esta pregunta](http://es.stackoverflow.com/questions/29626/asignar-a-un-float-un-literal-de-punto-flotante-sin-sufijo/29651) puede ayudarte también. – PaperBirdMaster Dec 23 '16 at 07:31

2 Answers2

4

Debes evitar comparar números en coma flotante mediante la igualdad, cada uno de los números puede tener precisiones distintas según su tipo o su valor puede no ser exáctamente representable de manera binaria causando errores de redondeo.

Por este motivo debes compararlos por la casi igualdad, una función así te podría ser de ayuda:

#include <cstdint> 

bool casi_iguales(float izquierda, float derecha)
{
     return fabs(izquierda – derecha) <= FLT_EPSILON;
}

bool casi_iguales(double izquierda, double derecha)
{
     return fabs(izquierda – derecha) <= DBL_EPSILON;
}

Los valores FLT_EPSILON y DBL_EPSILON pertenecen a la cabecera <cstdint> y son la diferencia entre el valor 1 y el siguiente valor representable por float y double respectivamente; en otras palabras, son aproximadamente el menor valor representable por cada uno de los tipos, así que si la diferencia entre izquierda y derecha es menor o igual a este valor es que ambos valores son casi iguales.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
2

En general comparar dos variables double no se hace con ==, debido a la representación interna de esos valores reales (mantisa, exponente).

Lo más común es evaluar si la diferencia entre esos dos valores es menor que un EPSILON que previamente se define (de acuerdo a la precisión que se requiera), y si la diferencia es menor se dice que los números son iguales.

Función tipo

bool isEqual(double d1, double d2, double epsilon = 0.00000000001)
{
    return fabs(d1 - d2) < epsilon;
}

Ejemplo

#include <iostream>
#include <stdio.h>
#include <cmath>


bool isEqual(double d1, double d2, double epsilon = 0.000000001)
{
    return fabs(d1 - d2) < epsilon;
}

int main()
{
    double primero=.33333;
    double segundo=1.0/3.0;

    printf ("primero: %.20f\n", primero);
    printf ("segundo: %.20f\n", segundo);

    std::cout << "isEqual: " << (isEqual(primero, segundo)) << "\n"; //Epsilon por defecto
    std::cout << "isEqual: " << (isEqual(primero, segundo, 0.0001)) << "\n"; 

 }

Resultado

primero: 0.33333000000000001517
segundo: 0.33333333333333331483
isEqual: 0
isEqual: 1
Alejandro Montilla
  • 1,060
  • 1
  • 10
  • 22
  • 2
    Los números en coma flotante no se deben comparar entre sí usando el operador de comparación porque su representación no es exacta y está sujeta a a decimales no significativos que pueden "bailar" y hacer que la comparación falle. Por mucho que tengan mantisa y exponente su representación está normalizada, luego el mismo número se representará exactamente igual en dos variables diferentes... el problema es cuando realizamos operaciones sobre dicho valor. Dicho de otra forma: esto puede fallar: `(1e10+1.0-1.0==1e10)` mientras que esto no fallará `1e10==1e10` – eferion Jan 16 '17 at 16:50