Comparing a double and int, without casting or con

2019-07-28 05:19发布

问题:

In one of the C++ modules we have, we have an expression evaluation language.

                                \
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,!=)

The module allows certain configuration rules to be set.

SO, if there was a rule in the database that said field1 - field2 > param1, it verifies this condition by passing to the expression language above and returns a result.

The problem we are facing now is say param1 = 3, and field1 = 6.15, and field2 = 3.15

It says the result is true. And I think it is because the difference of 6.15 and 3.15 results in 3.00

And when 3.00 is compared with 3, it thinks 3.00 is greater. Is there any way to work around this?

The reason I said we can't use casting is because we never know what datatype might come through for left and right. I hope this question made sense.

回答1:

You're going to get the "usual conversions" when dealing with values of different primitive types. I don't think there's any way around this.

If you're comparing ints to doubles you're going to need to come up with whatever rules you want to use when determining if two values are "close enough." You may consider using the std::modf function (in <cmath>) in making the comparison.

Consider:

#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';
}

Using Visual Studio 2010 on default settings (i.e., NOT using fastmath) I get:

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

3.0 may be representable exactly in binary floating point math, but 6.15 - 3.15 isn't 3.0 in binary floating point math.


There have already been two references to the paper "What Every Computer Scientist Should Know About Floating-Point Arithmetic" which describes how binary floating point math works, and how it doesn't always fit with human expectations. The main point to remember is that you hardly ever want to compare two floating point numbers for equality, especially if one (or both) of those numbers is the result of a math operation.

In your case, though, you're trying to compare a double with an int, and I have to assume you want some rounding. You might want to consider 3.1 to be equivalent to 3. You might not. I really have no idea.

If you were to use the rounding conventions taught in elementary school (round up on .5 or higher), you might do something like:

#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';
}

There are much more complex possibilities, including one described in the paper.



回答2:

If you want field1 - field2 > param1 for param1 = 3, field1 = 6.15 and field2 = 3.15 then you will need to use infinite precision arithmetic.

Please read the paper on Floating-Point Arithmetic mentioned above.