【问题标题】:Math.pow and Math.sqrt work differently for large values?Math.pow 和 Math.sqrt 对于大值的工作方式不同?
【发布时间】:2014-02-15 09:45:07
【问题描述】:

我正在使用 Heron 公式求三角形的面积。给定边 abcA = √(s(s-a)(s-b)(s-c)),其中 s 是半周长 (a+b+c)/2。这个公式应该可以完美运行,但我注意到 Math.pow() 和 Math.sqrt() 给出不同的结果。为什么会发生这种情况,我该如何解决? 我写了两个方法来查找区域并确定它是否为整数。

在第一种方法中,我取平方根,然后将它们相乘:

public static boolean isAreaIntegral(long a, long b, long c)
{
  double s = (a+b+c)/2.0;
  double area = Math.sqrt(s)*Math.sqrt(s-a)*Math.sqrt(s-b)*Math.sqrt(s-c);
  return area%1.0==0.0 && area > 0.0;
}

在第二种方法中,我找到产品然后取平方根:

public static boolean isAreaIntegral(long a, long b, long c)
{
  double s = (a+b+c)/2.0;
  double area = Math.pow(s*(s-a)*(s-b)*(s-c),0.5);
  return area%1.0==0.0 && area > 0.0;
}

谁能解释为什么这两种在数学上等价的方法给出不同的值?我正在处理Project Euler Problem 94。我的答案是999990060 第一种方式和996784416 第二种方式。 (我知道这两个答案都与实际相差甚远)

【问题讨论】:

  • abc的值是多少?
  • 你真的比较过sqrt(s*(s-a)*(s-b)*(s-c)pow(...,0.5)吗?无论如何,没有更简单的公式来计算2*(A/2*B)形式的面积...

标签: java math


【解决方案1】:

我肯定会投票支持“舍入问题”,因为您将第一个方法中的多个方法调用的结果(每个方法结果都被舍入)与第二个方法中的单个方法调用(您只舍入一次)相乘.

【讨论】:

    【解决方案2】:

    答案之间的差异比我预期的要大。或者也许不是。时间不早了,我的数学思维早就崩溃了。

    我认为您的问题在于四舍五入。当您将大量的根相乘时,您的答案会离真实值更远。

    第二种方法会更准确。 虽然,不一定像欧拉要求的那样准确。 计算器是不错的选择。

    【讨论】:

      【解决方案3】:

      这两种方法都有问题。在比较浮点值(也就是 double 精度浮​​点值)时,您通常应该非常小心。特别是,将计算结果==!= 进行比较几乎总是有问题的(而且往往只是错误)。比较“相等”的两个浮点值应该使用类似的方法

      private static boolean isEqual(double x, double y)
      {
        double epsilon = 1e-8;
        return Math.abs(x - y) <= epsilon * Math.abs(x);
        // see Knuth section 4.2.2 pages 217-218
      }    
      

      在这种情况下,浮点余数运算符也不会得到想要的结果。考虑以下经典示例

      public class PrecisionAgain
      {
          public static void main(String[] args)
          {
              double d = 0;
              for (int i=0; i<20; i++)
              {
                  d += 0.1;
              }
              System.out.println(d);
      
              double r = d%1.0;
              System.out.println(r);
          }
      }
      

      输出:

      2.0000000000000004
      4.440892098500626E-16
      

      在您的情况下,为了排除这些舍入错误,return 语句可能 (!) 类似

      return (area - Math.round(area) < 1e8);
      

      但在其他情况下,您绝对应该阅读有关浮点运算的更多信息。 (网站http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html 经常被推荐,但可能很难开始......)

      这仍然不能真正回答您的实际问题:为什么结果不同?毫无疑问,答案很简单:因为他们犯了不同的错误(但他们都犯了错误——实际上这在这里更重要!)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-09
        • 1970-01-01
        • 2020-10-02
        相关资源
        最近更新 更多