Java中BigDecimal的平方根

use*_*200 55 java bigdecimal square-root

我们是否可以BigDecimal仅使用Java API而不是定制的100行算法来计算Java中的平方根?

hav*_*ked 31

我用过这个,效果很好. 这是算法如何在高级别工作的示例.

编辑:我很想知道下面定义的准确程度.以下是来自官方来源的sqrt(2):

(first 200 digits) 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623091229702492483605585073721264412149709993583141322266592750559275579995050115278206057147
Run Code Online (Sandbox Code Playgroud)

在这里它使用我在下面概述的方法SQRT_DIG等于150:

(first 200 digits) 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623091229702492483605585073721264412149709993583141322266592750559275579995050115278206086685
Run Code Online (Sandbox Code Playgroud)

第一个偏差发生在195位精度之后.如果您需要如此高的精确度,请自担风险.

更改SQRT_DIG为1000会产生1570位精度.

private static final BigDecimal SQRT_DIG = new BigDecimal(150);
private static final BigDecimal SQRT_PRE = new BigDecimal(10).pow(SQRT_DIG.intValue());

/**
 * Private utility method used to compute the square root of a BigDecimal.
 * 
 * @author Luciano Culacciatti 
 * @url http://www.codeproject.com/Tips/257031/Implementing-SqrtRoot-in-BigDecimal
 */
private static BigDecimal sqrtNewtonRaphson  (BigDecimal c, BigDecimal xn, BigDecimal precision){
    BigDecimal fx = xn.pow(2).add(c.negate());
    BigDecimal fpx = xn.multiply(new BigDecimal(2));
    BigDecimal xn1 = fx.divide(fpx,2*SQRT_DIG.intValue(),RoundingMode.HALF_DOWN);
    xn1 = xn.add(xn1.negate());
    BigDecimal currentSquare = xn1.pow(2);
    BigDecimal currentPrecision = currentSquare.subtract(c);
    currentPrecision = currentPrecision.abs();
    if (currentPrecision.compareTo(precision) <= -1){
        return xn1;
    }
    return sqrtNewtonRaphson(c, xn1, precision);
}

/**
 * Uses Newton Raphson to compute the square root of a BigDecimal.
 * 
 * @author Luciano Culacciatti 
 * @url http://www.codeproject.com/Tips/257031/Implementing-SqrtRoot-in-BigDecimal
 */
public static BigDecimal bigSqrt(BigDecimal c){
    return sqrtNewtonRaphson(c,new BigDecimal(1),new BigDecimal(1).divide(SQRT_PRE));
}
Run Code Online (Sandbox Code Playgroud)

一定要看看barwnikk的答案.它更简洁,看似提供更好或更好的精度.

  • 它确实......"123.1231231229999988908297926818176540649 ..."但是,如果你执行乘法运算,你得到`15159.303447561417273129` .....使用`BigDecimal`的`String`构造函数会得到正确的答案.`bigSqrt(new BigDecimal("15159.303447561417273129"))= 123.1231231230000000000000000000000000 ...`我想这是`Double`到`BigDecimal`方便构造方法的问题,而不是sqrt算法. (5认同)
  • 请注意,对于较大的BigDecimal,此递归解决方案将耗尽堆栈. (4认同)

bar*_*ikk 28

public static BigDecimal sqrt(BigDecimal A, final int SCALE) {
    BigDecimal x0 = new BigDecimal("0");
    BigDecimal x1 = new BigDecimal(Math.sqrt(A.doubleValue()));
    while (!x0.equals(x1)) {
        x0 = x1;
        x1 = A.divide(x0, SCALE, ROUND_HALF_UP);
        x1 = x1.add(x0);
        x1 = x1.divide(TWO, SCALE, ROUND_HALF_UP);

    }
    return x1;
}
Run Code Online (Sandbox Code Playgroud)

