如何避免此三角区域计算中的舍入误差?

时间:2016-05-24 16:15:24

标签: java floating-point precision area

我正在尝试计算顶点

的三角形区域
{{0,1000000000},{1,0},{0,-1000000000}}

很容易看出这个三角形的面积应该是1,000,000,000,但是当我尝试使用Heron公式或Shoelace公式计算Java中的面积时,我得到该区域的0。

我很确定这是由于使用double时的舍入错误,但我不知道如何继续。有什么指针吗?

程序:

private static double areaShoelace(int[][] v) {
    return 0.5 * Math.abs(v[0][0]*v[1][1] + v[1][0]*v[2][1] + v[2][0]*v[0][1] +
            v[1][0]*v[0][1] + v[2][0]*v[1][1] + v[0][0]*v[2][1]);
}

private static double areaHeron(double a, double b, double c) {
    double p = (a + b + c) / 2.0d;
    return Math.sqrt(p * (p - a) * (p - b) * (p - c));
}

private static double length(int[] a, int [] b) {
    return Math.hypot(a[0] - b[0], a[1] - b[1]);
}

public static void main(String[] args) {
    int[][] tri = new int[][]{{0,1000000000},{1,0},{0,-1000000000}};
    System.out.println(areaShoelace(tri));
    System.out.println(areaHeron(length(tri[0], tri[1]), length(tri[1],tri[2]), length(tri[0],tri[2])));
}

输出:

0.0
0.0

3 个答案:

答案 0 :(得分:2)

苍鹭的公式不适合极端锐角或钝角三角形,因为p(p-x)项中至少有一个会遭受灾难性的取消。

更稳健的方法是计算三角形相对于最长边的高度,然后使用公式A=0.5*b*h。通过找到最靠近第三个顶点的基座上的位置并测量它到该顶点的距离来计算高度,而不是传统的交叉积(它本身会遭受灾难性的消除)。

答案 1 :(得分:2)

这里实际上有两种不同的错误。

shoelace formula实施中,某些迹象不正确(一半应为负数)。一旦你解决了这个问题,在这种情况下你应该得到正确的答案,但是你应该注意到乘法和加法是使用整数运算来执行的,它可能会溢出大数字。

如果将这些更改为浮点运算,将它们分组以减少运算次数和破坏性取消的可能性也是有意义的,我建议

0.5*Math.abs(v[0][0]*(v[1][1] - v[2][1]) + v[1][0]*(v[2][1] - v[0][1]) +
        v[2][0]*(v[0][1] - v[1][1]))

苍鹭公式的数值问题已得到很好的证实,并由浮点大师威廉卡汉解释:Miscalculating Area and Angles of a Needle-like Triangle

然而在这种情况下,你的问题甚至在此之前就出现了:Math.hypot(1, 1000000000)的结果在数值上等于1000000000(剩余的数字在浮点舍入中丢失),因此当它被输入到Heron时公式(即使精确计算),也会给出0。

答案 2 :(得分:1)

避免在Java中舍入错误的一种方法是使用java.math.BigDecimal而不是原始的双精度或浮点数。