From 7b7f9936e47040d8991617d906d764ff3c047947 Mon Sep 17 00:00:00 2001 From: Arthur Drobot Date: Mon, 7 Aug 2017 17:58:18 -0700 Subject: [PATCH] Rewrite precise number to limit significant figures rather than decimal places. Add getMaxError to NumberInterface; modify numbers and StandardPlugin appropriately. --- .../org/nwapw/abacus/number/NaiveNumber.java | 4 +++ .../nwapw/abacus/number/NumberInterface.java | 7 +++++ .../nwapw/abacus/number/PreciseNumber.java | 29 +++++++++++++++---- .../nwapw/abacus/plugin/StandardPlugin.java | 20 ++++--------- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/nwapw/abacus/number/NaiveNumber.java b/src/main/java/org/nwapw/abacus/number/NaiveNumber.java index 48beb1a..05dcc33 100755 --- a/src/main/java/org/nwapw/abacus/number/NaiveNumber.java +++ b/src/main/java/org/nwapw/abacus/number/NaiveNumber.java @@ -128,5 +128,9 @@ public class NaiveNumber extends NumberInterface { return Double.toString(Math.round(value * shiftBy) / shiftBy); } + @Override + public NumberInterface getMaxError(){ + return new NaiveNumber(Math.pow(10, -18)); + } } diff --git a/src/main/java/org/nwapw/abacus/number/NumberInterface.java b/src/main/java/org/nwapw/abacus/number/NumberInterface.java index 83007c2..34edfcc 100755 --- a/src/main/java/org/nwapw/abacus/number/NumberInterface.java +++ b/src/main/java/org/nwapw/abacus/number/NumberInterface.java @@ -255,4 +255,11 @@ public abstract class NumberInterface { 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(); + } diff --git a/src/main/java/org/nwapw/abacus/number/PreciseNumber.java b/src/main/java/org/nwapw/abacus/number/PreciseNumber.java index 7d288b7..e3af94f 100755 --- a/src/main/java/org/nwapw/abacus/number/PreciseNumber.java +++ b/src/main/java/org/nwapw/abacus/number/PreciseNumber.java @@ -1,7 +1,7 @@ package org.nwapw.abacus.number; import java.math.BigDecimal; -import java.math.RoundingMode; +import java.math.MathContext; /** * 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); + /** + * 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. */ @@ -48,7 +63,7 @@ public class PreciseNumber extends NumberInterface { @Override public int getMaxPrecision() { - return 65; + return internalContext.getPrecision(); } @Override @@ -58,7 +73,7 @@ public class PreciseNumber extends NumberInterface { @Override 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 @@ -147,7 +162,11 @@ public class PreciseNumber extends NumberInterface { @Override public String toString() { - BigDecimal rounded = value.setScale(getMaxPrecision() - 15, RoundingMode.HALF_UP); - return rounded.stripTrailingZeros().toPlainString(); + return value.round(outputContext).toString(); + } + + @Override + public NumberInterface getMaxError(){ + return new PreciseNumber(value.ulp()).multiplyInternal(TEN.intPowInternal(value.precision()-internalContext.getPrecision())); } } diff --git a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java index 4d8aea2..3f2ce22 100755 --- a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java @@ -188,7 +188,7 @@ public class StandardPlugin extends Plugin { */ 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). NumberInterface currentNumerator = x, currentTerm = x, sum = x; int n = 1; @@ -207,7 +207,7 @@ public class StandardPlugin extends Plugin { * @return the value of log(2) with the appropriate precision. */ private NumberInterface getLog2(NumberInterface number) { - NumberInterface maxError = getMaxError(number); + NumberInterface maxError = number.getMaxError(); //NumberInterface errorBound = fromInt(number.getClass(), 1); //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. @@ -301,7 +301,7 @@ public class StandardPlugin extends Plugin { @Override protected NumberInterface applyInternal(NumberInterface[] params) { - NumberInterface maxError = getMaxError(params[0]); + NumberInterface maxError = params[0].getMaxError(); int n = 0; if (params[0].signum() <= 0) { 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()); //Detect integer bases: 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){ NumberInterface[] newParams = {params[0], params[1].fractionalPart()}; return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(newParams)); @@ -476,16 +476,6 @@ public class StandardPlugin extends Plugin { 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 * computes factorials of non-negative integers. @@ -517,7 +507,7 @@ public class StandardPlugin extends Plugin { */ private static NumberInterface sinTaylor(NumberInterface x) { NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x; - NumberInterface maxError = getMaxError(x); + NumberInterface maxError = x.getMaxError(); int n = 1; do { n += 2;