Double Precision Problems on .Net

Double precision problems on .NET

If you omit all the F postfixes (ie -12.1 instead of -12.1F) you will get equality to a few digits more. Your constants (and especially the expected values) are now floats because of the F. If you are doing that on purpose then please explain.

But for the rest i concur with the other answers on comparing double or float values for equality, it's just not reliable.

Double Precision

You can have precision, but it depends on what else you want to do. If you put the following in a Console application:

double a = 1e-20;
Console.WriteLine(" a = {0}", a);
Console.WriteLine("1+a = {0}", 1+a);

decimal b = 1e-20M;
Console.WriteLine(" b = {0}", b);
Console.WriteLine("1+b = {0}", 1+b);

You will get

 a  = 1E-20
1+a = 1
b = 0,00000000000000000001
1+b = 1,00000000000000000001

But Note that The Pow function, like almost everything in the Math class, only takes doubles:

double Pow(double x, double y);

So you cannot take the Sine of a decimal (other then by converting it to double)

Also see this question.

C# loss of precision when dividing doubles

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.

double minus double giving precision problems

Use the decimal data type. "The Decimal value type is appropriate for financial calculations requiring large numbers of significant integral and fractional digits and no round-off errors."

Incorrect values for double calculation

That's because of how double (or floating point numbers in general) is represented in memory. You should use decimal for financial calculations instead of double:

double total = 192.04;
double paid = 200;
double change = 7.96;

double result = (total - paid) + change; // -7.99360577730113E-15
decimal total = 192.04m;
decimal paid = 200m;
decimal change = 7.96m;

decimal result = (total - paid) + change; // 0.00

I know it's C#, but you should see the difference anyway.

You can use Decimal.Parse to get a decimal from string:

Dim total As String = "192.04"
Dim paid As String = "200"
Dim change As String = "7.96"

MsgBox((Decimal.Parse(total) - Decimal.Parse(paid)) + Decimal.Parse(change))

.net core 3 yields different floating point results from version 2.2

.NET Core introduced a lot of floating point parsing and formatting improvements in IEEE floating point compliance. One of them is IEEE 754-2008 formatting compliance.

Before .NET Core 3.0, ToString() internally limited precision to "just" 15 places, producing string that couldn't be parsed back to the original. The question's values differ by a single bit.

In both .NET 4.7 and .NET Core 3, the actual bytes remains the same. In both cases, calling

BitConverter.GetBytes(d*d*d)

Produces

85, 14, 45, 178, 157, 111, 27, 64

On the other hand, BitConverter.GetBytes(6.859) produces :

86, 14, 45, 178, 157, 111, 27, 64

Even in .NET Core 3, parsing "6.859" produces the second byte sequence :

BitConverter.GetBytes(double.Parse("6.859"))

This is a single bit difference. The old behavior produced a string that couldn't be parsed back to the original value

The difference is explained by this change :

ToString(), ToString("G"), and ToString("R") will now return the shortest roundtrippable string. This ensures that users end up with something that just works by default.

That's why we always need to specify a precision when dealing with floating point numbers. There were improvements in this case too :

For the "G" format specifier that takes a precision (e.g. G3), the precision specifier is now always respected. For double with precisions less than 15 (inclusive) and for float with precisions less than 6 (inclusive) this means you get the same string as before. For precisions greater than that, you will get up to that many significant digits

Using ToString("G15") produces 6.859 while ToString("G16") produces 6.858999999999999, which has 16 fractional digits.

That's a reminder that we always need to specify a precision when working with floating point numbers, whether it's comparing or formatting

Is floating-point math consistent in C#? Can it be?

I know of no way to way to make normal floating points deterministic in .net. The JITter is allowed to create code that behaves differently on different platforms(or between different versions of .net). So using normal floats in deterministic .net code is not possible.

The workarounds I considered:

  1. Implement FixedPoint32 in C#. While this is not too hard(I have a half finished implementation) the very small range of values makes it annoying to use. You have to be careful at all times so you neither overflow, nor lose too much precision. In the end I found this not easier than using integers directly.
  2. Implement FixedPoint64 in C#. I found this rather hard to do. For some operations intermediate integers of 128bit would be useful. But .net doesn't offer such a type.
  3. Implement a custom 32 bit floatingpoint. The lack of a BitScanReverse intrinsic causes a few annoyances when implementing this. But currently I think this is the most promising path.
  4. Use native code for the math operations. Incurs the overhead of a delegate call on every math operation.

I've just started a software implementation of 32 bit floating point math. It can do about 70million additions/multiplications per second on my 2.66GHz i3.
https://github.com/CodesInChaos/SoftFloat . Obviously it's still very incomplete and buggy.



Related Topics



Leave a reply



Submit