How to correctly and standardly compare floats?

后端 未结 8 1301
夕颜
夕颜 2020-11-27 04:30

Every time I start a new project and when I need to compare some float or double variables I write the code like this one:

if (fabs(prev.min[i] - cur->min         


        
相关标签:
8条回答
  • 2020-11-27 04:37

    You should be aware that if you are comparing two floats for equality, you are intrinsically doing the wrong thing. Adding a slop factor to the comparison is not good enough.

    0 讨论(0)
  • 2020-11-27 04:38

    From The Floating-Point Guide:

    This is a bad way to do it because a fixed epsilon chosen because it “looks small” could actually be way too large when the numbers being compared are very small as well. The comparison would return “true” for numbers that are quite different. And when the numbers are very large, the epsilon could end up being smaller than the smallest rounding error, so that the comparison always returns “false”.

    The problem with the "magic number" here is not that it's hardcoded but that it's "magic": you didn't really have a reason for choosing 0.000001 over 0.000005 or 0.0000000000001, did you? Note that float can approximately represent the latter and still smaller values - it's just about 7 decimals of precision after the first nonzero digit!

    If you're going to use a fixed epsilon, you should really choose it according to the requirements of the particular piece of code where you use it. The alternative is to use a relative error margin (see link at the top for details) or, even better, or compare the floats as integers.

    0 讨论(0)
  • 2020-11-27 04:38

    Here is a c++11 implementation of @geotavros 's solution. It makes use of the new std::numeric_limits<T>::epsilon() function and the fact that std::fabs() and std::fmax() now have overloads for float, double and long float.

    template<typename T>
    static bool AreEqual(T f1, T f2) { 
      return (std::fabs(f1 - f2) <= std::numeric_limits<T>::epsilon() * std::fmax(std::fabs(f1), std::fabs(f2)));
    }
    
    0 讨论(0)
  • 2020-11-27 04:39

    You should use the standard define in float.h:

    #define DBL_EPSILON     2.2204460492503131e-016 /* smallest float value such that 1.0+DBL_EPSILON != 1.0 */
    

    or the numeric_limits class:

    // excerpt
    template<>
    class numeric_limits<float> : public _Num_float_base
    {
    public:
        typedef float T;
    
        // return minimum value
        static T (min)() throw();
    
        // return smallest effective increment from 1.0
        static T epsilon() throw();
    
        // return largest rounding error
        static T round_error() throw();
    
        // return minimum denormalized value
         static T denorm_min() throw();
    };
    

    [EDIT: Made it just a little bit more readable.]

    But in addition, it depends on what you're after.

    0 讨论(0)
  • 2020-11-27 04:41

    You can use std::nextafter for testing two double with the smallest epsilon on a value (or a factor of the smallest epsilon).

    bool nearly_equal(double a, double b)
    {
      return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
        && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
    }
    
    bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
    {
      double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
      double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;
    
      return min_a <= b && max_a >= b;
    }
    
    0 讨论(0)
  • 2020-11-27 04:44

    This post has a comprehensive explanation of how to compare floating point numbers: http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/

    Excerpt:

    • If you are comparing against zero, then relative epsilons and ULPs based comparisons are usually meaningless. You’ll need to use an absolute epsilon, whose value might be some small multiple of FLT_EPSILON and the inputs to your calculation. Maybe.
    • If you are comparing against a non-zero number then relative epsilons or ULPs based comparisons are probably what you want. You’ll probably want some small multiple of FLT_EPSILON for your relative epsilon, or some small number of ULPs. An absolute epsilon could be used if you knew exactly what number you were comparing against.
    0 讨论(0)
提交回复
热议问题