3

I wrote this code that allows me to check when two doubles are almost equal :

bool are_equal(double x, double y, double abs_tol=1e-15, double rel_tol=1e-15){ 
    double diff(std::abs(x-y));
    x = std::abs(x);
    y = std::abs(y);
    x = (x>y)?x:y;
    return (diff<rel_tol*x) || (diff<abs_tol);  /*has been updated*/
}

I'd like to know if it's a safe test and what are the "smart" values for abs_tol and rel_tol. I'd like to fix thoses values so that it works for very small and very big double.

edit Here is the link I took my inspiration from...

PinkFloyd
  • 2,103
  • 4
  • 28
  • 47
  • Default arguments, namespaces and some other things you use doesn't exist in C, so I changed the language tag to C++. – Some programmer dude Jul 25 '14 at 09:43
  • 1
    possible duplicate of [Most effective way for float and double comparison](http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison) – Emilien Jul 25 '14 at 09:44
  • 2
    You can probably return out with false early if the difference is >= abs_tol thus avoiding some operations. – Abhishek Bansal Jul 25 '14 at 09:49
  • 2
    Testing floating point values for equality is almost always an error. – n. m. could be an AI Jul 25 '14 at 09:50
  • @Emilien I read this post, but I have the feeling that my code is different because it checks two things... and it's 4 years old, so things may have changed – PinkFloyd Jul 25 '14 at 09:59
  • May be better to rename the function to `is_equal` in the sense that one number **is equal** to another number and 2 numbers **are equal** to third number or another value. This is only minor though and may even be opinion based. Sorry if you think I am annoying. – patrik Jul 25 '14 at 11:11
  • @patrik it's always interesting to have some feedback... – PinkFloyd Jul 25 '14 at 11:41
  • actually 10^15 quite well value for absolute tolerance because of its already precision of double and as relational tolerance can be (when y=(x – oknsnl Jul 25 '14 at 13:34

1 Answers1

1

It is unnecessarily inefficient.

You don't need the larger of abs (x), abs (y); picking one of them will do just fine.

Either x and y are very close together (almost equal), then it doesn't matter whether you compare with abs (x) or abs (y). Or they are not close together, then diff is large, and it doesn't matter whether you compare with abs (x) or abs (y).

Instead of using a ternary operator at the end, you could just write

return (diff < del_tol * x) && (diff < abs_tol);

Now it is obviously more efficient to do the second check first because it doesn't require any extra calculations.

double diff = std::abs (x - y);
return (diff < abs_tol) && (diff < del_tol * std::abs (x)); 

But finally the whole comparison method looks very suspicious. If your numbers are only slightly large, then diff < abs_tol will imply that x and y must be equal, so the whole code is just an absurdly complicated check for equality.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • Somehow it feels as if it should be the relative error that matters here. I mean `abs_tol == 0.00001` is just silly if the numbers are of magnitude 10^9. What I mean is that is it not simply enough (or even better) to only check for relative tolerance? – patrik Jul 25 '14 at 10:24
  • Ok I see. I can agree with that. However, there are situations where it can be useful with only a relative comparison and how it can be done is shown in a comment under the aritcle you linked to. This can be donr by `if (abs_tol == NULL) {myAbsTolVar = true;}` – patrik Jul 25 '14 at 11:05