2012-05-04 86 views
3

我知道这个问题已经被一次又一次地讨论过了,但是我似乎甚至没有得到一个单步分裂的最简单的例子,它会导致预期的,没有根据的结果在C#中 - 所以我想知道如果也许有一些编译器标志或其他奇怪的东西我没有想到。考虑下面这个例子:C#双精度分割时丢失精度

double v1 = 0.7; 
double v2 = 0.025; 
double result = v1/v2; 

当我最后一个行后断裂,在VS调试器检查它,“结果”的值是27.999999999999996。我知道我可以通过更改为“decimal”来解决它,但对于周围的程序来说这是不可能的。像这样的两个低精度双打不能分为28的正确值,这不奇怪吗? Math.Round的唯一解决方案是真的吗?

+0

查看“相关”侧边栏中的任何项目,如下所示:http://stackoverflow.com/q/4252917/121309 –

+1

http://docs.oracle.com/cd/E19957-01/806 -3568/ncg_goldberg.html必须阅读 – Habib

回答

16

像这样的两个低精度双打不能分为正确的28值是不是很奇怪?

不,不是真的。在double类型中,0.7和0.025都不能完全表示。涉及的精确值:

0.6999999999999999555910790149937383830547332763671875 
0.025000000000000001387778780781445675529539585113525390625 

现在你感到惊讶的是,分工不给确切28?垃圾进,垃圾出...

正如你所说的,正确的结果来表示十进制的数字恰好是使用decimal。如果程序的其余部分使用的是错误的类型,那意味着您需要确定哪一个更高:获得错误答案的成本或更改整个程序的成本。

+0

确定您从哪里得到确切的值?我尝试过,但无法解决它! – yamen

+0

你怎么知道“确切”的值? – CyprUS

+1

@yamen @CyprUS:我有一个名为'DoubleConverter'的小类,它查看位模式来构造十进制表示。见http://csharpindepth.com/Articles/General/FloatingPoint.aspx –

4

它与'简单'或'小'double数字是无关的。严格地说,0.70.025都不会被存储为计算机内存中的那些数字,因此如果您执行严格的精确度后,对它们执行计算可能会提供有趣的结果。

所以是的,使用decimal或一轮。

2

简短回答你的第一个问题:不,这并不奇怪。浮点数是实数的离散近似值,这意味着舍入误差会在进行算术运算时传播和缩放。

Theres'数学分析的一个整体领域,基本上处理如何使用这种近似值处理时最小化错误。

2

这是通常的浮点不精确。并非每个数字都可以表示为双重数字,并且这些次要表示的不准确性会加起来。这也是你不应该将双打与确切数字进行比较的原因。我刚刚测试过,result.ToString()显示28(也许某种舍入发生在double.ToString()?)。 result == 28虽然返回false。并且(int)result返回27。所以你只需要期待这样的不精确。

+0

是的,我也尝试过ToString()以及 - 但显然它必须做一些内部舍入,因为你也发现了... – Metal450

4

如果您正在处理floatdouble,精度始终是个问题。

它是计算机科学和每种编程语言中的一个已知问题,它受其影响。为了尽量减少这些与舍入相关的错误,一个完整的Numerical Analysis字段专用于它。

例如,让我们拿下面的代码。

你会期待什么?

你会预期的答案是1,但事实并非如此,你会得到0.9999907

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

为了通过类推说明这一点:

假设正在基座3工作在基体3,是0.1(十进制)1/3,或0.333333333' 。

因此,您可以在基数3中正确表示1/3(十进制),但在尝试用十进制表示它时会发生舍入误差。那么,你可以用一些十进制数得到完全一样的东西:它们可以精确地用十进制表示,但它们不能用二进制精确表示;因此,你会与他们发生四舍五入的错误。

+0

优秀的解释 - 谢谢:) – Metal450