【问题标题】:How can I avoid rounding errors in this triangle area calculation?如何避免此三角形面积计算中的舍入误差?
【发布时间】:2016-09-21 23:30:19
【问题描述】:

我正在尝试计算带顶点的三角形的面积

{{0,1000000000},{1,0},{0,-1000000000}}

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

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

程序:

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

【问题讨论】:

  • 你有没有看过这个问题:“Java中float和double的包含范围是多少?”它可能会对您的问题有所了解。 stackoverflow.com/questions/1650505/…
  • 您的 areaShoelace() 即使数字较小,仍然会给出 0。

标签: java floating-point precision area


【解决方案1】:

Heron 公式不适用于极锐角或钝角三角形,因为至少有一个 p(p-x) 项将遭受灾难性取消。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      这里实际上有 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]))
      

      Heron 公式的数值问题已由浮点大师 William Kahan 本人充分确立和解释:Miscalculating Area and Angles of a Needle-like Triangle

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

      【讨论】:

      • @Sneftel 也有一个信息丰富的答案,但您的答案指出了代码中的缺陷。好眼力!
      猜你喜欢
      • 1970-01-01
      • 2011-01-09
      • 2023-03-24
      • 1970-01-01
      • 2017-11-21
      • 2015-07-29
      • 2010-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多