Math.pow和Math.sqrt对大值的工作方式不同?

时间:2014-02-15 00:44:49

标签: java math

我正在使用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是第二种方式。 (我知道这两个答案都与实际情况相差甚远)

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

答案之间的差异大于我的预期。或许它不是。现在已经很晚了,我的数学思想也在不久前崩溃了。

我认为你的问题在于四舍五入。当你将一堆根连在一起时,你的答案会比真实值更进一步。

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

答案 2 :(得分:0)

这两种方法都存在问题。在比较浮点值(也就是 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经常被推荐,但可能很难开始......)

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