diff options
Diffstat (limited to 'src/main/java/org/apache/commons/math3/dfp/DfpDec.java')
-rw-r--r-- | src/main/java/org/apache/commons/math3/dfp/DfpDec.java | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/dfp/DfpDec.java b/src/main/java/org/apache/commons/math3/dfp/DfpDec.java new file mode 100644 index 0000000..5571c2d --- /dev/null +++ b/src/main/java/org/apache/commons/math3/dfp/DfpDec.java @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math3.dfp; + +/** + * Subclass of {@link Dfp} which hides the radix-10000 artifacts of the superclass. This should give + * outward appearances of being a decimal number with DIGITS*4-3 decimal digits. This class can be + * subclassed to appear to be an arbitrary number of decimal digits less than DIGITS*4-3. + * + * @since 2.2 + */ +public class DfpDec extends Dfp { + + /** + * Makes an instance with a value of zero. + * + * @param factory factory linked to this instance + */ + protected DfpDec(final DfpField factory) { + super(factory); + } + + /** + * Create an instance from a byte value. + * + * @param factory factory linked to this instance + * @param x value to convert to an instance + */ + protected DfpDec(final DfpField factory, byte x) { + super(factory, x); + } + + /** + * Create an instance from an int value. + * + * @param factory factory linked to this instance + * @param x value to convert to an instance + */ + protected DfpDec(final DfpField factory, int x) { + super(factory, x); + } + + /** + * Create an instance from a long value. + * + * @param factory factory linked to this instance + * @param x value to convert to an instance + */ + protected DfpDec(final DfpField factory, long x) { + super(factory, x); + } + + /** + * Create an instance from a double value. + * + * @param factory factory linked to this instance + * @param x value to convert to an instance + */ + protected DfpDec(final DfpField factory, double x) { + super(factory, x); + round(0); + } + + /** + * Copy constructor. + * + * @param d instance to copy + */ + public DfpDec(final Dfp d) { + super(d); + round(0); + } + + /** + * Create an instance from a String representation. + * + * @param factory factory linked to this instance + * @param s string representation of the instance + */ + protected DfpDec(final DfpField factory, final String s) { + super(factory, s); + round(0); + } + + /** + * Creates an instance with a non-finite value. + * + * @param factory factory linked to this instance + * @param sign sign of the Dfp to create + * @param nans code of the value, must be one of {@link #INFINITE}, {@link #SNAN}, {@link #QNAN} + */ + protected DfpDec(final DfpField factory, final byte sign, final byte nans) { + super(factory, sign, nans); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance() { + return new DfpDec(getField()); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final byte x) { + return new DfpDec(getField(), x); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final int x) { + return new DfpDec(getField(), x); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final long x) { + return new DfpDec(getField(), x); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final double x) { + return new DfpDec(getField(), x); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final Dfp d) { + + // make sure we don't mix number with different precision + if (getField().getRadixDigits() != d.getField().getRadixDigits()) { + getField().setIEEEFlagsBits(DfpField.FLAG_INVALID); + final Dfp result = newInstance(getZero()); + result.nans = QNAN; + return dotrap(DfpField.FLAG_INVALID, "newInstance", d, result); + } + + return new DfpDec(d); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final String s) { + return new DfpDec(getField(), s); + } + + /** {@inheritDoc} */ + @Override + public Dfp newInstance(final byte sign, final byte nans) { + return new DfpDec(getField(), sign, nans); + } + + /** + * Get the number of decimal digits this class is going to represent. Default implementation + * returns {@link #getRadixDigits()}*4-3. Subclasses can override this to return something less. + * + * @return number of decimal digits this class is going to represent + */ + protected int getDecimalDigits() { + return getRadixDigits() * 4 - 3; + } + + /** {@inheritDoc} */ + @Override + protected int round(int in) { + + int msb = mant[mant.length - 1]; + if (msb == 0) { + // special case -- this == zero + return 0; + } + + int cmaxdigits = mant.length * 4; + int lsbthreshold = 1000; + while (lsbthreshold > msb) { + lsbthreshold /= 10; + cmaxdigits--; + } + + final int digits = getDecimalDigits(); + final int lsbshift = cmaxdigits - digits; + final int lsd = lsbshift / 4; + + lsbthreshold = 1; + for (int i = 0; i < lsbshift % 4; i++) { + lsbthreshold *= 10; + } + + final int lsb = mant[lsd]; + + if (lsbthreshold <= 1 && digits == 4 * mant.length - 3) { + return super.round(in); + } + + int discarded = in; // not looking at this after this point + final int n; + if (lsbthreshold == 1) { + // look to the next digit for rounding + n = (mant[lsd - 1] / 1000) % 10; + mant[lsd - 1] %= 1000; + discarded |= mant[lsd - 1]; + } else { + n = (lsb * 10 / lsbthreshold) % 10; + discarded |= lsb % (lsbthreshold / 10); + } + + for (int i = 0; i < lsd; i++) { + discarded |= mant[i]; // need to know if there are any discarded bits + mant[i] = 0; + } + + mant[lsd] = lsb / lsbthreshold * lsbthreshold; + + final boolean inc; + switch (getField().getRoundingMode()) { + case ROUND_DOWN: + inc = false; + break; + + case ROUND_UP: + inc = (n != 0) || (discarded != 0); // round up if n!=0 + break; + + case ROUND_HALF_UP: + inc = n >= 5; // round half up + break; + + case ROUND_HALF_DOWN: + inc = n > 5; // round half down + break; + + case ROUND_HALF_EVEN: + inc = + (n > 5) + || (n == 5 && discarded != 0) + || (n == 5 + && discarded == 0 + && ((lsb / lsbthreshold) & 1) == 1); // round half-even + break; + + case ROUND_HALF_ODD: + inc = + (n > 5) + || (n == 5 && discarded != 0) + || (n == 5 + && discarded == 0 + && ((lsb / lsbthreshold) & 1) == 0); // round half-odd + break; + + case ROUND_CEIL: + inc = (sign == 1) && (n != 0 || discarded != 0); // round ceil + break; + + case ROUND_FLOOR: + default: + inc = (sign == -1) && (n != 0 || discarded != 0); // round floor + break; + } + + if (inc) { + // increment if necessary + int rh = lsbthreshold; + for (int i = lsd; i < mant.length; i++) { + final int r = mant[i] + rh; + rh = r / RADIX; + mant[i] = r % RADIX; + } + + if (rh != 0) { + shiftRight(); + mant[mant.length - 1] = rh; + } + } + + // Check for exceptional cases and raise signals if necessary + if (exp < MIN_EXP) { + // Gradual Underflow + getField().setIEEEFlagsBits(DfpField.FLAG_UNDERFLOW); + return DfpField.FLAG_UNDERFLOW; + } + + if (exp > MAX_EXP) { + // Overflow + getField().setIEEEFlagsBits(DfpField.FLAG_OVERFLOW); + return DfpField.FLAG_OVERFLOW; + } + + if (n != 0 || discarded != 0) { + // Inexact + getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT); + return DfpField.FLAG_INEXACT; + } + return 0; + } + + /** {@inheritDoc} */ + @Override + public Dfp nextAfter(Dfp x) { + + final String trapName = "nextAfter"; + + // make sure we don't mix number with different precision + if (getField().getRadixDigits() != x.getField().getRadixDigits()) { + getField().setIEEEFlagsBits(DfpField.FLAG_INVALID); + final Dfp result = newInstance(getZero()); + result.nans = QNAN; + return dotrap(DfpField.FLAG_INVALID, trapName, x, result); + } + + boolean up = false; + Dfp result; + Dfp inc; + + // if this is greater than x + if (this.lessThan(x)) { + up = true; + } + + if (equals(x)) { + return newInstance(x); + } + + if (lessThan(getZero())) { + up = !up; + } + + if (up) { + inc = power10(intLog10() - getDecimalDigits() + 1); + inc = copysign(inc, this); + + if (this.equals(getZero())) { + inc = power10K(MIN_EXP - mant.length - 1); + } + + if (inc.equals(getZero())) { + result = copysign(newInstance(getZero()), this); + } else { + result = add(inc); + } + } else { + inc = power10(intLog10()); + inc = copysign(inc, this); + + if (this.equals(inc)) { + inc = inc.divide(power10(getDecimalDigits())); + } else { + inc = inc.divide(power10(getDecimalDigits() - 1)); + } + + if (this.equals(getZero())) { + inc = power10K(MIN_EXP - mant.length - 1); + } + + if (inc.equals(getZero())) { + result = copysign(newInstance(getZero()), this); + } else { + result = subtract(inc); + } + } + + if (result.classify() == INFINITE && this.classify() != INFINITE) { + getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT); + result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result); + } + + if (result.equals(getZero()) && this.equals(getZero()) == false) { + getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT); + result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result); + } + + return result; + } +} |