0

I am working on floating point calculation by C# on Win7.

  double a, b;
  double diff = a -b;

because of floating point precision, sometimes a - b is a very small non-0 number even though a == b. but, sometimes, a and b are close but they are not equal. If I use

  if (abs(diff)/(abs(a) + abs(b)) < EPSILON)
      my_diff = 0;
  else 
      my_diff = sqrt(diff)

The problem is that how to set value for EPSILON ?

If it is too large, the case of diff is small but a != b cannot be handled correctly.

If it is too small, the case of diff is small but a == b cannot be handled correctly.

a and b should be equal but they are not because there are rounding precision problems. They are got in different ways and they should be equal but they have different precision.

Any help would be appreciated.

Ondrej Janacek
  • 12,486
  • 14
  • 59
  • 93
user2420472
  • 1,127
  • 1
  • 12
  • 20
  • 5
    I'm pretty sure that `a == b` implies `a - b == 0.0` unless someone is violaing IEEE 754 (assuming finite a and b). –  Feb 14 '14 at 16:03
  • 1
    @delnan, a and b should be equal but they are not because there are rounding precision problems. They are got in different ways and they should be equal but they have different precision. – user2420472 Feb 14 '14 at 16:06
  • They should be equal but different in computer due to precision. – user2420472 Feb 14 '14 at 16:09
  • 1
    Whether they should be equal or are equal is an important distinction. The latter generally means the computed values are equal down to the bit level. Believe it or not, that does happen and needs to be talked about :-) –  Feb 14 '14 at 16:11
  • 1
    You should first clearly state what you are trying to achieve, something like if |a - b| is larger than 1% of |a| I want to calculate the square root of |a - b|, otherwise I want to return 0. – Daniel Brückner Feb 14 '14 at 16:13
  • Have you tried [`double.Epsilon`](http://msdn.microsoft.com/en-us/library/system.double.epsilon(v=vs.110).aspx)? – D Stanley Feb 14 '14 at 16:15
  • No. If two floating-point numbers are equal, their difference is *exactly* zero. – tmyklebu Feb 14 '14 at 16:21
  • @DStanley That is the smallest possible `double` that > 0. While that's could be seen as a sort of "canonical epsilon", and it certainly has uses, it's not the best tolerance value for comparisons (because it's such a small tolerance window that you might as well compare exactly). Edit: See also the last paragraph of the remarks section at MSDN. –  Feb 14 '14 at 16:25

1 Answers1

2

The basic problem is:

  • Given two calculated values, a and b, that would be a and b if calculated exactly but that differ because of floating-point rounding, what can we determine about a and b regarding less than, equal to, and greater than?

Suppose we know the maximum possible total error in a and b is e. Then we can determine one of:

  • If ab < –e, then a is definitely less than b.
  • If ab > +e, then a is definitely greater than b.
  • Otherwise, it is impossible to tell.

There are two problems here. One, you have not given us any information that can be used to figure out what e is. It depends greatly on the calculations and values involved, and we do not know what those are. The accumulated error in multiple floating-point operations can range from zero to infinity or can be non-numeric (NaN). In order to know what value to use for your EPSILON, we must know the history of a and b.

(Alternatively, some people guess at e by running many test cases and seeing how much the values vary, or by performing other experiments.)

Two, if a and b are within the distance e from each other, then the only possible answer is “We are not sure.” It might be that a and b are equal, and you should set diff to zero. But, if they are not equal, is it okay if you set diff to zero? We do not know how that will affect your program. It is up to you to decide. Maybe, instead of accepting a and b as equal in this case, your program should recalculate a and b with better arithmetic, in the hope that it will be able to make an accurate determination.

In your particular case, perhaps you are mostly looking to avoid taking the square root of a negative number. If diff, calculated as a-b. represents some physical quantity that can never be negative in reality, then perhaps this code would work for you:

double diff = a-b;
if (diff <= 0)
    my_diff = 0;
else
    my_diff = sqrt(diff);

Notes

In IEEE 754 arithmetic, it is impossible for a-b to be non-zero when a == b. If you have observed a and b to have the same values but for a-b to be non-zero, then one of the following is true:

  • You observed displayed values of a and b that are not their true values (e.g., 15-digit displays, but the actual values differ at the 17th digit).
  • a-b and a == b were not calculated with IEEE 754 rules. E.g., extra precision was used in the former or not the latter or some process changed the value of one of them between the evaluations of the expressions. (Violating the IEEE 754 rules is not uncommon in Microsoft software. If you showed more of the code that produced a non-zero a-b and a true a == b, somebody might be able to help you find where the calculations go wrong.)

I used a simple error bound e. In theory, more complicated situations might arise in which the error bound might be larger on one side (favoring a > b) than the other (a < b), and it might be a function of a or of b or other values, or a combination.

Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312