5

Just started learning C#. I plan to use it for heavy math simulations, including numerical solving. The problem is I get precision loss when adding and subtracting double's, as well as when comparing. Code and what it returns (in comments) is below:

namespace ex3
{
    class Program
    {
        static void Main(string[] args)
        {

            double x = 1e-20, foo = 4.0;

            Console.WriteLine((x + foo)); // prints 4
            Console.WriteLine((x - foo)); // prints -4
            Console.WriteLine((x + foo)==foo); // prints True BUT THIS IS FALSE!!!
        }
    }
}

Would appreciate any help and clarifications!

What puzzles me is that (x + foo)==foo returns True.

halfer
  • 19,824
  • 17
  • 99
  • 186
Mikhail T.
  • 1,240
  • 12
  • 21
  • Keep in mind that comparing floating points using the `==` operator is not advised. Please refer to [this post](https://stackoverflow.com/questions/3874627/floating-point-comparison-functions-for-c-sharp) on how you should proceed. – marcelovca90 Aug 12 '16 at 13:06
  • 1
    @Marcelo Thank you! Could you post the link that's been in your answer (function to compare floats)? I find it very useful. – Mikhail T. Aug 12 '16 at 13:12
  • It is in my comment too. – marcelovca90 Aug 12 '16 at 13:14
  • Also look at the decimal type - smaller range but better precision than double : https://msdn.microsoft.com/en-us/library/364x0z75.aspx – PaulF Aug 12 '16 at 13:15

4 Answers4

4

Take a look at the MSDN reference for double: https://msdn.microsoft.com/en-AU/library/678hzkk9.aspx

It states that a double has a precision of 15 to 16 digits.

But the difference, in terms of digits, between 1e-20 and 4.0 is 20 digits. The mere act of trying to add or subtract 1e-20 to or from 4.0 simply means that the 1e-20 is lost because it cannot fit within the 15 to 16 digits of precision.

So, as far as double is concerned, 4.0 + 1e-20 == 4.0 and 4.0 - 1e-20 == 4.0.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • So why is `(x + foo) == foo` false in the OP's example? – xr280xr Feb 02 '18 at 23:20
  • @xr280xr - It's not. The OP says `// prints True BUT THIS IS FALSE!!!` meaning that the code `(x + foo) == foo` returns `True`, but mathematically it should be `False` – Enigmativity Feb 03 '18 at 02:26
2

Additional to Enigmativity's answer:

To make this works you need more precision, and that decimal with a precision of 28 to 29 digits and base of 10:

decimal x = 1e-20m, foo = 4.0m;
Console.WriteLine((x + foo)); // prints 4.00000000000000000001
Console.WriteLine((x - foo)); // prints -3.99999999999999999999 
Console.WriteLine((x + foo) == foo); // prints false.

But beware that it's true that decimal has bigger precision but has a lower range. see more about decimal here

Wael Alshabani
  • 1,495
  • 1
  • 15
  • 16
1

What you are looking for is probably the Decimal Structure (https://msdn.microsoft.com/en-us/library/system.decimal.aspx). Doubles can't right represent that kind of values with the precision you are looking for (C# double to decimal precision loss). Instead, try using the Decimal class, like so:

decimal x = 1e-20M, foo = 4.0M;

        Console.WriteLine(Decimal.Add(x, foo)); //prints 4,0000000000000000001
        Console.WriteLine(Decimal.Add(x, -foo)); //prints -3,9999999999999999999
        Console.WriteLine(Decimal.Add(x, foo) == foo); // prints false
Community
  • 1
  • 1
0

This is not a problem with C#, but with your computer. It's not really complicated or hard to understand, but it's a long read. You should read this article, if you want an actual in depth knowledge of how your computer works.

An excellent TLDR site that imho is a better intro to the subject than the aforementioned article is this one:

http://floating-point-gui.de/


I'll provide you with a very short explanation of what's going on, but you should certainly read at least that site, to avoid trouble in the future, since your field of application will require such knowledge in depth.

What happens is the following: you have 1e-20, which is a smaller number than 1.11e-16. That other number, is called the machine epsilon for double precision on your computer (most likely). If you add a number equal to or larger than 1, to something smaller than the machine epsilon, it will be rounded off, back to the large number. That's due to the IEEE 754 representation. This means that after the addition happens, the result, which is "correct" (as if you had infinite precision), the result is stored in a format of limited/finite precision, that rounds 4.00....001 to 4, because the error of the rounding is smaller than 1.11e-16, thus is considered acceptable.

RaidenF
  • 3,411
  • 4
  • 26
  • 42
  • thank you for your thourough exlanation as well as for your time. Wish it was possible to accept 2 answers. – Mikhail T. Aug 12 '16 at 17:08
  • This isn't at all anything to do with the computer per se. It's 100% to do with C# (and .NET) and the choice of floating-point number representation the framework designers chose. – Enigmativity Aug 13 '16 at 02:41
  • Also, you shouldn't provide answers in external links. You should put the content in the answer and only link to support what you've written. – Enigmativity Aug 13 '16 at 02:42
  • Every computer with 64 bit fp arithmetic behaves the same. It's not C#, it's the way IEEE 754 works. If they implemented doubles differently, they would effectively not be what every language calls double precision floating point number – RaidenF Aug 13 '16 at 09:56
  • And I did answer the question, just provided further reading as it's too long for a QA site, and essential imo. – RaidenF Aug 13 '16 at 09:58