java bigdecimal 开方_在Java中BigDecimal的平方根(Square root of BigDecimal in

这篇博客探讨了在Java中计算BigDecimal的平方根的不同方法,包括使用Java API、牛顿-拉弗森算法、二分法以及其他精确计算算法。文章通过多个示例展示了如何实现高精度的平方根计算,并提供了不同算法的效率和精度比较。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Answer 1:

我用这个和它的作品相当不错的。 下面是该算法如何工作在较高水平的例子。

编辑:我很好奇,想看看到底有多精确,这是定义如下。 这里给出一个从所述的sqrt(2) 官方来源 :

(first 200 digits) 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623091229702492483605585073721264412149709993583141322266592750559275579995050115278206057147

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

(first 200 digits) 1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157273501384623091229702492483605585073721264412149709993583141322266592750559275579995050115278206086685

第一偏差后的精度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));

}

一定要检查出barwnikk的答案。 它更简洁,看似提供一样好或更好的精度。

Answer 2:

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;

}

这项工作完美! 极快的超过65536位!

Answer 3:

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

Answer 4:

通过使用卡普的技巧,这可以在不只有两行的循环来实现,给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)));

}

Answer 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;

}

二分法 -达比牛顿慢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;

}

Answer 6:

如果要计算平方根的比更契合位号码在双(一个BigDecimal与大规模的):

维基百科有用于计算平方根的文章: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method

这是我实现的吧:

public static BigDecimal sqrt(BigDecimal in, int scale){

BigDecimal sqrt = new BigDecimal(1);

sqrt.setScale(scale + 3, RoundingMode.FLOOR);

BigDecimal store = new BigDecimal(in.toString());

boolean first = true;

do{

if (!first){

store = new BigDecimal(sqrt.toString());

}

else first = false;

store.setScale(scale + 3, RoundingMode.FLOOR);

sqrt = in.divide(store, scale + 3, RoundingMode.FLOOR).add(store).divide(

BigDecimal.valueOf(2), scale + 3, RoundingMode.FLOOR);

}while (!store.equals(sqrt));

return sqrt.setScale(scale, RoundingMode.FLOOR);

}

setScale(scale + 3, RoundingMode.Floor)因为在计算提供了更多的精度。 RoundingMode.Floor截断数, RoundingMode.HALF_UP不正常的四舍五入。

Answer 7:

这是非常准确和快速的解决方案,它是基于我的BigIntSqRoot的解决方案 ,并在未来观察:a ^ 2B的平方根-是一个乘B的根 。 使用这种方法,我可以容易地计算所述第一数字1000 2平方根。

1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605714701095599716059702745345968620147285174186408891986095523292304843087143214508397626036279952514079896872533965463318088296406206152583523950547457502877599617298355752203375318570113543746034084988471603868999706990048150305440277903164542478230684929369186215805784631115966687130130156185689872372352885092648612494977154218334204285686060146824720771435854874155657069677653720226485447015858801620758474922657226002085584466521458398893944370926591800311388246468157082630100594858704003186480342194897278290641045072636881313739855256117322040245091227700226941127573627280495738108967504018369868368450725799364729060762996941380475654823728997180326802474420629269124859052181004459842150591120249441341728531478105803603371077309182869314710171111683916581726889419758716582152128229518488472

因此,这里的源代码

public class BigIntSqRoot {

private static final int PRECISION = 10000;

private static BigInteger multiplier = BigInteger.valueOf(10).pow(PRECISION * 2);

private static BigDecimal root = BigDecimal.valueOf(10).pow(PRECISION);

private static BigInteger two = BigInteger.valueOf(2L);

public static BigDecimal bigDecimalSqRootFloor(BigInteger x)

throws IllegalArgumentException {

BigInteger result = bigIntSqRootFloor(x.multiply(multiplier));

//noinspection BigDecimalMethodWithoutRoundingCalled

return new BigDecimal(result).divide(root);

}

public static BigInteger bigIntSqRootFloor(BigInteger x)

throws IllegalArgumentException {

if (checkTrivial(x)) {

return x;

}

if (x.bitLength() < 64) { // Can be cast to long

double sqrt = Math.sqrt(x.longValue());

return BigInteger.valueOf(Math.round(sqrt));

}

// starting with y = x / 2 avoids magnitude issues with x squared

BigInteger y = x.divide(two);

BigInteger value = x.divide(y);

while (y.compareTo(value) > 0) {

y = value.add(y).divide(two);

value = x.divide(y);

}

return y;

}

public static BigInteger bigIntSqRootCeil(BigInteger x)

throws IllegalArgumentException {

BigInteger y = bigIntSqRootFloor(x);

if (x.compareTo(y.multiply(y)) == 0) {

return y;

}

return y.add(BigInteger.ONE);

}

private static boolean checkTrivial(BigInteger x) {

if (x == null) {

throw new NullPointerException("x can't be null");

}

if (x.compareTo(BigInteger.ZERO) < 0) {

throw new IllegalArgumentException("Negative argument.");

}

return x.equals(BigInteger.ZERO) || x.equals(BigInteger.ONE);

}

}

