【问题标题】:NumberFormatException: Infinite or NaNNumberFormatException:无限或 NaN
【发布时间】:2013-08-04 09:45:23
【问题描述】:

我有一个方法,它接受 n 并返回第 n 个斐波那契数。在方法实现中,我使用 BigDecimal 获取第 n 个斐波那契数,然后我使用方法 toBigInteger() 将数字作为 BigInteger 对象获取,这肯定是因为我在我的应用程序中处理大量数字。

我一直得到正确的结果,直到我将 1475 作为我的方法的参数传递。在这种情况下,我得到NumberFormatException: Infinite or NaN,我没有任何明确的理由。

能否请您解释一下为什么会出现此异常?

这是我的方法:

BigInteger getFib(int n){
     double phi = (1 + Math.sqrt(5))/2;
     double squareRoot = (Math.sqrt(5)) + (1/2);
     BigDecimal bd = new BigDecimal(Math.floor(Math.pow(phi, n)/(squareRoot)));
     return bd.toBigInteger();
}

【问题讨论】:

    标签: java fibonacci numberformatexception


    【解决方案1】:

    您的Math.pow(phi, n) 太大(Infinity),double 无法存储它,请改用 BigDecimal。

    流动的怎么样:

    static BigInteger getFib(int n) {
        BigDecimal x1 = new BigDecimal((1 + Math.sqrt(5)) / 2);
        BigDecimal x2 = new BigDecimal((1 - Math.sqrt(5)) / 2);
        return x1.pow(n).subtract(x2.pow(n))
                .divide(new BigDecimal(Math.sqrt(5))).toBigInteger();
    }
    

    由公式:

    更新: 上面的方法是不正确的,因为 Math.sqrt(5) 正如评论所说的那样没有足够的精度。我尝试使用 Netown 的方法更精确地计算 sqrt(5),发现x1.pow(n).subtract(x2.pow(n)).divide(...) 非常耗时,在我的计算机中 n = 200 花了大约 30 秒。

    我认为使用缓存的递归方式更快:

        public static void main(String[] args) {
        long start = System.nanoTime();
        System.out.println(fib(2000));
        long end = System.nanoTime();
        System.out.println("elapsed:"+ (TimeUnit.NANOSECONDS.toMillis(end - start)) + " ms");
    }
    
    private static Map<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();
    
    public static BigInteger fib(int n) {
        BigInteger bi = cache.get(n);
        if (bi != null) {
            return bi;
        }
        if (n <= 1) {
            return BigInteger.valueOf(n);
        } else {
            bi = fib(n - 1).add(fib(n - 2));
            cache.put(n, bi);
            return bi;
        }
    }
    

    它在我的计算机上花费了 7 毫秒 n = 2000。

    【讨论】:

    • 80776376321566249506594310369598056472970340482296717528846843403451897402974064859772924316558310641200045712627734657615023748095728890842338729730058516953255187002299033438319847576705753443152494213260389820409161525095795264470887903665494432793647441708843372648309455220167976247625265489563686977653 跨度>
    • 由于Math.sqrt(5) 的精度有限,我不确定这会产生高于 71 的正确斐波那契数。根据OEIS oeis.org/A000045/b000045.txt的第一千四百七十五斐波纳契数为80776376321562253452154706580907152472160058839390611448949567098441231253468434896849142844747379890769208411232896054029055178267612427002841980625179761093160192801001760246411376424603323375022055947591818021711030476170155984070706514302882384774850491778697586460466725335267737494971685815687040886025。跨度>
    • 是的……从这个公式中得到一个准确的值是非常困难的。 1) BigDecimal 数字在小数点右侧具有固定精度。 2) 不能用Math.sqrt(5)……不够准确。
    • 令人印象深刻。我尝试了循环,但它太麻烦了,然后我给了递归一个短时间,随着数量的增加,它现在放慢了速度,一切都很好。缓存提高速度
    【解决方案2】:

    这不是 INF / NaN 的原因,但它绝对是错误的。这……

    double squareRoot = (Math.sqrt(5)) + (1/2);
    

    ...等价于这个...

    double squareRoot = Math.sqrt(5));
    

    ...因为(1/2)是整数除法,返回一个整数值;即零。


    事实上,我认为 INF / NaN 最可能的解释是“phi1475”太大而无法表示为double。所以pow 方法返回INF ...这就是Java中“太大”表示为浮点数的方式。


    如果您想以这种方式计算斐波那契数,您需要使用能够表示所涉及的真正大数的表示......并以足够的准确度表示它们。 Java double 类型无法做到这一点。事实上,使用BigDecimal 进行计算是很困难的......正如接受答案中的 cmets 所证明的那样!

    我建议使用递归关系。它会变得更简单......而且可能也会更有效。

    【讨论】:

    • 没错,“phi1475”给出了无穷大。我实际上可以改用 BigDecimal,它工作正常,但仍然存在一个问题。在这种情况下,BigDecimal 与 Math.floor 的等价物是什么?
    • 尝试roundFLOOR 的舍入模式。但我认为使用递归关系可能会更快、更可靠。
    【解决方案3】:

    使用 float 或 double 创建 BigDecimal 不是一个好主意,因为它再次限制了它们的范围 您必须首先创建一个 BigDecimal 并对其功能进行一些操作,例如:

    BigDecimal a;
    BigDecimal b;
    x1.pow(b);
    

    【讨论】:

    • BigDecimal 没有接受另一个 BigDecimal 的 pow 方法。实际上它只支持整数 (int) 值的幂。
    【解决方案4】:

    你的问题在这里:

    BigDecimal bd = new BigDecimal(Math.floor(Math.pow(phi, n)/(squareRoot)));
    

    Math.floor(Math.pow(phi, n)/(squareRoot)) 的结果是给你无限或 NaN。

    根据BigDecimal javadoc,如果您使用具有无限值或 NaN 的 double,构造函数 (BigDecimal(double)) 可能会抛出 NumberFormatException

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-24
      • 2014-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多