8

I know this has been discussed time and time again, but I can't seem to get even the most simple example of a one-step division of doubles to result in the expected, unrounded outcome in C# - so I'm wondering if perhaps there's i.e. some compiler flag or something else strange I'm not thinking of. Consider this example:

double v1 = 0.7;
double v2 = 0.025;
double result = v1 / v2;

When I break after the last line and examine it in the VS debugger, the value of "result" is 27.999999999999996. I'm aware that I can resolve it by changing to "decimal," but that's not possible in the case of the surrounding program. Is it not strange that two low-precision doubles like this can't divide to the correct value of 28? Is the only solution really to Math.Round the result?

chaliasos
  • 9,659
  • 7
  • 50
  • 87
J23
  • 3,061
  • 6
  • 41
  • 52

6 Answers6

20

Is it not strange that two low-precision doubles like this can't divide to the correct value of 28?

No, not really. Neither 0.7 nor 0.025 can be exactly represented in the double type. The exact values involved are:

0.6999999999999999555910790149937383830547332763671875
0.025000000000000001387778780781445675529539585113525390625

Now are you surprised that the division doesn't give exactly 28? Garbage in, garbage out...

As you say, the right result to represent decimal numbers exactly is to use decimal. If the rest of your program is using the wrong type, that just means you need to work out which is higher: the cost of getting the wrong answer, or the cost of changing the whole program.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • OK where did you get the exact values from? I tried but couldn't work it out! – yamen May 04 '12 at 07:26
  • how did you know the "exact" values ? – CyprUS May 04 '12 at 07:26
  • 2
    @yamen @CyprUS: I have a little class called `DoubleConverter` which looks at the bit pattern to construct the decimal representation. See http://csharpindepth.com/Articles/General/FloatingPoint.aspx – Jon Skeet May 04 '12 at 07:29
  • 1
    @JonSkeet can you approximate how much of your SO reputation has been garnered specifically answering variations of this one question? Answer in double precision only please. – yamen May 04 '12 at 07:30
  • Great answer - thanks for looking more deeply into this. I guess it's Math.Round for me! :P – J23 May 04 '12 at 07:40
  • I like your cost note. @Metal450 - Math.Round will probably work for most situations, but you will probably get rounding errors at some point. If you are dealing with money here, people tend to get quite upset when you start losing pennies. – Paddy May 04 '12 at 09:51
6

Precision is always a problem, in case you are dealing with float or double.

Its a known issue in Computer Science and every programming language is affected by it. To minimize these sort of errors, which are mostly related to rounding, a complete field of Numerical Analysis is dedicated to it.

For instance, let take the following code.

What would you expect?

You will expect the answer to be 1, but this is not the case, you will get 0.9999907.

        float v = .001f;            
        float sum = 0;
        for (int i = 0; i < 1000; i++ )
        {
            sum += v;
        }
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
Asif Mushtaq
  • 13,010
  • 3
  • 33
  • 42
4

It has nothing to do with how 'simple' or 'small' the double numbers are. Strictly speaking, neither 0.7 or 0.025 may be stored as exactly those numbers in computer memory, so performing calculations on them may provide interesting results if you're after heavy precision.

So yes, use decimal or round.

yamen
  • 15,390
  • 3
  • 42
  • 52
4

To explain this by analogy:

Imagine that you are working in base 3. In base 3, 0.1 is (in decimal) 1/3, or 0.333333333'.

So you can EXACTLY represent 1/3 (decimal) in base 3, but you get rounding errors when trying to express it in decimal.

Well, you can get exactly the same thing with some decimal numbers: They can be exactly expressed in decimal, but they CAN'T be exactly expressed in binary; hence, you get rounding errors with them.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
2

Short answer to your first question: No, it's not strange. Floating-point numbers are discrete approximations of the real numbers, which means that rounding errors will propagate and scale when you do arithmetic operations.

Theres' a whole field of mathematics called numerical analyis that basically deal with how to minimize the errors when working with such approximations.

Christoffer
  • 12,712
  • 7
  • 37
  • 53
2

It's the usual floating point imprecision. Not every number can be represented as a double, and those minor representation inaccuracies add up. It's also a reason why you should not compare doubles to exact numbers. I just tested it, and result.ToString() showed 28 (maybe some kind of rounding happens in double.ToString()?). result == 28 returned false though. And (int)result returned 27. So you'll just need to expect imprecisions like that.

Botz3000
  • 39,020
  • 8
  • 103
  • 127
  • Yeah, I did try ToString() as well - but clearly it must be doing some internal rounding, as you also discovered... – J23 May 04 '12 at 07:42