Package macromedia.asc.util

Source Code of macromedia.asc.util.Decimal128$IEEERep

/*
*
*  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;
  }
}
TOP

Related Classes of macromedia.asc.util.Decimal128$IEEERep

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.