/*
*
* 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 macromedia.asc.util;
import java.math.*;
import java.lang.ArithmeticException;
public class Decimal128 {
private static final byte DEC_NAN = 0x01;
private static final byte DEC_SNAN = 0x02;
private static final byte DEC_INF = 0x04;
private static final byte DEC_NEG = 0x08;
private static final byte DEC_SPECIAL = (DEC_NAN | DEC_SNAN | DEC_INF);
private byte flags;
private BigDecimal value;
public static final int MAX_PRECISION = 34;
public static final BigDecimal BigDecimalZERO = new BigDecimal("0");
public static final Decimal128 ZERO = new Decimal128(BigDecimalZERO, (byte)0);
public static final Decimal128 NEGZERO = new Decimal128(BigDecimalZERO, DEC_NEG);
public static final Decimal128 ONE = new Decimal128(new BigDecimal("1"), (byte)0);
public static final Decimal128 NEG1 = new Decimal128(new BigDecimal("-1"), DEC_NEG);
public static final Decimal128 NaN = new Decimal128(BigDecimalZERO, DEC_NAN);
public static final Decimal128 INFINITY = new Decimal128(BigDecimalZERO, DEC_INF);
public static final Decimal128 NEGINFINITY = new Decimal128(BigDecimalZERO, (byte)(DEC_INF | DEC_NEG));
/* private constructors used internally */
private Decimal128() {
flags = 0;
value = BigDecimalZERO; // or maybe leave null
}
private Decimal128(BigDecimal val, byte flagval) {
value = val;
flags = flagval;
}
/* public constructors */
public Decimal128(String str) {
// parse string that's either NaN, SNaN, (+/-)INF[INITY], or valid BigDecimal
flags = 0;
parseDecimalNumber(str.toCharArray(), Decimal128Context.DECIMAL128);
}
public Decimal128(String str, Decimal128Context ctx) {
// parse string that's either NaN, SNaN, (+/-)INF[INITY], or valid BigDecimal
flags = 0;
parseDecimalNumber(str.toCharArray(), ctx);
}
public Decimal128(char in[]) {
// same as string
flags = 0;
parseDecimalNumber(in, Decimal128Context.DECIMAL128);
}
public Decimal128(char in[], Decimal128Context ctx) {
// same as string
flags = 0;
parseDecimalNumber(in, ctx);
}
private void parseDecimalNumber(char in[], Decimal128Context ctx) {
boolean seenS = false;
value = BigDecimalZERO; // until proven otherwise
preparse: do {
for (int i = 0; i < in.length; i++) {
char c = in[i];
if (Character.isDigit(c) || c == '.') {
if (seenS)
break; // invalid input
else
break preparse; // parse as BigDecimal
}
if (c == '+') {
if (seenS)
break; // invalid input
continue; // NaN and infinity can have signs
}
if (c == '-') {
if (seenS)
break; // invalid input
flags = DEC_NEG;
continue;
}
if ((c == 'S') || (c == 's')) {
if (seenS)
break; // invalid input
seenS = true;
continue;
}
if (c == 'N') {
if (((i+2) < in.length) && (in[i+1] == 'a') && (in[i+2] == 'N')) {
flags |= (seenS)? DEC_SNAN : DEC_NAN;
if ((i+3) < in.length) {
// additional characters after NAN.
for (int j = i+3; j < in.length; j++) {
if (!Character.isDigit(in[j])) {
throw new IllegalArgumentException("invalid NaN payload");
}
}
value = new BigDecimal(in, i+3, (in.length - (i+3)), ctx.mathCtx());
// we know scale = 0 since we checked for only digits
if (value.precision() > 33)
value = BigDecimalZERO;
}
return;
}
else break;
}
if (c == 'I') {
if (seenS)
break;
if (((i+8) == in.length) && (in[i+1] == 'n') && (in[i+2] == 'f')
&& (in[i+3] == 'i') && (in[i+4] == 'n')
&& (in[i+5] == 'i') && (in[i+6] == 't') && (in[i+7] == 'y')) {
flags |= DEC_INF;
return; // nothing more to do here
}
// temporarily allow short infinity literals to run Mike's testcases
if (((i+3) == in.length) && (in[i+1] == 'n') && (in[i+2] == 'f')) {
flags |= DEC_INF;
return; // nothing more to do here
}
break; // invalid input
}
}
// only get here if input is invalid
throw new IllegalArgumentException("invalid decimal literal");
} while (false); // dummy loop labeled preparse
// we got here because we saw something that looks like the beginning of a number
value = new BigDecimal(in, ctx.mathCtx());
this.ClampOverflow(ctx);
}
public Decimal128(int val) {
flags = (val >= 0)?0 : DEC_NEG;
value = new BigDecimal(val, MathContext.DECIMAL128);
}
public Decimal128(int val, Decimal128Context ctx) {
flags = (val >= 0)?0 : DEC_NEG;
value = new BigDecimal(val, ctx.mathCtx());
}
public Decimal128(double val) {
flags = (val >= 0)?0 : DEC_NEG;
value = BigDecimalZERO; // until proven otherwise
if (Double.isNaN(val)) {
flags |= DEC_NAN;
}
else if (Double.isInfinite(val)) {
flags |= DEC_INF; // sign already set above.
}
else
value = new BigDecimal(val, MathContext.DECIMAL128);
}
public Decimal128(double val, Decimal128Context ctx) {
flags = (val >= 0)?0 : DEC_NEG;
value = BigDecimalZERO; // until proven otherwise
if (Double.isNaN(val)) {
flags |= DEC_NAN;
}
else if (Double.isInfinite(val)) {
flags |= DEC_INF; // sign already set above.
}
else
value = new BigDecimal(val, ctx.mathCtx());
}
public Decimal128(long val) {
flags = (val >= 0)?0 : DEC_NEG;
value = new BigDecimal(val, MathContext.DECIMAL128);
}
public Decimal128(long val, Decimal128Context ctx) {
flags = (val >= 0)?0 : DEC_NEG;
value = new BigDecimal(val, ctx.mathCtx());
}
/* operations to/from IEEE representation */
private static final int mask[] = {0x000, 0x001, 0x003, 0x007, 0x00F,
0x01F, 0x03F, 0x07F, 0x0FF, 0x1FF, 0x3FF}; // can't declare static inside IEEERep
private class IEEERep {
// class to manipulate the representation of a IEEE 128 bit floating point
// currently generates format specified by http://www2.hursley.ibm.com/decimal/decbits.html
int data[] = {0, 0, 0, 0}; // data[0] is msb, containing sign, combo field, etc.
// data is initialized to 0, methods below OR in values taking advantage of this fact.
boolean negative=false, qNaN=false, sNaN=false, infinite=false, finalized=false;
int exponent;
short topDigit;
int currentDatum, currentShift, totalDigits;
/* *****************************************************************************
* Routines for taking apart IEEE representation
*******************************************************************************/
IEEERep() { // use this constructor to build a representation
topDigit = 0;
// initialize state for writing unit
currentDatum = 3;
currentShift = 0;
totalDigits = 0;
}
void setNeg() {
negative = true;
}
void setNaN(boolean signalling) {
if (signalling)
sNaN = true;
else qNaN = true;
}
void setInfinite() {
infinite = true;
}
void setExponent(int exp) { // exp is biased exponent
exponent = exp;
}
void addUnit (short unit) {
// add 3 more digits to the number. Digits are added in groups of 3, least significant
// first, the value passed to this routine is a number between 0 and 999. If 33 digits
// have already been added, it is an error if unit > 9.
if (totalDigits == 34)
throw new IllegalArgumentException("trying to add more than 34 digits to IEEE128");
if (totalDigits == 33) {
if (unit > 9)
throw new IllegalArgumentException("trying to add more than 34 digits to IEEE128");
topDigit = unit;
totalDigits++;
return;
}
totalDigits += 3;
int dpd = DenselyPackedDecimal.Bin2DPD(unit);
if (currentShift <= 22) {
// dpd fits into currentDatum completely
data[currentDatum] |= dpd << currentShift;
currentShift += 10;
}
else {
// bits overlap next data word
int onThis = 32 - currentShift; // will be between 0 and 9
if (onThis != 0)
data[currentDatum] |= dpd << currentShift;
data[currentDatum-1] |= (dpd >>> onThis);
currentDatum--;
currentShift = 10 - onThis;
}
}
byte[] getRep() {
if (!finalized) {
int combo = 0;
if (negative)
data[0] |= 0x80000000;
if (infinite)
combo = 0x1E;
else if (sNaN || qNaN) {
combo = 0x1F;
if (sNaN)
exponent = 0x800; // high order bit on in continuation field
else
exponent = 0;
}
else {
// finite number
if (topDigit < 8)
combo = topDigit | ((exponent >>> 9) & 0x18);
else
combo = 0x18 | (((exponent >>> 12) & 0x3) << 1) | (topDigit - 8);
}
data[0] |= (combo <<26) | ((exponent & 0xFFF) << 14);
finalized = true;
}
byte[] result = new byte[16];
int ndx = 0;
for (int i = 0; i < 16; i+= 4) {
int datum = data[ndx++];
result[i] = (byte)(datum >>> 24);
result[i+1] = (byte)((datum >>> 16) & 0xFF);
result[i+2] = (byte)((datum >>> 8) & 0xFF);
result[i+3] = (byte)(datum & 0xFF);
}
return result;
}
/* *****************************************************************************
* Routines for taking apart IEEE representation
*******************************************************************************/
int currentBit;
IEEERep(byte[] rep) { // use this constructor to take apart a representation
int ndx = 0;
for (int i = 0; i < 16; i+= 4) {
data[ndx++] = ((rep[i] & 0xFF)<<24) + ((rep[i+1] & 0xFF)<<16) +
((rep[i+2] & 0xFF) <<8) + (rep[i+3] & 0xFF);
}
if ((data[0] & 0x80000000) != 0)
negative = true;
int combo = (data[0] >> 26) & 0x1F;
if (combo == 0x1E)
infinite = true;
else if (combo == 0x1F) {
if ((data[0] & 0x02000000) != 0)
sNaN = true;
else qNaN = true;
}
else {
// finite number
int expmsb;
if ((combo & 0x18) == 0x18) {
expmsb = (combo >> 1) & 0x3;
topDigit = (short)(8 + (combo & 0x01));
}
else {
expmsb = combo >>> 3;
topDigit = (short)(combo & 0x7);
}
exponent = ((data[0] >>> 14) & 0xFFF) | (expmsb << 12);
}
// initialize state for reading digits
currentDatum = -1;
}
short getUnit() {
// returns bases 1000 units from most significant to least
short result = 0;
if (currentDatum == -1) {
currentDatum = 0;
currentBit = 14;
return topDigit;
}
else if (currentDatum > 3) {
throw new IllegalArgumentException("trying to read more than 34 digits from IEEE128");
}
else {
if (currentBit >= 10) {
// all bits are in the current Datum
result = (short)((data[currentDatum] >>> (currentBit - 10)) & 0x3FF);
currentBit -= 10;
}
else {
// bits overlap into next Datum
int overlap = 10 - currentBit;
if (currentBit != 0) {
result = (short)((data[currentDatum] & mask[currentBit]) << overlap);
}
result |= (short)((data[++currentDatum] >>> (32-overlap)));
currentBit = 32- overlap;
}
}
return DenselyPackedDecimal.DPD2Bin(result);
}
boolean isInfinite() {
return infinite;
}
boolean isNaN() {
return (qNaN | sNaN);
}
boolean isSNaN() {
return (sNaN);
}
boolean isNegative() {
return negative;
}
int getExponent() {
return exponent;
}
} // IEEERep class
private static final int DECIMAL128_Pmax = 34;
private static final int DECIMAL128_Emax = 6144;
private static final int DECIMAL128_Emin = -6143;
private static final int DECIMAL128_Bias = 6176;
// highest biased exponent (Elimit-1)
private static final int DECIMAL128_Ehigh = (DECIMAL128_Emax+DECIMAL128_Bias-DECIMAL128_Pmax+1);
public byte[] toIEEE(Decimal128Context ctx) {
// returns 16 bytes of IEEE decimal128, big endian
int exp; // holds biased exponent
short dval;
IEEERep ieee = new IEEERep();
if ((flags & DEC_NEG) != 0)
ieee.setNeg();
do { // dummy loop to allow break
if ((flags & DEC_SPECIAL) == 0) {
char[] coefficient = value.abs().unscaledValue().toString().toCharArray();
int exponent = -value.scale(); // it appears backward from decNumber
int digits = coefficient.length;
int ae = exponent + digits - 1;
if ((digits > DECIMAL128_Pmax)
|| (ae > DECIMAL128_Emax)
|| (ae < DECIMAL128_Emin)) {
// possibly out of range. Do things to get it in range
Decimal128 n2 = this.add(Decimal128.ZERO, Decimal128Context.DECIMAL128);
if ((n2.flags & DEC_INF) != 0) {
ieee.setInfinite();
break;
}
coefficient = n2.value.abs().unscaledValue().toString().toCharArray();
exponent = -n2.value.scale();
digits = coefficient.length;
}
if (value.compareTo(BigDecimalZERO) == 0) {
if (exponent < -DECIMAL128_Bias) {
exp = 0;
ctx.status |= Decimal128Context.DEC_Clamped;
}
else {
exp = exponent + DECIMAL128_Bias; // bias exponent
if (exp > DECIMAL128_Ehigh) {
exp = DECIMAL128_Ehigh;
ctx.status |= Decimal128Context.DEC_Clamped;
}
}
ieee.setExponent(exp);
}
else { // nonzero
exp = exponent + DECIMAL128_Bias;
if (exp > DECIMAL128_Ehigh) {
// We want to limit exponent to Ehigh, even if we add trailing
// 0's to the coefficient
int shift = exp - DECIMAL128_Ehigh;
int k;
exp = DECIMAL128_Ehigh;
ctx.status |= Decimal128Context.DEC_Clamped;
char newCoef[] = new char[digits + shift];
for (k = 0; k < digits; k++) {
newCoef[k] = coefficient[k];
}
for (k = digits; k < (digits + shift); k++) {
newCoef[k] = '0';
}
coefficient = newCoef;
digits += shift;
}
ieee.setExponent(exp);
for (int i = digits-1; i >= 0; i -= 3) {
switch (i) {
case 0:
dval = (short)(coefficient[0] - '0');
break;
case 1:
dval = (short)(10 * (coefficient[0] - '0') + (coefficient[1] - '0'));
break;
default:
dval = (short)(100 * (coefficient[i-2] - '0')
+ 10 * (coefficient[i-1] - '0') + (coefficient[i] - '0'));
}
ieee.addUnit(dval);
}
}
}
else {
if ((flags & DEC_INF) != 0) {
ieee.setInfinite();
}
else {
ieee.setNaN((flags & DEC_SNAN) != 0);
if (value != BigDecimalZERO) {
char[] payload = value.abs().unscaledValue().toString().toCharArray();
int digits = payload.length;
ieee.setExponent(0);
for (int i = digits-1; i >= 0; i -= 3) {
switch (i) {
case 0:
dval = (short)(payload[0] - '0');
break;
case 1:
dval = (short)(10 * (payload[0] - '0') + (payload[1] - '0'));
break;
default:
dval = (short)(100 * (payload[i-2] - '0')
+ 10 * (payload[i-1] - '0') + (payload[i] - '0'));
}
ieee.addUnit(dval);
}
}
}
}
} while (false);
return ieee.getRep();
}
public byte[] toIEEE() {
return toIEEE(Decimal128Context.DECIMAL128);
}
public ByteList toByteList(ByteList result) {
byte rep[] = toIEEE();
if (result == null)
result = new ByteList(16);
result.set(rep, 16);
return result;
}
/* constructor to go from IEEE representation */
public Decimal128(ByteList rep) {
byte reparray[] = rep.toByteArray();
initFromIEEE(reparray);
}
public Decimal128(byte[] rep) {
initFromIEEE(rep);
}
private void initFromIEEE(byte[] rep) {
// must be 16 bytes holding an IEEE decimal128, big endian
if (rep.length != 16)
throw new IllegalArgumentException("Decimal128 needs 16 bytes for representation");
IEEERep ieee = new IEEERep(rep);
if (ieee.isNegative())
flags = DEC_NEG;
else
flags = 0;
if (ieee.isNaN()) {
value = BigDecimalZERO; // some day support fancier NaNs
if (ieee.isSNaN())
flags |= DEC_SNAN;
else
flags |= DEC_NAN;
// and fall through to parse payload
}
else if (ieee.isInfinite()) {
value = BigDecimalZERO; // so it's not uninitialized
flags |= DEC_INF;
return;
}
byte thousand[] = {0x03, (byte)0xE8};
// Why isn't there a public constructor for BigInteger that takes an int or long or something?
BigInteger THOUSAND = new BigInteger(thousand);
BigInteger coefficient = BigInteger.ZERO;
short unit;
boolean leadingZeroes = true;
for (int i = 0; i < 12; i++) {
unit = ieee.getUnit();
if (!leadingZeroes || (unit != 0 )) {
if (leadingZeroes) {
leadingZeroes = false;
}
else {
coefficient = THOUSAND.multiply(coefficient);
}
byte unitBytes[] = {(byte)(unit >>> 8), (byte)(unit & 0xFF)};
BigInteger bigUnit = new BigInteger(unitBytes);
coefficient = coefficient.add(bigUnit);
}
}
int scale = (ieee.isNaN())? 0 : (-(ieee.getExponent() - DECIMAL128_Bias));
value = new BigDecimal(coefficient, scale);
if ((flags & (DEC_NEG | DEC_NAN | DEC_SNAN)) == DEC_NEG) {
value = value.negate(MathContext.DECIMAL128); // don't negate payload
}
}
/* to other numeric types */
public int intValue() {
if ((this.flags & DEC_SPECIAL) != 0) {
throw new ArithmeticException("no valid integer value");
}
return this.value.intValue();
}
public long longValue() {
if ((this.flags & DEC_SPECIAL) != 0) {
throw new ArithmeticException("no valid long value");
}
return this.value.longValue();
}
public double doubleValue() {
if ((flags & (DEC_NAN | DEC_SNAN)) != 0)
return Double.NaN;
if ((flags & DEC_INF) != 0) {
if ((flags & DEC_NEG) != 0)
return Double.NEGATIVE_INFINITY;
else
return Double.POSITIVE_INFINITY;
}
return this.value.doubleValue();
}
public String toString() {
StringBuilder result = new StringBuilder();
if (((flags & DEC_SPECIAL) != 0) && ((flags & DEC_NEG) != 0))
result.append('-');
if ((flags & (DEC_NAN | DEC_SNAN)) != 0) {
if ((flags & DEC_SNAN) != 0) {
result.append("S");
}
result.append("NaN");
if (value.compareTo(BigDecimalZERO) != 0) {
result.append(value.toString());
}
}
else if ((flags & DEC_INF) != 0) {
result.append("Infinity");
}
else {
// a number, but BigDecimal doesn't have -0
if (((flags & DEC_NEG) != 0) && (value.compareTo(BigDecimalZERO) == 0)) {
result.append('-');
}
result.append(value.toString());
}
return result.toString();
}
public String toEngineeringString() {
if ((flags & (DEC_NAN | DEC_SNAN)) != 0) {
return "NaN"; // what about number after NaN
}
else if ((flags & DEC_INF) != 0) {
return "Infinity";
}
else {
return value.toEngineeringString();
}
}
public boolean isNaN() {
return ((flags & (DEC_NAN | DEC_SNAN)) != 0);
}
public boolean isQNaN() {
return ((flags & DEC_NAN) != 0);
}
public boolean isSNaN() {
return ((flags & DEC_SNAN) != 0);
}
public boolean isFinite() {
return ((flags & DEC_INF) == 0);
}
public boolean isNegative() {
return ((flags & DEC_NEG) != 0);
}
/* helper functions for arithmetic operations */
private Decimal128 opNaNs(Decimal128 operand, Decimal128Context ctx) {
// either this or operand is NaN or SNan
Decimal128 lhs = this;
if ((this.flags & DEC_SNAN) != 0) {
ctx.status |= Decimal128Context.DEC_Invalid_operation | Decimal128Context.DEC_sNaN;
}
else if (operand != null && ((operand.flags & DEC_SNAN) != 0)) {
lhs = operand;
ctx.status |= Decimal128Context.DEC_Invalid_operation | Decimal128Context.DEC_sNaN;
}
else if ((this.flags & DEC_NAN) == 0) {
lhs = operand;
}
Decimal128 result = new Decimal128(lhs.value, lhs.flags);
// we've signaled by setting status, now clear SNAN to NAN if necessary
result.flags &= ~DEC_SNAN;
result.flags |= DEC_NAN;
return result;
}
private void ClampOverflow(Decimal128Context ctx) {
// check that scale is within valid range, modify as required to get into
// range or set to proper infinity.
if ((flags & DEC_SPECIAL) != 0) {
return; // special ones are not out of range
}
// some definitions to keep code below readable
// scale of BigDecimals are negative of exponents in Decimal128s
BigDecimal resultVal;
final int MINSCALE = -(DECIMAL128_Emax - DECIMAL128_Pmax + 1);
final int MAXSCALE = DECIMAL128_Bias;
int scale = value.scale();
if (scale > MAXSCALE) {
// see if we can change scale withing ctx.precision
int numPrecision = value.precision();
// see if we can decrease the precision to lower the scale
// but can't make precision less than 1
int delta = scale - MAXSCALE;
if ((numPrecision - delta) >= 1) {
resultVal = value.setScale(MAXSCALE, ctx.getRoundingMode());
}
else {
// can't fit. Return 0 or smallest num, which ever is closer
/* 754r says return smallest value only if we would round to it
* if we had infinite precision, otherwise return 0.
*
* TODO: test for possible return of smallest possible non-zero value
*/
value = new BigDecimal(BigInteger.ZERO, MAXSCALE);
return;
}
} // scale > MAXSCALE
else if (scale < MINSCALE) {
int numPrecision = value.precision();
// see if we can increase the precision to raise the scale
// but can't make precision greater than ctx.precision
int delta = MINSCALE - scale;
if ((numPrecision + delta) <= ctx.getPrecision() ||
// special case for 0 with a big scale
(value.compareTo(BigDecimalZERO) == 0)) {
resultVal = value.setScale(MINSCALE, ctx.getRoundingMode());
}
else {
// can't fit. Return Infinity or largest number, depending on rounding
RoundingMode mode = ctx.getRoundingMode();
if ((flags & DEC_NEG) != 0) {
// negative number
if (!((mode == RoundingMode.CEILING) || (mode == RoundingMode.DOWN))) {
flags = DEC_NEG | DEC_INF;
value = BigDecimalZERO;
return;
}
}
else {
// positive number
if (!((mode == RoundingMode.FLOOR) || (mode == RoundingMode.DOWN))) {
flags = DEC_INF;
value = BigDecimalZERO;
return;
}
}
// otherwise fall through to here and return largest possible number of this precision
byte nine[] = {(byte)9};
BigInteger NINE = new BigInteger(nine);
BigInteger total = NINE;
for (int i = 1; i < ctx.getPrecision(); i++) {
total = NINE.add(total.multiply(BigInteger.TEN));
}
resultVal = new BigDecimal(total, MINSCALE);
if ((flags & DEC_NEG) != 0) {
resultVal = resultVal.negate(ctx.mathCtx());
}
}
}
else
return; // it's in range
value = resultVal;
}
/* arithmetic operations */
public Decimal128 abs() {
return this.abs(Decimal128Context.DECIMAL128);
}
public Decimal128 abs(Decimal128Context ctx) {
Decimal128 result = new Decimal128();
result.flags = this.flags; // in case it's NaN or infinity
result.flags &= ~DEC_NEG;
result.value = this.value.abs(ctx.mathCtx());
return result;
}
public Decimal128 add(Decimal128 addend) {
return this.add(addend, Decimal128Context.DECIMAL128);
}
public Decimal128 add(Decimal128 addend, Decimal128Context ctx) {
Decimal128 result;
int specialbits = ((addend.flags | this.flags) & DEC_SPECIAL);
if (specialbits != 0) {
if ((specialbits & (DEC_NAN | DEC_SNAN)) != 0) {
result = opNaNs(addend, ctx); // one or more NaNs
return result;
}
else {//one or two infinities
result = new Decimal128();
if ((this.flags & DEC_INF) != 0) {
if ((addend.flags & DEC_INF) != 0) {
if (((this.flags ^ addend.flags) & DEC_NEG) != 0) { // different signs
ctx.status |= Decimal128Context.DEC_Invalid_operation;
result.flags = DEC_NAN; // should I do this?
return result;
}
}
result.flags = this.flags; // get sign and infinity flag from me
}
else { // augend is infinite, take its sign
result.flags = addend.flags;
}
return result;
}
}
result = new Decimal128();
result.value = this.value.add(addend.value, ctx.mathCtx());
if (result.value.compareTo(BigDecimalZERO) < 0)
result.flags |= DEC_NEG;
result.ClampOverflow(ctx);
return result;
}
public boolean equals(Decimal128 num) {
// return true if I'm > num
Decimal128 comp = this.compareTo(num, Decimal128Context.DECIMAL128);
if (comp == ZERO)
return true;
else
return false;
}
public boolean greaterThan(Decimal128 num) {
// return true if I'm > num
Decimal128 comp = this.compareTo(num, Decimal128Context.DECIMAL128);
if (comp == ONE)
return true;
else
return false;
}
public boolean greaterThanOrEqual(Decimal128 num) {
// return true if I'm >= num
Decimal128 comp = this.compareTo(num, Decimal128Context.DECIMAL128);
if (comp == ONE || comp == ZERO)
return true;
else
return false;
}
public boolean lessThan(Decimal128 num) {
// return true if I'm < num
Decimal128 comp = this.compareTo(num, Decimal128Context.DECIMAL128);
if (comp == NEG1)
return true;
else
return false;
}
public boolean lessThanOrEqual(Decimal128 num) {
// return true if I'm <= num
Decimal128 comp = this.compareTo(num, Decimal128Context.DECIMAL128);
if (comp == NEG1 || comp == ZERO)
return true;
else
return false;
}
// can't return just an int because could be NaN
private Decimal128 compareTo(Decimal128 val, Decimal128Context ctx) {
if (((this.flags | val.flags) & (DEC_NAN | DEC_SNAN)) != 0)
return opNaNs(val, ctx);
if ((this.flags & DEC_INF) != 0) {
// I'm infinite (+|-)
if ((this.flags & DEC_NEG) != 0) {
// I'm negative infinity, less than anything except another negative infinity
if ((val.flags & (DEC_INF | DEC_NEG)) == (DEC_INF | DEC_NEG)) {
ctx.status |= Decimal128Context.DEC_Invalid_operation;
return NaN;
}
return NEG1;
}
else { // I'm infinity, greater than anyone except another infinity
if ((val.flags & (DEC_INF | DEC_NEG)) == (DEC_INF)) {
ctx.status |= Decimal128Context.DEC_Invalid_operation;
return NaN;
}
return ONE;
}
}
else if ((val.flags & DEC_INF) != 0) {
// val is infinite and I'm not
if ((val.flags & DEC_NEG) != 0)
return ONE;
else
return NEG1;
}
else {
switch(this.value.compareTo(val.value)) {
case 1:
return ONE;
case -1:
return NEG1;
default:
return ZERO;
}
}
}
public Decimal128 divide(Decimal128 divisor) {
return this.divide(divisor, Decimal128Context.DECIMAL128);
}
public Decimal128 divide(Decimal128 divisor, Decimal128Context ctx) {
Decimal128 result;
boolean differentsigns = ((this.flags ^ divisor.flags) & DEC_NEG) != 0;
int specialbits = ((divisor.flags | this.flags) & DEC_SPECIAL);
if (specialbits != 0) {
if ((specialbits & (DEC_NAN | DEC_SNAN)) != 0) {
return opNaNs(divisor, ctx); // one or more NaNs
}
else {//one or two infinities
result = new Decimal128();
if ((this.flags & DEC_INF) != 0) {
// I'm infinite
if ((divisor.flags & DEC_INF) != 0) {
ctx.status |= Decimal128Context.DEC_Invalid_operation;
result.flags = DEC_NAN;
// if (differentsigns) result.flags |= DEC_NEG;
} else {
result.flags = DEC_INF;
if (differentsigns) result.flags |= DEC_NEG;
}
}
else { // divisor is infinite and I'm not, result is (+|-)0
result.value = BigDecimalZERO.setScale(DECIMAL128_Bias); // smallest 0
if (differentsigns) result.flags = DEC_NEG;
}
return result;
}
}
result = new Decimal128();
// now check for divide by 0
if (divisor.value.compareTo(BigDecimalZERO) == 0) { // don't use equals here
// divisor is 0, answer is (+|-)infinity unless I'm also 0
if (this.value.compareTo(BigDecimalZERO) == 0) {
// oops, 0/0 = NaN
ctx.status |= Decimal128Context.DEC_Invalid_operation;
result.flags = DEC_NAN;
}
else {
result.value = BigDecimalZERO;
result.flags |= DEC_INF;
}
}
else result.value = this.value.divide(divisor.value, ctx.mathCtx());
if (differentsigns)
result.flags |= DEC_NEG;
result.ClampOverflow(ctx);
return result;
}
public Decimal128 multiply(Decimal128 multiplicand) {
return this.multiply(multiplicand, Decimal128Context.DECIMAL128);
}
public Decimal128 multiply(Decimal128 multiplicand, Decimal128Context ctx) {
Decimal128 result;
boolean differentsigns = ((this.flags ^ multiplicand.flags) & DEC_NEG) != 0;
int specialbits = ((multiplicand.flags | this.flags) & DEC_SPECIAL);
if (specialbits != 0) {
if ((specialbits & (DEC_NAN | DEC_SNAN)) != 0) {
result = opNaNs(multiplicand, ctx); // one or more NaNs
return result;
}
else { //one or two infinities
result = new Decimal128();
if ((this.flags & DEC_INF) != 0) {
// I'm infinite, all is well unless he is 0
if (((multiplicand.flags & DEC_INF) == 0) && (multiplicand.value.compareTo(BigDecimalZERO) == 0)) {
ctx.status |= Decimal128Context.DEC_Invalid_operation; // should I do this?
result.flags = DEC_NAN;
return result;
}
}
else {
// He's infinite, I'd better not be 0
if (this.value.compareTo(BigDecimalZERO) == 0) {
ctx.status |= Decimal128Context.DEC_Invalid_operation; // should I do this?
result.flags = DEC_NAN;
return result;
}
}
result.flags = DEC_INF;
if (differentsigns) result.flags |= DEC_NEG;
return result;
}
}
result = new Decimal128();
result.value = this.value.multiply(multiplicand.value, ctx.mathCtx());
if (differentsigns) result.flags |= DEC_NEG;
result.ClampOverflow(ctx);
return result;
}
public Decimal128 remainder(Decimal128 divisor) {
return this.remainder(divisor, Decimal128Context.DECIMAL128);
}
public Decimal128 remainder(Decimal128 divisor, Decimal128Context ctx) {
Decimal128 result;
boolean differentsigns = ((this.flags ^ divisor.flags) & DEC_NEG) != 0;
int specialbits = ((divisor.flags | this.flags) & DEC_SPECIAL);
if (specialbits != 0) {
if ((specialbits & (DEC_NAN | DEC_SNAN)) != 0) {
return opNaNs(divisor, ctx); // one or more NaNs
}
else {//one or two infinities
result = new Decimal128();
if (((this.flags & DEC_INF) != 0) ||
((divisor.flags & DEC_INF) == 0) && (divisor.value.compareTo(BigDecimalZERO) == 0)) {
// Dividend infinite or divisor 0
ctx.status |= Decimal128Context.DEC_Invalid_operation;
result.flags = DEC_NAN;
}
else { // d % infinity == d
result.value = this.value;
if (differentsigns) result.flags = DEC_NEG;
}
return result;
}
}
result = new Decimal128();
// finite % 0 = NaN
if (divisor.value.compareTo(BigDecimalZERO) == 0) {
// doesn't matter whether this.value is non-zero
result.flags = DEC_NAN;
}
else {
// 0 % finite = 0
if (this.value.compareTo(BigDecimalZERO) == 0) {
result.value = this.value;
}
else {
// for reasons Mike says are "by design", it can blow up
try {
if (ctx.mathCtx().getPrecision() == 34)
result.value = this.value.remainder(divisor.value, ctx.mathCtx());
else {
// compute remainder in full precision, round answer to smaller precision
result.value = this.value.remainder(divisor.value, Decimal128Context.DECIMAL128.mathCtx());
result.value = result.value.add(BigDecimalZERO, ctx.mathCtx());
}
} catch (ArithmeticException e) {
result.flags = DEC_NAN;
}
}
}
if (differentsigns)
result.flags |= DEC_NEG;
result.ClampOverflow(ctx);
return result;
}
public Decimal128 subtract(Decimal128 subtrahend) {
return this.subtract(subtrahend, Decimal128Context.DECIMAL128);
}
public Decimal128 subtract(Decimal128 subtrahend, Decimal128Context ctx) {
Decimal128 result;
int specialbits = ((subtrahend.flags | this.flags) & DEC_SPECIAL);
if (specialbits != 0) {
if ((specialbits & (DEC_NAN | DEC_SNAN)) != 0) {
result = opNaNs(subtrahend, ctx); // one or more NaNs
return result;
}
else {//one or two infinities
result = new Decimal128();
if ((this.flags & DEC_INF) != 0) {
// I'm infinite, only bad value is subtracting one of same sign
if ((subtrahend.flags & DEC_INF) != 0) {
if (((this.flags ^ subtrahend.flags) & DEC_NEG) == 0) { // same signs
ctx.status |= Decimal128Context.DEC_Invalid_operation;
result.flags = DEC_NAN; // should I do this?
return result;
}
}
result.flags = this.flags; // get sign and infinity flag from me
}
else { // augend is infinite and I'm not, take its sign
result.flags = subtrahend.flags;
result.flags ^= DEC_NEG; // toggle the sign bit
}
return result;
}
}
result = new Decimal128();
result.value = this.value.subtract(subtrahend.value, ctx.mathCtx());
if (result.value.compareTo(BigDecimalZERO) < 0)
result.flags |= DEC_NEG;
result.ClampOverflow(ctx);
return result;
}
/* override method from Object */
public int hashCode() {
return value.hashCode() ^ flags;
}
}