Comparando un doble e int, sin conversión ni conversión.

En uno de los módulos de C++ que tenemos, tenemos un lenguaje de evaluación de expresiones.

                                \
EVDataElement NAME::eval(   const EvalContext &ec,                          \
                        const bool recursiveFlag,                       \
                        EVEvaluatorTraceFormatter * trace )             \
{                                                                           \
/*  EVTimer timer("(DECLARE_REL_EVAL)","eval","*", "", 1,1, 3);      */     \
    EVDataElement val (                                                     \
        (left->eval(ec, recursiveFlag, trace))                              \
        OP (right->eval(ec, recursiveFlag, trace)) );                       \
    return val;                                                             \
}

DECLARE_REL_EVAL(oLT,<)
DECLARE_REL_EVAL(oLE,<=)
DECLARE_REL_EVAL(oGT,>)
DECLARE_REL_EVAL(oGE,>=)
DECLARE_REL_EVAL(oEQ,==)
DECLARE_REL_EVAL(oNE,!=)

El módulo permite establecer ciertas reglas de configuración.

Entonces, si había una regla en la base de datos que decía field1 - field2 > param1, verifica esta condición pasando al lenguaje de expresión anterior y devuelve un resultado.

El problema al que nos enfrentamos ahora es, por ejemplo, param1 = 3, field1 = 6.15 y field2 = 3.15

Dice que el resultado es verdadero. Y creo que es porque la diferencia de 6.15 y 3.15 da como resultado 3.00

Y cuando se compara 3.00 con 3, piensa que 3.00 es mayor. ¿Hay alguna manera de evitar esto?

La razón por la que dije que no podemos usar la conversión es porque nunca sabemos qué tipo de datos podría aparecer para la izquierda y la derecha. Espero que esta pregunta haya tenido sentido.

preguntado el 02 de mayo de 12 a las 19:05

¿Cómo sabes que el valor es 6.15? ¿Estás seguro de que no es 6.150000000000001? -

Para muchos casos en los que está comparando flotantes, debe al menos encontrar la diferencia absoluta y comparar para verificar si es menor que un épsilon pequeño (por ejemplo, 1e-10) que se adapte a sus necesidades. -

@MooingDuck: no sé cómo se almacena en la memoria, pero ese valor se lee de la base de datos y en la base de datos se almacena 6.15. Estoy tratando de encontrar el código que lee los valores de la base de datos, pero creo que lo lee como un doble. Entonces, incluso en la memoria, debería ser 6.150000 -

2 Respuestas

Obtendrá las "conversiones habituales" cuando trate con valores de diferentes tipos primitivos. No creo que haya forma de evitar esto.

Si estas comparando intpara doubles vas a tener que idear las reglas que quieras usar para determinar si dos valores están "lo suficientemente cerca". Puede considerar usar el std::modf función (en <cmath>) al hacer la comparación.

Considerar:

#include <iostream>
#include <cmath>

int main()
{
    double d = 6.15 - 3.15;
    std::cout << std::boolalpha;
    std::cout << "d == 3.0: " << (d == 3.0) << '\n';
    double i;
    d = std::modf(d, &i);
    std::cout << "i = " << i << ", d = " << d << '\n';
    std::cout << "i == 3.0: " << (i == 3.0) << '\n';
}

Usar Visual Studio 2010 en la configuración predeterminada (es decir, NO usar fastmath) Yo obtengo:

d == 3.0: false
i = 3, d = 4.44089e-016
i == 3.0: true

3.0 puede ser representable exactamente en matemáticas binarias de punto flotante, pero 6.15 - 3.15 no es 3.0 en matemáticas binarias de punto flotante.


Ya ha habido dos referencias al papel "Lo que todo informático debe saber sobre la aritmética de coma flotante" que describe cómo funcionan las matemáticas binarias de coma flotante y cómo no siempre se ajustan a las expectativas humanas. El punto principal a recordar es que casi nunca querrás comparar dos números de coma flotante para la igualdad, especialmente si uno (o ambos) de esos números es el resultado de una operación matemática.

En su caso, sin embargo, está tratando de comparar un double con una int, y tengo que asumir que quieres algo redondeo Es posible que desee considerar 3.1 ser equivalente a 3. Puede que no. Realmente no tengo ni idea.

Si tuviera que usar las convenciones de redondeo que se enseñan en la escuela primaria (redondear hacia arriba en .5 o más), podría hacer algo como:

#include <iostream>
#include <cmath>

int main()
{
    double d = 6.15 - 3.15;
    std::cout << std::boolalpha;
    std::cout << "d == 3.0: " << (d == 3.0) << '\n';
    // note:  this rounds negative numbers the wrong direction
    std::cout << "d is 'close enough' to 3.0: " << (std::floor(d + 0.5) == 3.0) << '\n';
}

Hay posibilidades mucho más complejas, incluida una descrita en el documento.

contestado el 02 de mayo de 12 a las 20:05

Intenté esto y usé double d = 6.1 - 3.1 y tengo i = 2 and d = 1 . ¿Qué sugiere eso? falló la igualdad con 3.0, de hecho dijo que es menos de 3.0 - roymustang86

@rorymustang: si std::modf(6.1 - 3.1, &i) devoluciones 1 (Es decir, d = 1, entonces sospecho que d es menor que, pero muy cercano a 1.0. Especialmente desde d + 2 < 3.0. - Max Lybbert

@rorymustang: es posible que no se dé cuenta de que los números de punto flotante a menudo se redondean cuando se muestran (este es un problema separado de "las matemáticas de punto flotante en la computadora se calculan de manera diferente a lo que hace a mano"). std::cout ,, 1.23456789 probablemente mostrará algo como 1.23457. - Max Lybbert

sí, usé setprecision() y descubrí cómo mostrar todos los números. - roymustang86

UPS. eso debería haber sido std::cout << 1.23456789; o, más al punto, std::cout << .9999999 probablemente mostrará 1. - Max Lybbert

Si desea field1 - field2 > param1 por param1 = 3, field1 = 6.15 y field2 = 3.15 entonces tendrás que usar infinite precision aritmética.

Por favor, lea el documento en Aritmética de coma flotante mencionado anteriormente.

contestado el 02 de mayo de 12 a las 19:05

Oye, estoy tratando de encontrar una manera de usar precisión infinita en C++. ¿Cómo puedo mostrar la precisión completa? Intenté usar setprecision cplusplus.com/reference/iostream/manipuladores/setprecision, pero el compilador bajo solaris no parece reconocer la función. - roymustang86

@ rorymustang86: la biblioteca estándar de C ++ no ofrece una biblioteca de precisión infinita. GNU ofrece GNU GMP ( gmplib.org ) y GNU MPFR ( mpfr.org ), ambos bajo la LGPL. Puede encontrar una lista de otras bibliotecas en oonumerics.org/oon (busque "Multiprecisión, tipos de datos de precisión arbitraria"). - Max Lybbert

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.