dhairyashil
dhairyashil

Reputation: 621

comparing equal calculation outputs for floating point numbers

The outputs of two calculations are supposed to be same as described below but even after taking machine precision into account, they come out to be unequal. What would be way around it to get them equal?

#include <iostream>
#include <limits>
#include <math.h>

bool definitelyGreaterThan(double a, double b, double epsilon)
{
  return (a - b) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
}

bool definitelyLessThan(double a, double b, double epsilon)
{
  return (b - a) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
}

int main ()
{
  double fig1, fig2;
  double m1 = 235.60242, m2 = 126.734781;
  double n1 = 4.2222, n2 = 2.1111;
  double p1 = 1.245, p2 = 2.394;
   fig1 = (m1/m2) * (n1/n2) - p1 * 6.0 / p2;

  m1 = 1.2*m1, m2 = 1.2*m2; // both scaled equally, numerator and denominator 
  n1 = 2.0*n1, n2 = 2.0*n2;
  p1 = 3.0*p1, p2 = 3.0*p2;

  fig2 = (m1/m2) * (n1/n2) - p1 * 6.0 / p2; // same as above

  double epsilon = std::numeric_limits<double>::epsilon();
  std::cout << "\n fig1 " << fig1 << " fig2 " << fig2 << " difference " << fig1 - fig2 << " epl " << epsilon;

  std::cout << "\n if(fig1 < fig2) " << definitelyLessThan(fig1, fig2, epsilon) 
          << "\n if(fig1 > fig2) " << definitelyGreaterThan(fig1, fig2, epsilon) << "\n";
}

with output as -

fig1 0.597738 fig2 0.597738 difference 8.88178e-16 epl 2.22045e-16
if(fig1 < fig2) 0
if(fig1 > fig2) 1

The difference between two numbers is greater than machine precision.

The key question is whether there is any universal method to deal with such aspects or solution has to be application dependent?

Upvotes: 1

Views: 57

Answers (1)

Bizzarrus
Bizzarrus

Reputation: 1329

There are two things to consider:

  • First, the possible rounding error (introduced by limited machine precision) scales with the number of operations that were used to calculate the value. For example, storing the result of m1/m2 might introduce some rounding error (depending on the actual value), and multiplying this value with something also multiplies that rounding error, and adds another possiblie rounding error on top of that (by storing that result).
  • Second, floating point values are not stored linearly (instead, they use an exponent-and-mantissa format): The bigger a values actually is, the bigger is the difference between that value and the next representable value (and therefore also the possible rounding error). std::numeric_limits<T>::epsilon() only states the difference between 1.0 and the next representable value, so if your value is not exactly 1.0, then this epsilon does not exactly represent the machine precision (meaning: The difference between this value and the next representable one) for that value.

So, to answer your question: The solution is to select an application-dependend, reasonable maximum rounding error that is allowed for two values to still be considered equal. Since this allowed rounding error depends both on expected values and number of operations (as well as what's acceptable for the application itself, of course), a truly universal solution is not possible.

Upvotes: 1

Related Questions