9

Why can't c# do any exact operations.

Math.Pow(Math.Sqrt(2.0),2) == 2.0000000000000004

I know how doubles work, I know where the rounding error is from, I know that it's almost the correct value, and I know that you can't store infinite numbers in a finite double. But why isn't there a way that c# can calculate it exactly, while my calculator can do it.

Edit

It's not about my calculator, I was just giving an example:

http://www.wolframalpha.com/input/?i=Sqrt%282.000000000000000000000000000000000000000000000000000000000000000000000000000000001%29%5E2

Cheers

Yaroslav Bulatov
  • 57,332
  • 22
  • 139
  • 197
Timo Willemsen
  • 8,717
  • 9
  • 51
  • 82
  • Obviously you didn't grasp it. Your calculator most likely doesn't use a Double for storing a number...have a look at the `Decimal` Type. – Bobby Jan 05 '11 at 12:03
  • 5
    @Bobby: Decimal would find it just as hard to represent irrational numbers as Double does... – Jon Skeet Jan 05 '11 at 12:06
  • A result within 10^-15 accuracy is a pretty exact result when dealing with doubles. – Alexandre C. Jan 05 '11 at 12:11
  • You should get exact result if you use single precision instead of double precision on that example, maybe that's what your calculator is doing – Yaroslav Bulatov Jan 05 '11 at 20:11

8 Answers8

8

Chances are your calculator can't do it exactly - but it's probably storing more information than it's displaying, so the error after squaring ends up outside the bounds of what's displayed. Either that, or its errors happen to cancel out in this case - but that's not the same as getting it exactly right in a deliberate way.

Another option is that the calculator is remembering the operations that resulted in the previous results, and applying algebra to cancel out the operations... that seems pretty unlikely though. .NET certainly won't try to do that - it will calculate the intermediate value (the root of two) and then square it.

If you think you can do any better, I suggest you try writing out the square root of two to (say) 50 decimal places, and then square it exactly. See whether you come out with exactly 2...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Okay, well my question is not really about my calculator, it's about c#. sqrt(2)^2 has an exact value, there must be ways a computer can get the exact value of it. – Timo Willemsen Jan 05 '11 at 12:06
  • @Timo: Yes - store the algebraic operations instead of computing intermediate values. That's what tools like Mathematica do... but that's not how any mainstream languages work as far as I'm aware. – Jon Skeet Jan 05 '11 at 12:09
  • @Timo Only if (as Jon already pointed out) the computer knows algebra, or in particular a Math.Pow() function that takes another function as argument and evaluates that... – Martin Hennings Jan 05 '11 at 12:09
  • I find that very strange, because well... a computer is just based on math, yet it can't solve simple mathematical functions exactly. – Timo Willemsen Jan 05 '11 at 12:10
  • @Timo Yes it can! That's what Mathematica is for after all. And by the way: A computer is based on the math of the discrete numbers 0 and 1. – Martin Hennings Jan 05 '11 at 12:12
  • 1
    @Timo: Please read: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems – Martin Thurau Jan 05 '11 at 12:13
  • Martin, I know about floating points inaccuracy, but what I find strange is that, why would you want the inaccuracy when other programming languages (Mathematica) cán calculate it exactly. – Timo Willemsen Jan 05 '11 at 12:14
  • @Timo "why would you want the inaccuracy when other programming languages (Mathematica) cán calculate it exactly" - I think you should rephrase your question like this, or ask it as a new question because there are a ton of other answers to that question. (Like, performance!) – Martin Hennings Jan 05 '11 at 12:16
  • Okay, i'll make a new question, because otherwise the responses to this one don't make sense anymore. – Timo Willemsen Jan 05 '11 at 12:17
  • 2
    Because floating point arithmetic is fast and simple and for most applications it's good enough. Yes, Mathematica can do it and yes it is fast. But it comes with the price of increased complexity. And since every programmer knows (or should know) it's not a problem: If this type of accuracy is needed there are tools that can do it. – Martin Thurau Jan 05 '11 at 12:18
  • @Timo: A computer is based on *arithmetic* rather than *algebra*. Computers are very good at performing specific arithmetic operations very quickly, but that's not the same as spotting that if you square a square root you get back to the original value. You might think of it as similar to the difference between being able to write on paper very quickly vs being able to compose a beautiful poem. Two people can both be good at "writing" in very different ways. – Jon Skeet Jan 05 '11 at 13:45
  • BTW, mantissa of single precision 2.0000000000000004 is 10, same as for 2.0 so in this case C# calculation is exact, it's really a display issue – Yaroslav Bulatov Jan 05 '11 at 18:45
  • @Yaroslav: But it's not using single precision, it's using double precision. There are no `float` values involved, only `double`. – Jon Skeet Jan 05 '11 at 19:16
  • I only checked for single precision, but I would guess it's also exact for double precision. The issue here is how binary `10.` should be printed in decimal – Yaroslav Bulatov Jan 05 '11 at 19:25
  • @Yaroslav: No it's not. Obviously 2.0000000000000004 is the same as 2 in single precision, because single precision only has 7 digits of precision - whereas double has 15 or 16. In short, it's *not* a display issue. – Jon Skeet Jan 05 '11 at 19:28
  • OK, I think you are right, the last bit of the significand of `2.000...4` is 1 – Yaroslav Bulatov Jan 05 '11 at 20:08
