简体   繁体   中英

simple calculation, different results in c# and delphi

The question is, why do these code snippets give different results?

private void InitializeOther()
{
  double d1, d2, d3;
  int i1;

  d1 = 4.271343859532459e+18;
  d2 = 4621333065.0;
  i1 = 5;

  d3 = (i1 * d1) - Utils.Sqr(d2);
  MessageBox.Show(d3.ToString());
}

and

procedure TForm1.InitializeOther;
var d1, d2, d3 : Double;
    i1 : Integer;
begin
    d1:=4.271343859532459e+18;
    d2:=4621333065.0;
    i1:=5;

    d3:=i1*d1-Sqr(d2);
    ShowMessage(FloatToStr(d3));
end;

The Delphi code gives me 816, while the c# code gives me 0. Using a calculator, I get 775. Can anybody please give me a detailed explanation?

Many thanks!

Delphi stores intermediate values as Extended (an 80-bit floating point type). This expression is Extended:

i1*d1-Sqr(d2);

The same may not be true of C# (I don't know). The extra precision could be making a difference.

Note that you're at the limits of the precision of the Double data type here, which means that calculations here won't be accurate.

Example:

d1 = 4.271343859532459e+18

which can be said to be the same as:

d1 = 4271343859532459000

and so:

d1 * i1 = 21356719297662295000

in reality, the value in .NET will be more like this:

2.1356719297662296E+19

Note the rounding there. Hence, at this level, you're not getting the right answers.

This is certainly not an explanation of this exact situation but it will help to explain the problem.

What Every Computer Scientist Should Know About Floating-Point Arithmetic

AC# double has at most 16 digits of precision. Taking 4.271343859532459e+18 and multiply by 5 will give a number of 19 digits. You want to have a number with only 3 digits as a result. Double cannot do this.

In C#, the Decimal type can handle this example -- if you know to use the 123M format to initialize the Decimal values.

    Decimal d1, d2, d3; 
    int i1; 
    d1 = 4.271343859532459e+18M;
    d2 = 4621333065.0M;
    i1 = 5; 
    d3 = (i1 * d1) - (d2*d2); 

    MessageBox.Show(d3.ToString());

This gives 775.00 which is the correct answer.

Any calculation such as this is going to lead to dramas with typical floating point arithmetic. The larger the difference in scaling of the numbers, the bigger the chance of an accuracy problem.

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems gives a good overview.

I think that this is an error caused by the limited precision (Above all, because using doubles instead of integers). Perhaps d1 isn't the same after the assignment. d2*d2 will surely be different than the correct value as it's bigger than 2^32.

As 5*d1 is even bigger than 2^64, even using 64-bit integers won't help. You'd have to use bignums or a 128-bit integer class to get the correct result.

Basically, as other people have pointed out, double-precision isn't precise enough for the scale of the computation you're trying to do. Delphi uses "extended precision" by default, which adds another 16 bits over Double to allow for more precise computation. The .NET framework doesn't have an extended-precision data type.

Not sure what type your calculator is using, but it's apparently doing something different from both Delphi and C#.

As commented by the others, the double isn't precise enough for your computation. The decimal is a good alternative eventhough someone pointed out that it would be rounded it is not.

In C#, the Decimal type cannot handle this example easily either since 4.271343859532459e+18 will be rounded to 4271343859532460000.

This is not the case. The answer if you use decimal will be correct. But as he said the range is different.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM