1
0
mirror of https://github.com/DanilaFe/abacus synced 2024-11-18 00:19:32 -08:00

Rewrite precise number to limit significant figures rather than decimal places. Add getMaxError to NumberInterface; modify numbers and StandardPlugin appropriately.

This commit is contained in:
Arthur Drobot 2017-08-07 17:58:18 -07:00
parent 7ae59afade
commit 7b7f9936e4
4 changed files with 40 additions and 20 deletions

View File

@ -128,5 +128,9 @@ public class NaiveNumber extends NumberInterface {
return Double.toString(Math.round(value * shiftBy) / shiftBy); return Double.toString(Math.round(value * shiftBy) / shiftBy);
} }
@Override
public NumberInterface getMaxError(){
return new NaiveNumber(Math.pow(10, -18));
}
} }

View File

@ -255,4 +255,11 @@ public abstract class NumberInterface {
return promoteToInternal(toClass); return promoteToInternal(toClass);
} }
/**
* Returns the smallest error this instance can tolerate depending
* on its precision and value.
* @return the smallest error that should be permitted in calculations.
*/
public abstract NumberInterface getMaxError();
} }

View File

@ -1,7 +1,7 @@
package org.nwapw.abacus.number; package org.nwapw.abacus.number;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.MathContext;
/** /**
* A number that uses a BigDecimal to store its value, * A number that uses a BigDecimal to store its value,
@ -22,6 +22,21 @@ public class PreciseNumber extends NumberInterface {
*/ */
public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN); public static final PreciseNumber TEN = new PreciseNumber(BigDecimal.TEN);
/**
* The number of extra significant figures kept in calculations before rounding for output.
*/
private static int numExtraInternalSigFigs = 15;
/**
* MathContext that is used when rounding a number prior to output.
*/
private static MathContext outputContext = new MathContext(30);
/**
* MathContext that is actually used in calculations.
*/
private static MathContext internalContext = new MathContext(outputContext.getPrecision()+numExtraInternalSigFigs);
/** /**
* The value of the PreciseNumber. * The value of the PreciseNumber.
*/ */
@ -48,7 +63,7 @@ public class PreciseNumber extends NumberInterface {
@Override @Override
public int getMaxPrecision() { public int getMaxPrecision() {
return 65; return internalContext.getPrecision();
} }
@Override @Override
@ -58,7 +73,7 @@ public class PreciseNumber extends NumberInterface {
@Override @Override
public NumberInterface divideInternal(NumberInterface divisor) { public NumberInterface divideInternal(NumberInterface divisor) {
return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, this.getMaxPrecision(), RoundingMode.HALF_UP)); return new PreciseNumber(value.divide(((PreciseNumber) divisor).value, internalContext));
} }
@Override @Override
@ -147,7 +162,11 @@ public class PreciseNumber extends NumberInterface {
@Override @Override
public String toString() { public String toString() {
BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP); return value.round(outputContext).toString();
return rounded.stripTrailingZeros().toPlainString(); }
@Override
public NumberInterface getMaxError(){
return new PreciseNumber(value.ulp()).multiplyInternal(TEN.intPowInternal(value.precision()-internalContext.getPrecision()));
} }
} }

View File

@ -188,7 +188,7 @@ public class StandardPlugin extends Plugin {
*/ */
private NumberInterface getLogPartialSum(NumberInterface x) { private NumberInterface getLogPartialSum(NumberInterface x) {
NumberInterface maxError = getMaxError(x); NumberInterface maxError = x.getMaxError();
x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1). x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1).
NumberInterface currentNumerator = x, currentTerm = x, sum = x; NumberInterface currentNumerator = x, currentTerm = x, sum = x;
int n = 1; int n = 1;
@ -207,7 +207,7 @@ public class StandardPlugin extends Plugin {
* @return the value of log(2) with the appropriate precision. * @return the value of log(2) with the appropriate precision.
*/ */
private NumberInterface getLog2(NumberInterface number) { private NumberInterface getLog2(NumberInterface number) {
NumberInterface maxError = getMaxError(number); NumberInterface maxError = number.getMaxError();
//NumberInterface errorBound = fromInt(number.getClass(), 1); //NumberInterface errorBound = fromInt(number.getClass(), 1);
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n) //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
//In the following, a=1/3^n, b=1/4^n, c = 1/n. //In the following, a=1/3^n, b=1/4^n, c = 1/n.
@ -301,7 +301,7 @@ public class StandardPlugin extends Plugin {
@Override @Override
protected NumberInterface applyInternal(NumberInterface[] params) { protected NumberInterface applyInternal(NumberInterface[] params) {
NumberInterface maxError = getMaxError(params[0]); NumberInterface maxError = params[0].getMaxError();
int n = 0; int n = 0;
if (params[0].signum() <= 0) { if (params[0].signum() <= 0) {
NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm; NumberInterface currentTerm = NaiveNumber.ONE.promoteTo(params[0].getClass()), sum = currentTerm;
@ -352,7 +352,7 @@ public class StandardPlugin extends Plugin {
return NaiveNumber.ONE.promoteTo(params[1].getClass()); return NaiveNumber.ONE.promoteTo(params[1].getClass());
//Detect integer bases: //Detect integer bases:
if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0 if(params[0].fractionalPart().compareTo(fromInt(params[0].getClass(), 0)) == 0
&& FUNCTION_ABS.apply(params[0]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 0 && FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[0].getClass(), Integer.MAX_VALUE)) < 0
&& FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0){ && FUNCTION_ABS.apply(params[1]).compareTo(fromInt(params[1].getClass(), 1)) >= 0){
NumberInterface[] newParams = {params[0], params[1].fractionalPart()}; NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams)); return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams));
@ -476,16 +476,6 @@ public class StandardPlugin extends Plugin {
return sum; return sum;
} }
/**
* Returns the maximum error based on the precision of the class of number.
*
* @param number Any instance of the NumberInterface in question (should return an appropriate precision).
* @return the maximum error.
*/
private static NumberInterface getMaxError(NumberInterface number) {
return fromInt(number.getClass(), 10).intPow(-number.getMaxPrecision());
}
/** /**
* A factorial function that uses memoization for each number class; it efficiently * A factorial function that uses memoization for each number class; it efficiently
* computes factorials of non-negative integers. * computes factorials of non-negative integers.
@ -517,7 +507,7 @@ public class StandardPlugin extends Plugin {
*/ */
private static NumberInterface sinTaylor(NumberInterface x) { private static NumberInterface sinTaylor(NumberInterface x) {
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x; NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
NumberInterface maxError = getMaxError(x); NumberInterface maxError = x.getMaxError();
int n = 1; int n = 1;
do { do {
n += 2; n += 2;