Precisione di "algoritmi calcolatori"

5

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):

  1. Posso raggiungere il livello di accuratezza richiesto utilizzando gli algoritmi della calcolatrice?
  2. Se sì, allora dove posso trovare gli algoritmi richiesti?
  3. 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.

    
posta Hungry Blue Dev 30.09.2014 - 14:51
fonte

1 risposta

6

Come le persone suggeriscono nei commenti, se vuoi farlo per il divertimento di farlo, questa è una cosa, ma se vuoi una libreria di funzioni scientifiche per usare , dovresti usare uno che è già stato scritto da esperti. C'è un elenco su link

Le librerie open-source elencate sono anche un posto dove cercare (se non trovi!) codice leggibile.

Algoritmi come questo fanno parte di analisi numerica ; vedi anche Wikipedia per quello. I famosi libri "Ricette numeriche" forniscono codice e discussioni per molti tipi di calcoli.

Se stai parlando di calcolatrici elettroniche fisiche (al contrario di programmi che vengono anche chiamati "calcolatori"), ci sono tre ragioni per cui dubito che potresti usare il codice della calcolatrice come prototipo per la tua libreria matematica di precisione arbitraria.

  • Non conosco nessuna compagnia di calcolatori che abbia rilasciato il software e le progettazioni hardware dei suoi calcolatori.

  • Non ho mai sentito di una calcolatrice elettronica con aritmetica di precisione arbitraria. Un tipico calcolatore scientifico ha una precisione fissa. Non vi sarà alcun indizio nel codice su come estendere una determinata funzione ad una precisione maggiore (mentre gli scrittori di, ad esempio, GNU MPFR avranno risolto questi problemi). Ad esempio, quali risultati intermedi di precisione avranno bisogno? Quante volte dovrai passare attraverso i loop? L'algoritmo funziona in modo abbastanza veloce, o rallenterà troppo quando aggiungi precisione?

  • Una determinata calcolatrice può avere hardware matematico molto avanzato (per esempio serie Taylor) o hardware molto primitivo (non può nemmeno moltiplicare i numeri a due cifre). Il codice sarà probabilmente codice macchina scritto a mano senza codice sorgente più leggibile. Dovresti capire ed emulare entrambe le funzioni dell'hardware e del codice nel tuo software. Un hobby interessante ma forse non quello a cui stavi pensando.

risposta data 01.10.2014 - 19:59
fonte

Leggi altre domande sui tag