0

Why float.CompareTo() using big numbers don't work as expected?

Test numbers:

float a = 1000000000f;
float b = 1000000001f;
float c = 1000000002f;
float d = 999999999f;

Results:

a.CompareTo(a) = 0
a.CompareTo(b) = 0
a.CompareTo(c) = 0
a.CompareTo(d) = 0

The documentation says that:

  • Less than zero: This instance precedes obj in the sort order.
  • Zero: This instance occurs in the same position in the sort order as obj.
  • Greater than zero: This instance follows obj in the sort order.

And as you can see the results says that a, b, c and d are the same.

But, if I use smaller numbers, like:

float a = 100000f;
float b = 100001f;
float c = 100002f;
float d = 99999f;

Results:

a.CompareTo(a) = 0
a.CompareTo(b) = -1
a.CompareTo(c) = -1
a.CompareTo(d) = 1

Comparing the numbers correctly.

I read Is floating point math broken? and I understand the precision errors when having decimal numbers. But why that big numbers without decimals have this unexpected results?

Context. Why using CompareTo?

I was working on some pathfinding algorithms like BFS, Dijkstra and A*, and I end up using IComparable in a class for my PriorityQueue, then while testing I got that errors. And while I solved all the errors in my algorithm, I want to know why that happened.

Edit

I test printing the value as @GSerg suggested, and I got more questions. a.ToString("N0"), b.ToString("N0"), c.ToString("N0"), d.ToString("N0"), in the first scenario all four variables print: "1,000,000,000". And in the second scenario prints the correct smaller numbers.

Answer

As I understand, the reason of this behaviour is because a large float number is stored in a "scientific notation" like style, making them to lose precision with their decimals. And Is floating point math broken? explains that.

Marco Elizondo
  • 552
  • 3
  • 9
  • 3
    Possible duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – GSerg May 31 '20 at 21:34
  • 4
    Before you proceed to compare your big floats, print them out. `Console.WriteLine(a.ToString("N0")); Console.WriteLine(b.ToString("N0")); Console.WriteLine(c.ToString("N0")); Console.WriteLine(d.ToString("N0"));`. See https://stackoverflow.com/a/14839748/11683 and https://stackoverflow.com/a/618542/11683. – GSerg May 31 '20 at 21:35
  • All four prints "1,000,000,000" hmm... – Marco Elizondo May 31 '20 at 21:39
  • `Btw, using Equals() gives the true/false value correctly even for the big numbers` - please show the code. It could be happening with expressions, but not with float variables. – GSerg May 31 '20 at 21:56
  • Sorry my bad, I continue testing and I din't use that big numbers when Equals was correct. It also gives true on big numbers. But I know understand what is happening. I am writting a response on Christian's answer. Thank you @GSerg, your comment opened my eyes. – Marco Elizondo May 31 '20 at 22:04

1 Answers1

0

This is because a float can represent integer values from −16,777,216 to 16,777,216. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limitations_on_integer_values

When using double you get expected results.

double a = 1000000000;
double b = 1000000001;
double c = 1000000002;
double d = 999999999;
Christian
  • 11
  • 3
  • Thank you. Your answer with @GSerg comment opened my eyes. Your answer is correct, but I want to complete it with more of the "why". Larger numbers are stored in "scientific notation" like style, thats why they lose precision and comparing them have its issues as "Is floating point math broken?" question answer. – Marco Elizondo May 31 '20 at 22:10
  • 3
    `float` can represent numbers from −∞ to +∞ and finite numbers from about −3.4e38 to +3.4e38. −16,777,216 to +16,777,216 is the interval throughout which it can represent all integers. – Eric Postpischil May 31 '20 at 23:02