0

Given that I have two floats, a and b, and only care if they are "approximately equal", would something similar to the following work reliably, or would be it still be subject to precision issues?

eg:

Math.Round(a) == Math.Round(b)

Alternatively, is there a way to reliably round them to the nearest integer? If the above doesn't work, then I assume simply doing (int)Math.Round(a) won't be reliable either.

EDIT: I should have predicted I'd get answers like this, but I'm not trying to determine 'closeness' of the two values. Assuming the logic above is sound, will the above work, or is there a chance that I will get something like 3.0 == 3.0001?

Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • Perhaps related: http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be/6685966#6685966 – CodesInChaos Aug 17 '11 at 09:19
  • I don't really understand what you want to do/what your question is. – CodesInChaos Aug 17 '11 at 09:21
  • If all you care is they are approximate equal then would it be possible to compare the `Math.Abs` difference of the two to an tolerance of your choice – V4Vendetta Aug 17 '11 at 09:25
  • @CodeInChaos: Consider animating something across a form. You need to keep state in a form that accepts decimal places due to the speed your animation loop is likely to be executing, but at the end of the day, finding the current position on the form is expressed in whole pixels. For the purposes of this comparison, that final value is all that I'm worried about, but I have floats for bookkeeping. – Matthew Scharley Aug 17 '11 at 09:26
  • To round to the nearest int Math.Round should be the way to go (Note that by default Math.Round follows IEEE Standard 754, section 4, ie RoundToEven that is 2.5 is rounded to 2, 3.5 is rounded to 4). – Just another metaprogrammer Aug 17 '11 at 09:31

5 Answers5

3

Nothing of this kind will work. There is always a sharp boundary when very similar numbers will be rounded to different numbers.

In your first example think of a=0.4999999 and b=0.5000001. Very close but will round to different integers.

Another problem is that if the numbers are large rounding to an integer will have no effect at all. And even very close(relative) numbers will already an absolute difference >1.


If you care about determinism, you're out of luck with float and double. You just can't get those deterministic on .net. Use Decimal.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • For the purposes of this question I don't care about this. I need to store as a float because I need the extra precision during processing, but at the end all that matters is what the closest integer is. I'm not trying to measure the 'closeness' of the two values. – Matthew Scharley Aug 17 '11 at 09:17
3

IF you really want it reliable then you will have to use Decimal instead of float... although that is much slower...

EDIT:

With ((int)Math.Round(a)) == ((int)Math.Round(b)) you avoid the problem 3.0 == 3.0001 BUT all the other pitfalls mentioned your post and in the answers will still apply...

You can complicate the logic to try to make it a bit more reliable (example see below which could be packaged nicely into some method) but it will never be really reliable...

// 1 = near, 2 = nearer, 3 = even nearer, 4 = nearest

int HowNear = 0;

if (((int)Math.Round(a)) == ((int)Math.Round(b)))
   HowNear++;

if (((int)Math.Floor(a)) == ((int)Math.Floor(b)))
   HowNear++;

if (((int)Math.Ceiling(a)) == ((int)Math.Ceiling(b)))
   HowNear++;

if (Math.Round(a) == Math.Round(b))
   HowNear++;
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Seems an odd way of getting a number for how close two numbers are together. I'm really unclear on why you would use this method? – ForbesLindesay Aug 18 '11 at 11:34
  • this method is just some sort of "relative closeness classification" - as I understood the OP he doesn't want to measure the difference and the difference would itself be subject to the "float precision problem"... – Yahia Aug 18 '11 at 11:40
  • Yes, but surely it would be better to measure the difference and then divide that into arbitrary groups if you wanted things grouped into 4 categories of closeness (e.g. Math.Round(Math.Abs(a-b)*5)), which would give similar results to yours (except with 0 being closest and 5 being somewhat similar to a score of Near in your example) Doesn't that give a less arbitrary measure of closeness? – ForbesLindesay Aug 18 '11 at 12:03
1

The correct way to convert from double/float to integers is:

(int)Math.Round(a)

having done this you will always get whole numbers which can be tested for equality. If you have numbers which you know are going to be virtually whole numbers (e.g. the result of 6.0/3.0) then this will work great. The recommended way of checking two numbers which are doubles/floats are approximately equal is:

Math.Abs(a-b)<tolerance

where tolerance is a double value that determines how similar they should be, for example, if you want them to be within 1 unit of each other you could use a tolerance of 1.0 which would give you similar accuracy to Math.Round and comparing the results, but is well behaved when you get two values which are very close to half way between two integers.

ForbesLindesay
  • 10,482
  • 3
  • 47
  • 74
0

You could use Floor or Ceieling methods right to rounding of the values.

Zenwalker
  • 1,883
  • 1
  • 14
  • 27
0

"approximately equal" is your answer actually, a pseudoexample:

double tollerance = 0.03;

if(Math.Abs(a-b)<=tollerance ) 
   // these numbers are equal !
else 
  //non equal

EDIT

Or if you want to be more "precise":

int aint = (int)(a*100); // 100 is rounding tollerance
int bint = (int)(b*100); // 100 is rounding tollerance

and after

if(Math.Abs(aint -bint )<=tollerance ) // tollerance has to be integer in this case, obviously
   // these numbers are equal !
else 
  //non equal
Tigran
  • 61,654
  • 8
  • 86
  • 123