0

I need to write a float comparison function (equal/not equal) but I have to use C++98 and boost libraries at most. I know that float comparison should include epsilon but I don't know how to write such code without using C++11.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
EagleOne
  • 541
  • 1
  • 10
  • 28
  • Here's an answer for Python, wouldn't take much to translate: https://stackoverflow.com/a/33024979/5987 – Mark Ransom Nov 13 '19 at 18:02
  • [`std::numeric_limits::epsilon()`](https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon) is in C++98. Also, here's a [language-agnostic version of this question](https://stackoverflow.com/questions/4915462/how-should-i-do-floating-point-comparison) that has multiple answers that should translate to C++98. – Romen Nov 13 '19 at 18:31
  • "_I know that float comparison should include epsilon_" why? – curiousguy Nov 16 '19 at 06:47

2 Answers2

1

One C++98 example:

#include <cmath>
#include <limits>
#include <iostream>

inline bool equal_with_tolerance(float a, float b, float tolerance = std::numeric_limits<float>::epsilon()) {
    return std::abs(a - b) < tolerance;
}

int main() {
    float a = 0.1f;
    float b = 0.1000001f;
    std::cout << (a == b) << '\n';                    // Outputs 0.
    std::cout << equal_with_tolerance(a, b) << '\n';  // Outputs 1.
}

tolerance depends on your problem domain, using std::numeric_limits<float>::epsilon is rarely adequate, see this for more details.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 1
    I would go as far as saying that using `std::numeric_limits::epsilon()` is [*rarely*](https://floating-point-gui.de/errors/comparison/) adequate as a threshold for your given function. – Romen Nov 13 '19 at 18:36
1

I know that float comparison should include epsilon but I don't know how

You could use std::numeric_limits<float>::epsilon() to get the "machine" epsilon.

However, floating point equality comparison with tolerance is not quite as simple as directly comparing absolute difference to machine epsilon. Any small epsilon is going to devolve the comparison into an equality comparison for large values, which leaves you with zero error tolerance.

Meaningful tolerant comparison, requires that you know what sort of values you're expecting, their magnitude, their sign, expected error that you wish to tolerate.

This blog explains the problem in intricate detail. It suggests following, which may be reasonable for "generic" comparison

bool AlmostEqualRelativeAndAbs(float A, float B,
            float maxDiff, float maxRelDiff = FLT_EPSILON)
{
    // Check if the numbers are really close -- needed
    // when comparing numbers near zero.
    float diff = fabs(A - B);
    if (diff <= maxDiff)
        return true;

    A = fabs(A);
    B = fabs(B);
    float largest = (B > A) ? B : A;

    if (diff <= largest * maxRelDiff)
        return true;
    return false;
}

The example is in C, but trivial to translate to C++ idioms. There is also an ULP based function in the article, but its implementation relies on union type punning that is not allowed in C++.

eerorika
  • 232,697
  • 12
  • 197
  • 326