Component of the Week #21: bdlb_doublecompareutil

Summary:
  • Provides utilities for “fuzzy” comparison of double values.

The bdlb_doublecompareutil component provides a utility class, bdlb::DoubleCompareUtil, that performs “fuzzy” equality and relational operations on double values. Such fuzzy comparisons should often be used instead of the built-in equality and comparison operators, which frequently do not yield the expected result due to the approximate nature of floating-point arithmetic.

When performing calculations with floating-point numbers, small rounding errors and precision limitations can cause values that should mathematically be equal to have slightly different binary representations. This component allows you to determine if two double values are “close enough” to be considered equal for practical purposes.

The component defines two types of tolerances for comparing floating-point values: absolute tolerance and relative tolerance.

  1. Absolute Tolerance: The maximum absolute difference between two values that can still be considered equal.

  2. Relative Tolerance: The maximum relative difference (as a ratio compared to the magnitude of the values) that can still be considered equal.

Both absolute and relative tolerance are taken into account when two values are compared using bdlb::DoubleCompareUtil. For example, two values a and b are considered “fuzzy equal” if the difference between them falls within either the absolute or relative tolerance.

Here’s a simple example demonstrating how to use the component for comparing prices in a financial application:

#include <bdlb_doublecompareutil.h>
#include <bsl_iostream.h>

using namespace BloombergLP;

int main() {
    // Prices calculated through different formulas
    const double price1 = 100.0 / 3.0;
    const double price2 = 17.0 * (100.0 / 51.0);

    // Standard comparison would show these are different
    if (price1 == price2) {
        bsl::cout << "Prices are exactly equal (unlikely)\n";
    }
    else {
        bsl::cout << "Standard comparison: Prices differ\n";
    }

    // Using fuzzy comparison with default tolerances
    if (bdlb::DoubleCompareUtil::fuzzyEq(price1, price2)) {
        bsl::cout << "Fuzzy comparison: Prices are approximately equal\n";
    }

    // Using fuzzy comparison with custom tolerances (relative and absolute
    // tolerance of 1e-7)
    if (bdlb::DoubleCompareUtil::fuzzyEq(price1, price2, 1e-7, 1e-7)) {
        bsl::cout << "Custom fuzzy comparison: Prices are within "
                  << "acceptable range\n";
    }

    return 0;
}

The bdlb::DoubleCompareUtil class provides six main fuzzy comparison methods that correspond to the standard C++ comparison operators:

  • fuzzyEq(a, b, relTol, absTol): Fuzzy equality (similar to ==)

  • fuzzyNe(a, b, relTol, absTol): Fuzzy inequality (similar to !=)

  • fuzzyLt(a, b, relTol, absTol): Fuzzy less than (similar to <)

  • fuzzyLe(a, b, relTol, absTol): Fuzzy less than or equal (similar to <=)

  • fuzzyGt(a, b, relTol, absTol): Fuzzy greater than (similar to >)

  • fuzzyGe(a, b, relTol, absTol): Fuzzy greater than or equal (similar to >=)

Each function accepts two optional tolerance parameters:

  • relTol: The relative tolerance.

  • absTol: The absolute tolerance.

Both arguments can be omitted; it is also possible to specify relTol while omitting absTol. Omitted tolerances default to an unspecified reasonable value.

Note that NaNs are considered to be not fuzzy equal to any value (including another NaN), and negative zero is considered equal to zero per IEEE 754.

When comparing any floating-point values that are produced by arithmetic, using bdlb::DoubleCompareUtil helps prevent bugs caused by the inherent limitations of floating-point representation.

For more details, see: