如何处理C#的精度

时间:2014-04-07 16:28:10

标签: c# algorithm coordinate-systems

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

我正在编写一个算法,就像这样(伪)。

给定x1,y1和x2,y2

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

然后 对于i = Min(x1,x2)i&lt; j = Min(y1,y2)j的最大值(x1,x2)j <1。 Max(y1,y2)如果j =(m * i)+ c则为++

最后,返回结果-1

该代码适用于某些测试用例,但在其他测试用例中失败,例如当两个端点彼此垂直时,我必须处理m的无穷大,以及c的NaN情况。但是,一个特殊的案例引起了我的注意,分别为x1,y1和x2,y2测试案例为43,48,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;

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

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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

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

它的不良做法,注定无法将两个浮点数与相等运算符进行比较。

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

  

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

作为解决方案,如果您真的想要进行比较,则可以在比较结果之前使用Math.round(x, decimals)对结果进行敏感处理。