简体   繁体   English

划分双打时C#精度损失

[英]C# loss of precision when dividing doubles

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 ie some compiler flag or something else strange I'm not thinking of. 我知道这已经一次又一次地讨论了,但是我似乎无法得到一个一步一步的双重划分的最简单的例子,导致C#的预期的,不完整的结果 - 所以我想知道是否也许有一些编译器标志或其他奇怪的我没有想到的。 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. 当我在最后一行之后断开并在VS调试器中检查它时,“result”的值是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? 像这样的两个低精度双倍不能分成28的正确值是不奇怪的? Is the only solution really to Math.Round the result? 对Math.Round来说唯一的解决方案是结果吗?

Is it not strange that two low-precision doubles like this can't divide to the correct value of 28? 像这样的两个低精度双倍不能分成28的正确值是不奇怪的?

No, not really. 不,不是真的。 Neither 0.7 nor 0.025 can be exactly represented in the double type. double类型中,0.7和0.025都不能精确表示。 The exact values involved are: 涉及的确切价值是:

0.6999999999999999555910790149937383830547332763671875
0.025000000000000001387778780781445675529539585113525390625

Now are you surprised that the division doesn't give exactly 28? 现在你感到惊讶的是,该部门并没有给出28分? Garbage in, garbage out... 垃圾进垃圾出...

As you say, the right result to represent decimal numbers exactly is to use decimal . 如你所说,正确表示十进制数的正确结果是使用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. 如果你的程序的其余部分使用了错误的类型,那只意味着你需要解决更高的问题:获得错误答案的成本,或者更改整个程序的成本。

Precision is always a problem, in case you are dealing with float or double . 如果你正在处理floatdouble ,精度总是一个问题。

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 . 你会期望答案是1 ,但事实并非如此,你会得到0.9999907

        float v = .001f;            
        float sum = 0;
        for (int i = 0; i < 1000; i++ )
        {
            sum += v;
        }

It has nothing to do with how 'simple' or 'small' the double numbers are. 它与double数字的“简单”或“小”无关。 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. 严格地说, 0.70.025都不能存储为计算机内存中的那些数字,因此如果你的精确度很高,那么对它们进行计算可能会提供有趣的结果。

So yes, use decimal or round. 所以是的,使用decimal或圆形。

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'. 想象一下,你在基数3工作。在基数3中,0.1是(十进制)1/3,或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. 因此,您可以在基数3中精确表示1/3(十进制),但在尝试以十进制表示时会出现舍入错误。

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. 因此,你会得到舍入错误。

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. Theres是一个称为数值分析的整个数学领域,它基本上处理了在使用这种近似时如何最小化误差。

It's the usual floating point imprecision. 这是通常的浮点不精确。 Not every number can be represented as a double, and those minor representation inaccuracies add up. 并非每个数字都可以表示为double,并且这些次要表示不准确性会加起来。 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.ToString() double.ToString()显示28 (也许某种舍入发生在double.ToString() ?)。 result == 28 returned false though. result == 28虽然返回了false And (int)result returned 27 . 并且(int)result返回27 So you'll just need to expect imprecisions like that. 所以你只需要期待那样的不精确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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