Answer 8:

没有在Java API的任何东西,所以如果双不够准确(如果没有,为什么要用BigDecimal的呢?),那么你需要像下面的代码。)

从http://www.java2s.com/Code/Java/Language-Basics/DemonstrationofhighprecisionarithmeticwiththeBigDoubleclass.htm

import java.math.BigDecimal;

public class BigDSqrt {

public static BigDecimal sqrt(BigDecimal n, int s) {

BigDecimal TWO = BigDecimal.valueOf(2);

// Obtain the first approximation

BigDecimal x = n

.divide(BigDecimal.valueOf(3), s, BigDecimal.ROUND_DOWN);

BigDecimal lastX = BigDecimal.valueOf(0);

// Proceed through 50 iterations

for (int i = 0; i < 50; i++) {

x = n.add(x.multiply(x)).divide(x.multiply(TWO), s,

BigDecimal.ROUND_DOWN);

if (x.compareTo(lastX) == 0)

break;

lastX = x;

}

return x;

}

}

Answer 9:

public static BigDecimal sqrt( final BigDecimal value )

{

BigDecimal guess = value.multiply( DECIMAL_HALF );

BigDecimal previousGuess;

do

{

previousGuess = guess;

guess = sqrtGuess( guess, value );

} while ( guess.subtract( previousGuess ).abs().compareTo( EPSILON ) == 1 );

return guess;

}

private static BigDecimal sqrtGuess( final BigDecimal guess,

final BigDecimal value )

{

return guess.subtract( guess.multiply( guess ).subtract( value ).divide( DECIMAL_TWO.multiply( guess ), SCALE, RoundingMode.HALF_UP ) );

}

private static BigDecimal epsilon()

{

final StringBuilder builder = new StringBuilder( "0." );

for ( int i = 0; i < SCALE - 1; ++i )

{

builder.append( "0" );

}

builder.append( "1" );

return new BigDecimal( builder.toString() );

}

private static final int SCALE = 1024;

private static final BigDecimal EPSILON = epsilon();

public static final BigDecimal DECIMAL_HALF = new BigDecimal( "0.5" );

public static final BigDecimal DECIMAL_TWO = new BigDecimal( "2" );

Answer 10:

正如有人说过:如果你不介意你的答案会是怎样的精度,但只希望产生15还是有效的,那么为什么要使用BigDecimal的所有后随机数字?

下面是应该做浮点BigDecimals的绝招方法的代码:

import java.math.BigDecimal;

import java.math.BigInteger;

import java.math.MathContext;

public BigDecimal bigSqrt(BigDecimal d, MathContext mc) {

// 1. Make sure argument is non-negative and treat Argument 0

int sign = d.signum();

if(sign == -1)

throw new ArithmeticException("Invalid (negative) argument of sqrt: "+d);

else if(sign == 0)

return BigDecimal.ZERO;

// 2. Scaling:

// factorize d = scaledD * scaleFactorD

// = scaledD * (sqrtApproxD * sqrtApproxD)

// such that scalefactorD is easy to take the square root

// you use scale and bitlength for this, and if odd add or subtract a one

BigInteger bigI=d.unscaledValue();

int bigS=d.scale();

int bigL = bigI.bitLength();

BigInteger scaleFactorI;

BigInteger sqrtApproxI;

if ((bigL%2==0)){

scaleFactorI=BigInteger.ONE.shiftLeft(bigL);

sqrtApproxI=BigInteger.ONE.shiftLeft(bigL/2);

}else{

scaleFactorI=BigInteger.ONE.shiftLeft(bigL-1);

sqrtApproxI=BigInteger.ONE.shiftLeft((bigL-1)/2 );

}

BigDecimal scaleFactorD;

BigDecimal sqrtApproxD;

if ((bigS%2==0)){

scaleFactorD=new BigDecimal(scaleFactorI,bigS);

sqrtApproxD=new BigDecimal(sqrtApproxI,bigS/2);

}else{

scaleFactorD=new BigDecimal(scaleFactorI,bigS+1);

sqrtApproxD=new BigDecimal(sqrtApproxI,(bigS+1)/2);

}

BigDecimal scaledD=d.divide(scaleFactorD);

// 3. This is the core algorithm:

// Newton-Ralpson for scaledD : In case of f(x)=sqrt(x),

// Heron's Method or Babylonian Method are other names for the same thing.

// Since this is scaled we can be sure that scaledD.doubleValue() works

// for the start value of the iteration without overflow or underflow

System.out.println("ScaledD="+scaledD);

double dbl = scaledD.doubleValue();

double sqrtDbl = Math.sqrt(dbl);

BigDecimal a = new BigDecimal(sqrtDbl, mc);

BigDecimal HALF=BigDecimal.ONE.divide(BigDecimal.ONE.add(BigDecimal.ONE));

BigDecimal h = new BigDecimal("0", mc);

// when to stop iterating? You start with ~15 digits of precision, and Newton-Ralphson is quadratic

// in approximation speed, so in roundabout doubles the number of valid digits with each step.

// This fmay be safer than testing a BigDecifmal against zero.

int prec = mc.getPrecision();

int start = 15;

do {

h = scaledD.divide(a, mc);

a = a.add(h).multiply(HALF);

start *= 2;

} while (start <= prec);

// 3. Return rescaled answer. sqrt(d)= sqrt(scaledD)*sqrtApproxD :

return (a.multiply(sqrtApproxD));

}

