查找两点之间的积分点数的奇怪精确行为:
我正在编写一个算法,就像这样(伪)。
给定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);
答案 0 :(得分:2)
m
和c
为double
,因此(m*i)+c
将返回double
。但j
是int
。所以你要将一个整数与一个整数进行比较。鉴于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)
对结果进行敏感处理。