【问题标题】:How to deal with precision in C#如何在 C# 中处理精度
【发布时间】:2014-05-20 00:35:52
【问题描述】:

在查找两点之间的积分点数时出现奇怪的精确行为:

我正在为此编写一个算法,如下所示(伪)。

给定 x1,y1 和 x2,y2

我计算了 double m 其中 m 是线段的梯度,给出为 (double)(y2-y1)/(x2-x1) 然后我计算了 double c 其中 c 是 y1 - (m*x1) 给出的 y 截距

那么 对于 i = Min(x1,x2) i

最后,返回结果-1

该代码适用于某些测试用例,但在其他测试用例上失败,例如当两个端点相互垂直时,我必须处理 m 的无穷大和 c 的 NaN 案例。但是,一个特殊情况引起了我的注意,分别为 x1,y1 和 x2,y2 的测试用例 43,38,17,6。

运行代码 j 从 6 开始,i 从 17 开始,所以这个点肯定在线段上,即使我不应该计算它,因为它是一个端点。奇怪的是这个值 i, j != (m*i)+c = 5.9999999999... 而不是 6。这怎么可能?我在哪里失去了精确度?更重要的是我怎么会失去精度?

代码:

        int cnt = 0;
        double i, j;
        double m = (double)(y2 - y1) / (x2 - x1);
        double c = y1 - (m * x1);
        for (i = Math.Min(x1, x2); i <= Math.Max(x1, x2); i++)
        {
            for (j = Math.Min(y1, y2); j <= Math.Max(y1, y2); j++)
            {
                if (j == (m * i) + c||double.IsInfinity(m) && double.IsNaN(c))
                    cnt++;
            }
        }
        return cnt - 2;

所以我将所有变量都更改为十进制,但不幸的是,我仍然得到失败的测试用例。但我想我已经把范围缩小到这里了:decimal m = (decimal)(y2 - y1) / (x2 - x1);

【问题讨论】:

  • 这里不再描述你的代码,而是展示它。在没有看到一行代码的情况下帮助您解决这样的问题是非常困难的。
  • 您使用的是doubles,但如果您关心计算过程中的精度,您可能应该使用decimals(又名Money)。

标签: c# algorithm coordinate-systems


【解决方案1】:

双打不能精确。它们只能精确到一定数量的数字。请记住,它们使用内部格式以字节为单位存储。这将不可避免地导致一些精度误差。

更糟糕的是,即使没有进行任何计算,您放入 double 中的某些值也无法精确存储。

让您了解的示例: 将1.94 分配给double 变量可以测试here 并将导致: 1.939999999999999946709294817992486059665679931640625!

这是不好的做法,注定无法用相等运算符比较两个浮点数。

关于浮点数的重要阅读: What Every Computer Scientist Should Know About Floating-Point Arithmetic

将无限多个实数压缩为有限位数 需要一个近似的表示。虽然有无限 许多整数,在大多数程序中整数计算的结果可以 以 32 位存储。相反,给定任何固定数量的比特, 大多数实数计算将产生的数量 无法准确表示使用这么多位。因此 浮点计算的结果通常必须按顺序四舍五入 以适应其有限的表示。 这个舍入误差是 浮点计算的特征。

作为解决方案,如果您真的想比较,您可以在比较之前用Math.round(x, decimals) 敏感地对结果进行四舍五入。

【讨论】:

    【解决方案2】:

    mcdoubles,所以(m*i)+c 将返回double。但是,jint。因此,您将整数与双精度数进行比较。鉴于floating point representation,在进行直接比较时,这将成为一个问题。您需要将该比较的右侧转换为整数,或者进行某种非精确比较。或者,您可以使用不是浮点精度的东西,例如 decimal,它不会显示此问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-19
      • 2019-05-22
      • 2018-08-27
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      相关资源
      最近更新 更多