2

Your calculator is not calculating it exactly, it just that the rounding error is so small that it's not displayed.

Jackson Pope
  • 14,520
  • 6
  • 56
  • 80
1

I believe most calculators use binary-coded decimals, which is the equivalent of C#'s decimal type (and thus is entirely accurate). That is, each byte contains two digits of the number and maths is done via logarithms.

pdr
  • 6,372
  • 1
  • 29
  • 38
  • ... calculators (not calendars) ... logarithms (not logarhythms) ... In any case, BCD won't help with irrationals :-) – paxdiablo Jan 05 '11 at 12:10
  • 2
    Thanks for the corrections. A bit harsh downvoting for typos when other answers here are just kinda wrong. – pdr Jan 05 '11 at 12:35
0

What makes you think your calculator can do it? It's almost certainly displaying less digits than it calculates with and you'd get the 'correct' result if you printed out your 2.0000000000000004 with only five fractional digits (for example).

I think you'll probably find that it can't. When I do the square root of 2 and then multiply that by itself, I get 1.999999998.

The square root of 2 is one of those annoying irrational numbers like PI and therefore can't be represented with normal IEEE754 doubles or even decimal types. To represent it exactly, you need a system capable of symbolic math where the value is stored as "the square root of two" so that subsequent calculations can deliver correct results.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
0

The way calculators round up numbers vary from model to model. My TI Voyage 200 does algebra to simplify equations (among other things) but most calculators will display only a portion of the real value calculated, after applying a round function on the result. For example, you may find the square root of 2 and the calculator would store (let's say) 54 decimals, but will only display 12 rounded decimals. Thus when doing a square root of 2, then do a power of that result by 2 would return the same value since the result is rounded. In any case, unless the calculator can keep an infinite number of decimals, you'll always have a best approximate result from complexe operations.

By the way, try to represent 10.0 in binary and you'll realize that you can't represent it evenly and you'll end up with (something like) 10.00000000000..01

Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
0

Your calculator has methods which recognize and manipulate irrational input values.

For example: 2^(1/2) is likely not evaluated to a number in the calculator if you do not explicitly tell it to do so (as in the ti89/92).

Additionally, the calculator has logic it can use to manipulate them such as x^(1/2) * y^(1/2) = (x*y)^1/2 where it can then wash, rinse, repeat the method for working with irrational values.

If you were to give c# some method to do this, I suppose it could as well. After all, algebraic solvers such as mathematica are not magical.

Matthew
  • 10,244
  • 5
  • 49
  • 104
0

It has been mentioned before, but I think what you are looking for is a computer algebra system. Examples of these are Maxima and Mathematica, and they are designed solely to provide exact values to mathematical calculations, something not covered by the CPU.

The mathematical routines in languages like C# are designed for numerical calculations: it is expected that if you are doing calculations as a program you will have simplified it already, or you will only need a numerical result.

TheBoff
  • 11
  • 1
-1

2.0000000000000004 and 2. are both represented as 10. in single precision. In your case, using single precision for C# should give the exact answer

For your other example, Wolfram Alpha may use higher precision than machine precision for calculation. This adds a big performance penalty. For instance, in Mathematica, going to higher precision makes calculations about 300 times slower

k = 1000000;
vec1 = RandomReal[1, k];
vec2 = SetPrecision[vec1, 20];
AbsoluteTiming[vec1^2;]
AbsoluteTiming[vec2^2;]

It's 0.01 second vs 3 seconds on my machine

You can see the difference in results using single precision and double precision introduced by doing something like the following in Java

public class Bits {
    public static void main(String[] args) {
    double a1=2.0;
    float a2=(float)2.0;
    double b1=Math.pow(Math.sqrt(a1),2);
    float b2=(float)Math.pow(Math.sqrt(a2),2);
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(a1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(a2)));
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(b1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(b2)));
    }
}

You can see that single precision result is exact, whereas double precision is off by one bit

Yaroslav Bulatov
  • 57,332
  • 22
  • 139
  • 197
  • 2.0000000000000004 is *not* represented as the same number as 2 in double precision. Use `BitConverter.DoubleToInt64Bits` and you'll see that there are different values. Don't use one platform to find out the representation in a different one... – Jon Skeet Jan 05 '11 at 19:30
  • But the question was fundamentally about C# "doing it wrong" - and you didn't check the representation in .NET, which is what's important here. You just assumed - incorrectly - that single precision was relevant here, and elsewhere you assumed that the double precision representation would be the same. .NET *isn't* giving the exact answer, so it *isn't* a display issue. – Jon Skeet Jan 05 '11 at 19:47