DfpDec.java
/*
* 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
*
* https://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.
*/
/*
* This is not the original file distributed by the Apache Software Foundation
* It has been modified by the Hipparchus project
*/
package org.hipparchus.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.
*/
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())) {
getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result);
}
return result;
}
}