这项工作完美!速度超过65536位!

  • @EntangledLoops此算法是[Babylonian方法]的实现(https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method).无论初始估算如何,它都会在SCALE给出的精度水平上收敛到相同的精确结果.估计数仅用于提高业绩; Math.sqrt()是一个合理的开始:它对于较大的值(使MZB的替代选择成为一个很好的通用选择)失败,但是当它不返回NaN时*不会影响精度.你可能会欺骗自己超出范围,但肯定不精确. (10认同)
  • +1使用良好的初始估算.`!x0.equals(x1)`虽然有点危险.你有证据表明循环终止了吗? (4认同)
  • 请注意,对于较大的BigDecimal,A.doubleValue()= NaN.使用(比方说)A.divide(TWO,RoundingMode.FLOOR)可以使用更大的值. (3认同)
  • 什么是TWO,它不编译?它只是第二个,BigDecimal.valueOf(2)? (3认同)
  • 使用`BigDecimal.ZERO`而不是重新创建轮子.此外,`doubleValue()`调用通过丢弃任意精度来破坏本练习的全部目的. (3认同)
  • @barwnikk当然,这里必须要有常量-只是尝试了一下您的代码,并且不确定两个是否代表我所缺少的其他东西。感谢您的澄清。 (2认同)

dim*_*414 12

从Java 9开始,你可以!见BigDecimal.sqrt().


小智 8

通过使用Karp的技巧,这可以在没有仅仅两行的循环的情况下实现,给出32位精度:

public static BigDecimal sqrt(BigDecimal value) {
    BigDecimal x = new BigDecimal(Math.sqrt(value.doubleValue()));
    return x.add(new BigDecimal(value.subtract(x.multiply(x)).doubleValue() / (x.doubleValue() * 2.0)));
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,只有32个精度...... BigDecimal适用于大数字. (3认同)
  • 这样就丢弃了BigDecimal的任意精度,这是整个要点。 (3认同)

Eug*_*lin 5

如果只需要找到整数平方根 - 这两种方法都可以使用.

牛顿的方法 - 即使对于1000位数BigInteger也非常快:

public static BigInteger sqrtN(BigInteger in) {
    final BigInteger TWO = BigInteger.valueOf(2);
    int c;

    // Significantly speed-up algorithm by proper select of initial approximation
    // As square root has 2 times less digits as original value
    // we can start with 2^(length of N1 / 2)
    BigInteger n0 = TWO.pow(in.bitLength() / 2);
    // Value of approximate value on previous step
    BigInteger np = in;

    do {
        // next approximation step: n0 = (n0 + in/n0) / 2
        n0 = n0.add(in.divide(n0)).divide(TWO);

        // compare current approximation with previous step
        c = np.compareTo(n0);

        // save value as previous approximation
        np = n0;

        // finish when previous step is equal to current
    }  while (c != 0);

    return n0;
}
Run Code Online (Sandbox Code Playgroud)

二分法 - 比牛顿慢50倍 - 仅用于教育目的:

 public static BigInteger sqrtD(final BigInteger in) {
    final BigInteger TWO = BigInteger.valueOf(2);
    BigInteger n0, n1, m, m2, l;
    int c;

    // Init segment
    n0 = BigInteger.ZERO;
    n1 = in;

    do {
        // length of segment
        l = n1.subtract(n0);

        // middle of segment
        m = l.divide(TWO).add(n0);

        // compare m^2 with in
        c = m.pow(2).compareTo(in);

        if (c == 0) {
            // exact value is found
            break;
        }  else if (c > 0) {
            // m^2 is bigger than in - choose left half of segment
            n1 = m;
        } else {
            // m^2 is smaller than in - choose right half of segment
            n0 = m;
        }

        // finish if length of segment is 1, i.e. approximate value is found
    }  while (l.compareTo(BigInteger.ONE) > 0);

    return m;
}
Run Code Online (Sandbox Code Playgroud)


Nim*_*sky -3

BigDecimal.valueOf(Math.sqrt(myBigDecimal.doubleValue()));
Run Code Online (Sandbox Code Playgroud)

  • BigDecimal 的目的就这样落空了。 (15认同)
  • 不过,我认为您需要明确指出,只有当双精度数的精度答案可以接受,并且原始 BigDecimal 在双精度数允许的范围内时,这才足够。通常使用 BigDecimal 的全部理由是这些条件之一或两个都不成立。 (10认同)
  • 好吧,这使用了 doubleValue() 这意味着我可能会失去很多精度,但另一方面我的问题是“如何仅使用 JAVA API”,所以,非常感谢你。我会用这个,无论发生什么,都会发生。 (2认同)