I have a very simple code and I don't understad it's result.
I think, this have to print "OK" but does not!

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

class real
{
  double value;
  double error;
public:
  real(double,double);
  friend bool operator==(const real&,const double);
};

real::real(double val, double err) : value(val), error(fabs(err)) {}

bool operator==(const real& x1, const double x2)
{
  // after substituting: 0.1 <= 0.1 is true!
  return fabs(x2-x1.value) <= x1.error;
}

int main()
{
  real x(1.1,0.1);
  if (x == 1.0) std::cout << "OK" << std::endl; 
  return 0;
}

Any idea?

Dani AI

Generated

The surprising result is not a bug in fabs — it is the normal consequence of binary floating-point rounding. As noted, the literal 0.1 and 1.1 are not stored exactly as decimal 0.1 and 1.1. The subtraction 1.0 - 1.1 produces a tiny extra bit of error so the computed difference ends up just slightly larger than the stored error value. 's suggestion to step through and inspect the intermediate values is exactly the right first step.

To inspect values reliably, print doubles with full precision (about 17 significant digits for IEEE-754 double) so you can see those tiny differences. The mismatch happens before fabs runs; fabs only returns the absolute value of whatever slightly-inexact number the CPU produced.

For correctness, compare with a tolerance rather than exact equality. A robust pattern uses both an absolute and a relative tolerance. Example (new, not from the thread):

bool almost_equal(double a, double b,
                  double abs_tol = 1e-12,
                  double rel_tol = 1e-12)
{
    double diff = std::fabs(a - b);
    if (diff <= abs_tol) return true;
    return diff <= rel_tol * std::max(std::fabs(a), std::fabs(b));
}

Alternatives: use fixed-point (integers scaled by a known factor) when values are inherently decimal and fixed, or a decimal arithmetic library (Boost.Multiprecision or a dedicated decimal type) when exact decimal representation is required. One important caveat: overloading operator== to perform fuzzy comparisons can be surprising and break expectations (containers, hashing, strict equivalence). Prefer an explicit almost_equal or document the behavior clearly.

For background reading, see David Goldberg’s classic treatment of floating-point issues: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

Recommended Answers

All 6 Replies

x1.error is not 0.1 -- its 0.100000001.

bool operator==(const real& x1, const double x2)
{
  // after substituting: 0.1 <= 0.1 is true!
    bool b;
    double x = fabs(x2-x1.value);
    printf("x = %0.15f, x1.error = %0.25f\n", x, x1.error);
    if( x <= x1.error )
        b = true;
    else
        b = false;
    return b;
}

x = 0.100000000000000, x1.error = 0.1000000000000000100000000
FALSE

Press any key to continue . . .

x1.error is not 0.1 -- its 0.100000001.

why???
its contructor is error=err (=0.1) and everything is double!!!???

The value 0.1 can not be represented exactly as a double.

commented: Well said +17

Have you tried running your code through a debugger?

If you take the call to fabs out of your return statement in your operator== overload and store the result in a variable and then step through your code and take a look at the value, you'll see the problem.

Change your operator== overload to look like this:

bool operator==(const real& x1, const double x2)
{
	double x3  = fabs(x2 - x1.value);
	return x3 <= x1.error;
}

Step through the code and take a look at the values of x3 and x1.error.

Floating point mathematics strikes again!
This is down to the way that floating point values are handled in computer systems. In any computer system there are a finite number of numbers that can be accurately represented (depending on the architecture). Int values are quite straightforward and can be represented accurately, but floating point values are a bit trickier and are never exact, they are mere approximations. So not every possible decimal number can be accurately represented!

So although you've put 1.1 and 0.1 into your real class, these values are represented as something like 1.10000000000000001 and 0.100000000000000001.

fabs is giving you a result of 0.10000000000000009

Which isn't what you want when you're trying to compare one floating point number against another!

However, if you know how many decimal places you need to be accurate to (e.g. 4!), you could do something like this to compare the values:

bool operator==(const real& x1, const double x2)
{
	const int DECIMALSHIFT = 10000; // Shift 4 decimal places to the left

	// shift x1.error 4 decimal places to the left and store as an int:
	int z1 = (int)(x1.error * DECIMALSHIFT);
	// shift result of fabs by 4 decimal places and store as an int: 
	int z2 = (int)( (fabs(x2-x1.value)) * DECIMALSHIFT);

	// now compare the two values...
	return z2 < z1;
}

Which if you wanted, you could reduce down to this:

bool operator==(const real& x1, const double x2)
{
	const int DECIMALSHIFT = 10000;

	return (int)( (fabs(x2-x1.value)) * DECIMALSHIFT) <= (int)(x1.error * DECIMALSHIFT);
}

All we've done there is multiplied the floating point numbers by 10000 (shifting the numbers 4 decimal places to the left) and stored the results as ints (thus chopping off the unwanted additional decimal places).
If you want more or less decimal places, alter the number of zeros in DECIMALSHIFT!
Comparing the two resultant values then gives us our result (accurate to n decimal places, where n is the number of zeros in DECIMALSHIFT!)

Now the comparison in the return of your operator== function should work...Albeit accurate to n decimal places! :)

There are probably better solutions out there, but that was just off the top of my head!

Cheers for now,
Jas.

Thank you very much, but I don't understand something.
If I leave fabs away it works, so the problem is fabs,
however the absolute value function is a symmetric function mathematically,
and the double representation is also symmetric as I know
(which means tha the sign is stored separatelly in one bit)
so the fabs function has been written badly???

Sorry it does not work properly without fabs...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.