作为测试,尝试多次方的一些几次嘴皮子反复平方根,看你是从你开始的地方多么接近。

Answer 11:

我一直在研究这个问题,现在去了几天,并用一种​​算法,不只是采取平方根来了,但确实每个BigDecimal的整数下面每一根。

public static BigDecimal nrt(BigDecimal bd,int root) {

//if number is smaller then double_max_value it's faster to use the usual math

//library

if(bd.compareTo(BigDecimal.valueOf(Double.MAX_VALUE)) < 0)

return new BigDecimal( Math.pow(bd.doubleValue(), 1D / (double)root ));

BigDecimal in = bd;

int digits = bd.precision() - bd.scale() -1; //take digits to get the numbers power of ten

in = in.scaleByPowerOfTen (- (digits - digits%root) ); //scale down to the lowest number with it's power of ten mod root is the same as initial number

if(in.compareTo(BigDecimal.valueOf( Double.MAX_VALUE) ) > 0) { //if down scaled value is bigger then double_max_value, we find the answer by splitting the roots into factors and calculate them seperately and find the final result by multiplying the subresults

int highestDenominator = highestDenominator(root);

if(highestDenominator != 1) {

return nrt( nrt(bd, root / highestDenominator),highestDenominator); // for example turns 1^(1/25) 1^(1/5)^1(1/5)

}

//hitting this point makes the runtime about 5-10 times higher,

//but the alternative is crashing

else return nrt(bd,root+1) //+1 to make the root even so it can be broken further down into factors

.add(nrt(bd,root-1),MathContext.DECIMAL128) //add the -1 root and take the average to deal with the inaccuracy created by this

.divide(BigDecimal.valueOf(2),MathContext.DECIMAL128);

}

double downScaledResult = Math.pow(in.doubleValue(), 1D /root); //do the calculation on the downscaled value

BigDecimal BDResult =new BigDecimal(downScaledResult) // scale back up by the downscaled value divided by root

.scaleByPowerOfTen( (digits - digits % root) / root );

return BDResult;

}

private static int highestDenominator(int n) {

for(int i = n-1; i>1;i--) {

if(n % i == 0) {

return i;

}

}

return 1;

}

注意,这种方法不能给你更多的数字则可以在双举行。 所以如果你需要高精确度,这种方法是不适合你。 但是,如果你需要做一个非常大的数量的根确实是体面的快速和准确性的可接受的水平。 这尤其是减慢如果根是一个大素数,这是与高度分割的根源更快。

它的工作原理,因为数学性质的是n ^(X / A)=(N * 10 ^一)^(X / A)/ 10这意味着我们可以采取一个非常大的数量和规模下来的东西双CAN处理,然后它的规模回升的基础上,这是多大的最初缩小。

我测试的平均误差的1.000384947520725(0.03%关闭)的一个因素,后一百万次迭代。 这是不是一样大,我会希望它是,但是当你与双打工作,并缩减25000位数字肯定有很多是一些不准确。

我的标杆给我的结果:

100000 5098rt在11786ms的数均3.599916E + 150737

100000 3766rt与4195ms的数均1.606334E + 46239

100000 6849rt与8888ms的数均1.624569E + 291918

100000 733rt在536ms的数均1.688336E + 14797

100000 841rt与1985ms的数均7.201332E + 170465

100000 3335rt与3781ms的数均1.884063E + 295617

所以平均大约0,1 - 0,5毫秒的运行时间是不是很大,但它是可以接受的。

它配备了现在能够做到权力与浮点,instaed只有整数,具有以下功能的免费奖励:

public static BigDecimal pow(BigDecimal bd, float power) {

int rt = (int) (1D / ( power - ((long)power) )); // 1 divided by decimals in power

int i = (int)power; //take the real number part of power

return bd.pow(i,MathContext.DECIMAL64).multiply(nrt(bd,rt),MathContext.DECIMAL64);

}

Answer 12:

BigDecimal.valueOf(Math.sqrt(myBigDecimal.doubleValue()));

文章来源: Square root of BigDecimal in Java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值