【问题标题】:C# loss of precision when dividing doublesC# 除法双精度时丢失精度
【发布时间】:2012-05-13 17:36:33
【问题描述】:

我知道这已经被讨论过一次又一次,但我似乎无法得到一个最简单的例子,即一步除法的双打结果在 C# 中产生预期的、不四舍五入的结果——所以我想知道是否可能有一些编译器标志或其他我没有想到的奇怪的东西。考虑这个例子:

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

当我在最后一行之后中断并在 VS 调试器中检查它时,“结果”的值为 27.999999999999996。我知道我可以通过更改为“十进制”来解决它,但在周围程序的情况下这是不可能的。像这样的两个低精度双精度数不能除到正确的值 28 是不是很奇怪? Math.Round 结果真的是唯一的解决方案吗?

【问题讨论】:

标签: c# double precision


【解决方案1】:

像这样的两个低精度双精度数不能除以正确的值 28 是不是很奇怪?

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

0.6999999999999999555910790149937383830547332763671875
0.025000000000000001387778780781445675529539585113525390625

现在你对除法没有准确地给出 28 感到惊讶吗?垃圾进,垃圾出……

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

【讨论】:

  • 好的,你从哪里得到确切的值?我试过了,还是不行!
  • 你是怎么知道“确切”值的?
  • @yamen @CyprUS:我有一个名为DoubleConverter 的小类,它查看位模式以构造十进制表示。见csharpindepth.com/Articles/General/FloatingPoint.aspx
  • @JonSkeet 您能否估算一下您在专门回答这一问题的变体时获得了多少 SO 声誉?请仅以双精度回答。
  • 很好的答案 - 感谢您更深入地研究这个问题。我想这对我来说是 Math.Round! :P
【解决方案2】:

这与double 数字的“简单”或“小”无关。严格来说,0.70.025 都不能完全按照这些数字存储在计算机内存中,因此如果您追求高精确度,对它们执行计算可能会提供有趣的结果。

所以是的,使用decimal 或圆形。

【讨论】:

    【解决方案3】:

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

    有一个称为数值分析的完整数学领域,主要处理在使用此类近似值时如何最小化错误。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 是的,我也尝试过 ToString() - 但显然它必须进行一些内部舍入,正如您还发现的那样......
      【解决方案5】:

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

      这是计算机科学中的一个已知问题,每种编程语言都会受到它的影响。为了最大限度地减少这些主要与舍入相关的错误,Numerical Analysis 的完整字段专用于它。

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

      你会期待什么?

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

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

      【讨论】:

        【解决方案6】:

        以此类推:

        假设您在以 3 为基数工作。在以 3 为基数的情况下,0.1 是(十进制)1/3,或 0.333333333'。

        因此您可以精确地表示以 3 为底的 1/3(十进制),但在尝试以十进制表示时会出现舍入错误。

        嗯,你可以用一些十进制数得到完全相同的东西:它们可以用十进制精确表示,但不能用二进制精确表示;因此,您会遇到舍入错误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-17
          • 2010-10-16
          • 1970-01-01
          • 2016-12-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多