C# double value precision is 42bit?

  • Thread starter Thread starter StefanG
  • Start date Start date
S

StefanG

With the test I've made on my WinXP/VS2005/C#2.0,
I get a precision of 42bit. Is this specified in C# or dependent on what?

double dLeft = 2183.23 - 695.37;
double dRight = 1487.86;
double dFraction = Math.Pow(2, -42);
if (Math.Abs(dLeft - dRight) == dFraction) {
System.Console.WriteLine("Success");
}
// Output: Success
 
With the test I've made on my WinXP/VS2005/C#2.0,
I get a precision of 42bit. Is this specified in C# or dependent on what?

double dLeft = 2183.23 - 695.37;
double dRight = 1487.86;
double dFraction = Math.Pow(2, -42);
if (Math.Abs(dLeft - dRight) == dFraction) {
System.Console.WriteLine("Success");
}
// Output: Success

It's not really clear to me why you expect your test to actually tell
you the precision of floating point numbers in C# or .NET. The
compiler is performing the arithmetic, rather than it being done at
execution time. I'm actually slightly disappointed that the two
numbers aren't identical. However, these are the exact numbers
represented dLeft, dRight, and dFraction:

dLeft:1487.86000000000012732925824820995330810546875
dRight:1487.859999999999899955582804977893829345703125

What you're seeing is the difference between the two being 2^-42 - but
bear in mind that they're of the order of 2^10 already - so you're
seeing around 52 bits of precision, but only 42 bits of that are after
the binary point.

Doubles in C# have a 52 bit mantissa and an 11 bit exponent (and a
sign bit). The exact precision of that depends on normalisation.
See http://pobox.com/~skeet/csharp/floatingpoint.html for more
information.

Jon
 
With the test I've made on my WinXP/VS2005/C#2.0,
I get a precision of 42bit. Is this specified in C# or dependent on what?

      double dLeft = 2183.23 - 695.37;
      double dRight = 1487.86;
      double dFraction = Math.Pow(2, -42);
      if (Math.Abs(dLeft - dRight) ==  dFraction) {
        System.Console.WriteLine("Success");
      }
     // Output: Success

What are you trying to test?
 
Jon said:
I'm actually slightly disappointed that the two
numbers aren't identical.

When you think of it, it would be more disappointing if they always
were. You want the result to be the same when the compiler does the
subtraction and when it's done at runtime:

double dLeft = 2183.23 - 695.37;
double dRight = 1487.86;

double dLeft2 = 2183.23;
dLeft2 -= 695.37;

You want dLeft and dLeft2 to be equal, rather than dLeft and dRight.
 
Göran Andersson said:
When you think of it, it would be more disappointing if they always
were. You want the result to be the same when the compiler does the
subtraction and when it's done at runtime:

double dLeft = 2183.23 - 695.37;
double dRight = 1487.86;

double dLeft2 = 2183.23;
dLeft2 -= 695.37;

You want dLeft and dLeft2 to be equal, rather than dLeft and dRight.

No - the compiler has more information available. It has the exact
decimal representation in the code, rather than the approximation to
doubles.

I'm sure I've seen this make a difference in the past, where the
compiler does the maths for the constant expression in a more precise
way than at runtime. I don't have an example to hand though.
 
Ignacio Machin ( .NET/ C# MVP ) said:
What are you trying to test?
In my unit tests, double.Epsilon does not work to compare double values, it
is too small. So I'm worried all my code is wrong. So I came up with the
example above.

In productive code it would be Math.Abs(d1-d2) <= double.Epsilon.

I've read about the double 52bit precision and read somewhere in the MSDN:
"[...] 15-17 digits precision [...]".
 
Jon Skeet said:
With the test I've made on my WinXP/VS2005/C#2.0,
I get a precision of 42bit. Is this specified in C# or dependent on what?

double dLeft = 2183.23 - 695.37;
double dRight = 1487.86;
double dFraction = Math.Pow(2, -42);
if (Math.Abs(dLeft - dRight) == dFraction) {
System.Console.WriteLine("Success");
}
// Output: Success

It's not really clear to me why you expect your test to actually tell
you the precision of floating point numbers in C# or .NET. The
compiler is performing the arithmetic, rather than it being done at
execution time. [...]

In my unit tests I'm having the problem that comparing double values with
double.Epsion fails. I came up with the example that could lead to the idea
the mantissa is 42bit, because the rounding error is exactly 2^-42.
Anyway, I was unaware that the compiler performs some arithmetic.
 
In my unit tests, double.Epsilon does not work to compare double values, it
is too small. So I'm worried all my code is wrong. So I came up with the
example above.

In productive code it would be Math.Abs(d1-d2) <= double.Epsilon.

That's very, very unlikely to produce a different result than if (d1 -
d2 == 0). The result of d1-d2 will only be double.Epsilon if both d1
and d2 are incredibly small.

What are you *really* trying to find out? If you want to know whether
their difference is within a certain tolerance, use that tolerance.
double.Epsilon is almost always inappropriate.
I've read about the double 52bit precision and read somewhere in the MSDN:
"[...] 15-17 digits precision [...]".

Yes, because you get 15-17 decimal digits of precision from 52 bits of
precision.

