Sto costruendo una classe di libreria che fornisce funzionalità per operazioni matematiche su BigDecimals
(e alcuni su BigIntegers
) . Ora, i BigIntegers sono abbastanza facili da padroneggiare e piacevoli da usare. BigDecimals può essere difficile, ma alla fine, ripaga finalmente.
Voglio che la mia classe fornisca risultati accurati fino a un livello specificato di accuratezza (come il numero di posti dopo il punto decimale). Questo è dove Math
mi interrompe perché supporta operazioni su% codice%. Ora double
supporta 15-16 cifre significative (non 15-16 punti dopo i decimali). Quindi, ho deciso di eseguire l'upgrade a double
.
Ma ora, il problema è dove posso trovare algoritmi che supportano calcoli di precisione arbitrari? Inoltre, vorrei rendere il mio Bigdecimal
(così lo chiamo) analogo al suo fratello nel java.lang pacchetto. Poi mi ha colpito il fatto che se optassi per algoritmi usati dalle calcolatrici scientifiche, allora potrei raggiungere il livello di accuratezza richiesto.
Quindi ecco le mie domande (correlate):
- Posso raggiungere il livello di accuratezza richiesto utilizzando gli algoritmi della calcolatrice?
- Se sì, allora dove posso trovare gli algoritmi richiesti?
- Ci sono dei avvertimenti in agguato nel mio approccio?
UPDATE:
Al momento, ho appena aggiunto il supporto per il metodo BigMath
. Usa il metodo Newton (e un trucco speciale per generare una stima buona ).
Quindi, ecco la intera sqrt(BigDecimal)
class:
package in.blogspot.life_on_the_heap.bigmath;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
/**
* @author ambigram_maker
*/
public class BigMath {
public static void main(String[] args) {
BigDecimal number = new BigDecimal("91.91");
BigDecimal sqrt = sqrt(number);
System.out.println("number = " + number);
System.out.println("sqrt(number) = " + sqrt);
System.out.println("number = " +
sqrt.multiply(sqrt, getContext(sqrt, DEFAULT_PREC))
.stripTrailingZeros());
}
public static final BigDecimal TWO = BigDecimal.valueOf(2L);
/*
* The default number of places after the decimal. This is also the
* *minimum* precision provided by this class.
*/
private static final int DEFAULT_PREC = 20;
/*
* Maintains the "precision" or the number of digits after the decimal.
*/
private static int precision = DEFAULT_PREC;
public static void setPrecision(int precision_) {
if (precision_ < DEFAULT_PREC) {
precision = DEFAULT_PREC;
} else {
precision = precision_;
}
}
public static int getPrecision() {
return precision;
}
public static BigDecimal sqrt(BigDecimal decimal) {
return sqrt(decimal, precision);
}
public static BigDecimal sqrt(BigDecimal decimal, int P_A_D) {
// quick exit checks:
if (decimal.compareTo(BigDecimal.ZERO) < 0) {
return BigDecimal.valueOf(Double.NaN);
}
BigDecimal
answer, // The storage for the guesses.
original, // The result of squaring the guesses
epsillon; // The tolerance of this method.
MathContext context = getContext(decimal, P_A_D);
{
/*
* Obtain a good estimate of the square-root for the initial guess.
* This is done by obtaining the "top" half of the bits of the decimal.
*/
BigInteger integer = decimal.toBigInteger();
answer = new BigDecimal
(integer.shiftRight(integer.bitLength() >>> 1));
}
original = answer.multiply(answer);
epsillon = getEpsillon(P_A_D);
while (original.subtract(decimal).abs().compareTo(epsillon) > 0) {
answer = answer.subtract(original.subtract(decimal)
.divide(TWO.multiply(answer),
context));
original = answer.multiply(answer);
}
return answer.round(context);
}
public static BigDecimal getEpsillon(int precision) {
return new BigDecimal("1E-" + precision);
}
private static MathContext getContext(BigDecimal decimal, int precision) {
int beforePoint = (decimal.toString()).indexOf('.');
if (beforePoint == -1) beforePoint = decimal.toString().length();
return new MathContext(beforePoint + precision);
}
}
L'output è quello desiderato:
number = 91.91
sqrt(number) = 9.586970324351692703533
number = 91.91
Poiché sono abbastanza contento del risultato, sono determinato ad andare avanti. Quindi, cerco un consiglio.