Jon
 
Jon said:
No - the compiler has more information available. It has the exact
decimal representation in the code, rather than the approximation to
doubles.

I'm sure I've seen this make a difference in the past, where the
compiler does the maths for the constant expression in a more precise
way than at runtime. I don't have an example to hand though.

Yes, the compiler has access to more precise information, but the point
is that it should not use it. As it's optimising away code by doing the
calculation at compile time instead of at runtime, the result should be
the same as it would be at runtime.

In this example it's rather obvious that the compiler will do the
calculation at compile time, but in other situations it may not be that
obvious, for example when one of the operands is a class member that may
or may not be cosntant. You have to be able to trust that the result
will be the same regardless if the calculation is done at compile time
or runtime.
 
Yes, the compiler has access to more precise information, but the point
is that it should not use it. As it's optimising away code by doing the
calculation at compile time instead of at runtime, the result should be
the same as it would be at runtime.

I've just checked the spec, and this is actually the specified
behaviour (7.18):

<quote>
The compile-time evaluation of constant expressions uses the same
rules as run-time evaluation of non-constant expressions, except that
where run-time evaluation would have thrown an exception, compile-time
evaluation causes a compile-time error to occur.
</quote>

I'm pretty sure I've seen this not being the case, however. Will try
to reproduce when I get the time.
In this example it's rather obvious that the compiler will do the
calculation at compile time, but in other situations it may not be that
obvious, for example when one of the operands is a class member that may
or may not be cosntant. You have to be able to trust that the result
will be the same regardless if the calculation is done at compile time
or runtime.

There are lots of places where you won't get the same results
depending on how the compilation is achieved. Even putting extra
statements *after* the calculation can affect the result of the
calculation, based on whether the JIT can just use an 80 bit register
for the result, etc. Just a caveat, really.

Jon
 
Jon Skeet said:
That's very, very unlikely to produce a different result than if (d1 -
d2 == 0). The result of d1-d2 will only be double.Epsilon if both d1
and d2 are incredibly small.

What are you *really* trying to find out? If you want to know whether
their difference is within a certain tolerance, use that tolerance.
double.Epsilon is almost always inappropriate.

Thank you for your answers, I've marked the question to answered and I think
this is now a new question and although I don't know the answer it is
probably answered already:
What is the tolerance of double.Parse(string)?

In my real program I've written a unit test for a function:

void Process(double armPos) {
if (armPos>100) {
// Do something
} else {
// do something else
}
}

The unit test takes another execution path than the program that takes the
value from the user interface.
I hope we agree that for floating point comparisons you must check the
tolerance and "armPos>100" is a comparison.

Maybe I was wrong to assume that double.Parse(string) has something to do
with the precision of the double type?
 
The unit test takes another execution path than the program that takes the
value from the user interface.
I hope we agree that for floating point comparisons you must check the
tolerance and "armPos>100" is a comparison.

I think I'd have to know *exactly* what you mean by tolerance in this
case. While equality comparisons should usually involve a certain
tolerance, for straight greater than / less than you don't usually
need to (although you can, of course).
Maybe I was wrong to assume that double.Parse(string) has something to do
with the precision of the double type?

Well, it's certainly related to the precision of double - but it's
also related to the current culture of the thread, and the rounding
applied as part of parsing.

What are the strings in question, in both your unit test and user
interface? If this is a number someone is typing in, is it definitely
appropriate for it to be a double rather than a decimal?

Jon
 
Jon Skeet said:
I think I'd have to know *exactly* what you mean by tolerance in this
case. While equality comparisons should usually involve a certain
tolerance, for straight greater than / less than you don't usually
need to (although you can, of course).

What a would be the smallest possible tolerance for my function with double
comparison in it if I expect that always the same execution path is taken,
regardless if the function is called in a unit test with “Process(100.0)†or
called from the real application using double.Parse(string)?
I guess the answer is that the tolerance is problem domain specific anyway,
I must choose a reasonable tolerance (e.g. 10^-10).
Well, it's certainly related to the precision of double - but it's
also related to the current culture of the thread, and the rounding
applied as part of parsing.

What are the strings in question, in both your unit test and user
interface? If this is a number someone is typing in, is it definitely
appropriate for it to be a double rather than a decimal?

Decimal would work, but it not an option for me.
 
What a would be the smallest possible tolerance for my function with double
comparison in it if I expect that always the same execution path is taken,
regardless if the function is called in a unit test with “Process(100.0)” or
called from the real application using double.Parse(string)?
I guess the answer is that the tolerance is problem domain specific anyway,
I must choose a reasonable tolerance (e.g. 10^-10).

So are you happy to treat the value as being > 100.0 even if it's
actually 99.9999999999? If so, that's fine. Otherwise, it needs
further thought.

In some cases, I'd expect double.Parse(string) to accept a string like
"99.999999999999999999999999999999999" and convert that to 100.0. I
wouldn't expect it to ever convert a number beginning "100.0" to a
value less than 100.0. Note that you're in the happy situation where
100.0 is exactly representable.

This would make it easy if your logic were using >= instead of > - but
I guess your tolerance here is really "how close to 100 can the value
be (from above) and still count as being greater than 100". Is that
fair to say?

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top