diff --git a/CHANGELOG.md b/CHANGELOG.md index da536f1ba..9399201ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Fixed - AS1/2 script export to single file maintains script order +- #1088 ECMA Number to string conversion ## [13.0.1] - 2021-02-09 ### Fixed diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java index b3a541ecd..2f8f47787 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/action/model/DirectValueActionItem.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.action.model; import com.jpexs.decompiler.flash.SourceGeneratorLocalData; @@ -182,8 +183,7 @@ public class DirectValueActionItem extends ActionItem implements SimpleValue { return writer.appendWithData(((RegisterNumber) value).translate(), srcData); } - //return writer.append(value.toString()); - return writer.append(EcmaScript.toString(value, true)); // todo, use this line + return writer.append(EcmaScript.toString(value)); } @Override diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaFloatingDecimal.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaFloatingDecimal.java deleted file mode 100644 index 2e042bc20..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaFloatingDecimal.java +++ /dev/null @@ -1,1647 +0,0 @@ -/* - * Copyright (C) 2010-2021 JPEXS, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ -package com.jpexs.decompiler.flash.ecma; - -import java.util.regex.Pattern; - -public class EcmaFloatingDecimal { - - boolean isExceptional; - - boolean isNegative; - - int decExponent; - - char digits[]; - - int nDigits; - - int bigIntExp; - - int bigIntNBits; - - boolean mustSetRoundDir = false; - - boolean fromHex = false; - - int roundDir = 0; // set by doubleValue - - private EcmaFloatingDecimal(boolean negSign, int decExponent, char[] digits, int n, boolean e) { - isNegative = negSign; - isExceptional = e; - this.decExponent = decExponent; - this.digits = digits; - this.nDigits = n; - } - - /* - * Constants of the implementation - * Most are IEEE-754 related. - * (There are more really boring constants at the end.) - */ - static final long signMask = 0x8000000000000000L; - - static final long expMask = 0x7ff0000000000000L; - - static final long fractMask = ~(signMask | expMask); - - static final int expShift = 52; - - static final int expBias = 1023; - - static final long fractHOB = (1L << expShift); // assumed High-Order bit - - static final long expOne = ((long) expBias) << expShift; // exponent of 1.0 - - static final int maxSmallBinExp = 62; - - static final int minSmallBinExp = -(63 / 3); - - static final int maxDecimalDigits = 15; - - static final int maxDecimalExponent = 308; - - static final int minDecimalExponent = -324; - - static final int bigDecimalExponent = 324; // i.e. abs(minDecimalExponent) - - static final long highbyte = 0xff00000000000000L; - - static final long highbit = 0x8000000000000000L; - - static final long lowbytes = ~highbyte; - - static final int singleSignMask = 0x80000000; - - static final int singleExpMask = 0x7f800000; - - static final int singleFractMask = ~(singleSignMask | singleExpMask); - - static final int singleExpShift = 23; - - static final int singleFractHOB = 1 << singleExpShift; - - static final int singleExpBias = 127; - - static final int singleMaxDecimalDigits = 7; - - static final int singleMaxDecimalExponent = 38; - - static final int singleMinDecimalExponent = -45; - - static final int intDecimalDigits = 9; - - - /* - * count number of bits from high-order 1 bit to low-order 1 bit, - * inclusive. - */ - private static int countBits(long v) { - // - // the strategy is to shift until we get a non-zero sign bit - // then shift until we have no bits left, counting the difference. - // we do byte shifting as a hack. Hope it helps. - // - if (v == 0L) { - return 0; - } - - while ((v & highbyte) == 0L) { - v <<= 8; - } - while (v > 0L) { // i.e. while ((v&highbit) == 0L ) - v <<= 1; - } - - int n = 0; - while ((v & lowbytes) != 0L) { - v <<= 8; - n += 8; - } - while (v != 0L) { - v <<= 1; - n += 1; - } - return n; - } - - /* - * Keep big powers of 5 handy for future reference. - */ - private static FDBigInt b5p[]; - - private static synchronized FDBigInt big5pow(int p) { - assert p >= 0 : p; // negative power of 5 - if (b5p == null) { - b5p = new FDBigInt[p + 1]; - } else if (b5p.length <= p) { - FDBigInt t[] = new FDBigInt[p + 1]; - System.arraycopy(b5p, 0, t, 0, b5p.length); - b5p = t; - } - if (b5p[p] != null) { - return b5p[p]; - } else if (p < small5pow.length) { - return b5p[p] = new FDBigInt(small5pow[p]); - } else if (p < long5pow.length) { - return b5p[p] = new FDBigInt(long5pow[p]); - } else { - // construct the value. - // recursively. - int q, r; - // in order to compute 5^p, - // compute its square root, 5^(p/2) and square. - // or, let q = p / 2, r = p -q, then - // 5^p = 5^(q+r) = 5^q * 5^r - q = p >> 1; - r = p - q; - FDBigInt bigq = b5p[q]; - if (bigq == null) { - bigq = big5pow(q); - } - if (r < small5pow.length) { - return (b5p[p] = bigq.mult(small5pow[r])); - } else { - FDBigInt bigr = b5p[r]; - if (bigr == null) { - bigr = big5pow(r); - } - return (b5p[p] = bigq.mult(bigr)); - } - } - } - - // - // a common operation - // - private static FDBigInt multPow52(FDBigInt v, int p5, int p2) { - if (p5 != 0) { - if (p5 < small5pow.length) { - v = v.mult(small5pow[p5]); - } else { - v = v.mult(big5pow(p5)); - } - } - if (p2 != 0) { - v.lshiftMe(p2); - } - return v; - } - - // - // another common operation - // - private static FDBigInt constructPow52(int p5, int p2) { - FDBigInt v = new FDBigInt(big5pow(p5)); - if (p2 != 0) { - v.lshiftMe(p2); - } - return v; - } - - /* - * Make a floating double into a FDBigInt. - * This could also be structured as a FDBigInt - * constructor, but we'd have to build a lot of knowledge - * about floating-point representation into it, and we don't want to. - * - * AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES - * bigIntExp and bigIntNBits - * - */ - private FDBigInt doubleToBigInt(double dval) { - long lbits = Double.doubleToLongBits(dval) & ~signMask; - int binexp = (int) (lbits >>> expShift); - lbits &= fractMask; - if (binexp > 0) { - lbits |= fractHOB; - } else { - assert lbits != 0L : lbits; // doubleToBigInt(0.0) - binexp += 1; - while ((lbits & fractHOB) == 0L) { - lbits <<= 1; - binexp -= 1; - } - } - binexp -= expBias; - int nbits = countBits(lbits); - /* - * We now know where the high-order 1 bit is, - * and we know how many there are. - */ - int lowOrderZeros = expShift + 1 - nbits; - lbits >>>= lowOrderZeros; - - bigIntExp = binexp + 1 - nbits; - bigIntNBits = nbits; - return new FDBigInt(lbits); - } - - /* - * Compute a number that is the ULP of the given value, - * for purposes of addition/subtraction. Generally easy. - * More difficult if subtracting and the argument - * is a normalized a power of 2, as the ULP changes at these points. - */ - private static double ulp(double dval, boolean subtracting) { - long lbits = Double.doubleToLongBits(dval) & ~signMask; - int binexp = (int) (lbits >>> expShift); - double ulpval; - if (subtracting && (binexp >= expShift) && ((lbits & fractMask) == 0L)) { - // for subtraction from normalized, powers of 2, - // use next-smaller exponent - binexp -= 1; - } - if (binexp > expShift) { - ulpval = Double.longBitsToDouble(((long) (binexp - expShift)) << expShift); - } else if (binexp == 0) { - ulpval = Double.MIN_VALUE; - } else { - ulpval = Double.longBitsToDouble(1L << (binexp - 1)); - } - if (subtracting) { - ulpval = -ulpval; - } - - return ulpval; - } - - /* - * Round a double to a float. - * In addition to the fraction bits of the double, - * look at the class instance variable roundDir, - * which should help us avoid double-rounding error. - * roundDir was set in hardValueOf if the estimate was - * close enough, but not exact. It tells us which direction - * of rounding is preferred. - */ - float stickyRound(double dval) { - long lbits = Double.doubleToLongBits(dval); - long binexp = lbits & expMask; - if (binexp == 0L || binexp == expMask) { - // what we have here is special. - // don't worry, the right thing will happen. - return (float) dval; - } - lbits += (long) roundDir; // hack-o-matic. - return (float) Double.longBitsToDouble(lbits); - } - - - /* - * This is the easy subcase -- - * all the significant bits, after scaling, are held in lvalue. - * negSign and decExponent tell us what processing and scaling - * has already been done. Exceptional cases have already been - * stripped out. - * In particular: - * lvalue is a finite number (not Inf, nor NaN) - * lvalue > 0L (not zero, nor negative). - * - * The only reason that we develop the digits here, rather than - * calling on Long.toString() is that we can do it a little faster, - * and besides want to treat trailing 0s specially. If Long.toString - * changes, we should re-evaluate this strategy! - */ - private void developLongDigits(int decExponent, long lvalue, long insignificant) { - char digits[]; - int ndigits; - int digitno; - int c; - // - // Discard non-significant low-order bits, while rounding, - // up to insignificant value. - int i; - for (i = 0; insignificant >= 10L; i++) { - insignificant /= 10L; - } - if (i != 0) { - long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i; - long residue = lvalue % pow10; - lvalue /= pow10; - decExponent += i; - if (residue >= (pow10 >> 1)) { - // round up based on the low-order bits we're discarding - lvalue++; - } - } - if (lvalue <= Integer.MAX_VALUE) { - assert lvalue > 0L : lvalue; // lvalue <= 0 - // even easier subcase! - // can do int arithmetic rather than long! - int ivalue = (int) lvalue; - ndigits = 10; - digits = (char[]) (perThreadBuffer.get()); - digitno = ndigits - 1; - c = ivalue % 10; - ivalue /= 10; - while (c == 0) { - decExponent++; - c = ivalue % 10; - ivalue /= 10; - } - while (ivalue != 0) { - digits[digitno--] = (char) (c + '0'); - decExponent++; - c = ivalue % 10; - ivalue /= 10; - } - digits[digitno] = (char) (c + '0'); - } else { - // same algorithm as above (same bugs, too ) - // but using long arithmetic. - ndigits = 20; - digits = (char[]) (perThreadBuffer.get()); - digitno = ndigits - 1; - c = (int) (lvalue % 10L); - lvalue /= 10L; - while (c == 0) { - decExponent++; - c = (int) (lvalue % 10L); - lvalue /= 10L; - } - while (lvalue != 0L) { - digits[digitno--] = (char) (c + '0'); - decExponent++; - c = (int) (lvalue % 10L); - lvalue /= 10; - } - digits[digitno] = (char) (c + '0'); - } - char result[]; - ndigits -= digitno; - result = new char[ndigits]; - System.arraycopy(digits, digitno, result, 0, ndigits); - this.digits = result; - this.decExponent = decExponent + 1; - this.nDigits = ndigits; - } - - // - // add one to the least significant digit. - // in the unlikely event there is a carry out, - // deal with it. - // assert that this will only happen where there - // is only one digit, e.g. (float)1e-44 seems to do it. - // - private void roundup() { - int i; - int q = digits[i = (nDigits - 1)]; - if (q == '9') { - while (q == '9' && i > 0) { - digits[i] = '0'; - q = digits[--i]; - } - if (q == '9') { - // carryout! High-order 1, rest 0s, larger exp. - decExponent += 1; - digits[0] = '1'; - return; - } - // else fall through. - } - digits[i] = (char) (q + 1); - } - - /* - * FIRST IMPORTANT CONSTRUCTOR: DOUBLE - */ - public EcmaFloatingDecimal(double d, boolean maxPrecision) { - long dBits = Double.doubleToLongBits(d); - long fractBits; - int binExp; - int nSignificantBits; - - // discover and delete sign - if ((dBits & signMask) != 0) { - isNegative = true; - dBits ^= signMask; - } else { - isNegative = false; - } - // Begin to unpack - // Discover obvious special cases of NaN and Infinity. - binExp = (int) ((dBits & expMask) >> expShift); - fractBits = dBits & fractMask; - if (binExp == (int) (expMask >> expShift)) { - isExceptional = true; - if (fractBits == 0L) { - digits = infinity; - } else { - digits = notANumber; - isNegative = false; // NaN has no sign! - } - nDigits = digits.length; - return; - } - isExceptional = false; - // Finish unpacking - // Normalize denormalized numbers. - // Insert assumed high-order bit for normalized numbers. - // Subtract exponent bias. - if (binExp == 0) { - if (fractBits == 0L) { - // not a denorm, just a 0! - decExponent = 0; - digits = zero; - nDigits = 1; - return; - } - while ((fractBits & fractHOB) == 0L) { - fractBits <<= 1; - binExp -= 1; - } - nSignificantBits = expShift + binExp + 1; // recall binExp is - shift count. - binExp += 1; - } else { - fractBits |= fractHOB; - nSignificantBits = expShift + 1; - } - binExp -= expBias; - // call the routine that actually does all the hard work. - dtoa(binExp, fractBits, nSignificantBits); - - if (!maxPrecision) { - if (nDigits > 15) { - nDigits = 15; - if (digits[15] >= '5') { - roundup(); - } - - while (nDigits > 0 && digits[nDigits - 1] == '0') { - nDigits--; - } - } - } - } - - /* - * SECOND IMPORTANT CONSTRUCTOR: SINGLE - */ - public EcmaFloatingDecimal(float f) { - int fBits = Float.floatToIntBits(f); - int fractBits; - int binExp; - int nSignificantBits; - - // discover and delete sign - if ((fBits & singleSignMask) != 0) { - isNegative = true; - fBits ^= singleSignMask; - } else { - isNegative = false; - } - // Begin to unpack - // Discover obvious special cases of NaN and Infinity. - binExp = (int) ((fBits & singleExpMask) >> singleExpShift); - fractBits = fBits & singleFractMask; - if (binExp == (int) (singleExpMask >> singleExpShift)) { - isExceptional = true; - if (fractBits == 0L) { - digits = infinity; - } else { - digits = notANumber; - isNegative = false; // NaN has no sign! - } - nDigits = digits.length; - return; - } - isExceptional = false; - // Finish unpacking - // Normalize denormalized numbers. - // Insert assumed high-order bit for normalized numbers. - // Subtract exponent bias. - if (binExp == 0) { - if (fractBits == 0) { - // not a denorm, just a 0! - decExponent = 0; - digits = zero; - nDigits = 1; - return; - } - while ((fractBits & singleFractHOB) == 0) { - fractBits <<= 1; - binExp -= 1; - } - nSignificantBits = singleExpShift + binExp + 1; // recall binExp is - shift count. - binExp += 1; - } else { - fractBits |= singleFractHOB; - nSignificantBits = singleExpShift + 1; - } - binExp -= singleExpBias; - // call the routine that actually does all the hard work. - dtoa(binExp, ((long) fractBits) << (expShift - singleExpShift), nSignificantBits); - } - - private void dtoa(int binExp, long fractBits, int nSignificantBits) { - int nFractBits; // number of significant bits of fractBits; - int nTinyBits; // number of these to the right of the point. - int decExp; - - // Examine number. Determine if it is an easy case, - // which we can do pretty trivially using float/long conversion, - // or whether we must do real work. - nFractBits = countBits(fractBits); - nTinyBits = Math.max(0, nFractBits - binExp - 1); - if (binExp <= maxSmallBinExp && binExp >= minSmallBinExp) { - // Look more closely at the number to decide if, - // with scaling by 10^nTinyBits, the result will fit in - // a long. - if ((nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64)) { - /* - * We can do this: - * take the fraction bits, which are normalized. - * (a) nTinyBits == 0: Shift left or right appropriately - * to align the binary point at the extreme right, i.e. - * where a long int point is expected to be. The integer - * result is easily converted to a string. - * (b) nTinyBits > 0: Shift right by expShift-nFractBits, - * which effectively converts to long and scales by - * 2^nTinyBits. Then multiply by 5^nTinyBits to - * complete the scaling. We know this won't overflow - * because we just counted the number of bits necessary - * in the result. The integer you get from this can - * then be converted to a string pretty easily. - */ - long halfULP; - if (nTinyBits == 0) { - if (binExp > nSignificantBits) { - halfULP = 1L << (binExp - nSignificantBits - 1); - } else { - halfULP = 0L; - } - if (binExp >= expShift) { - fractBits <<= (binExp - expShift); - } else { - fractBits >>>= (expShift - binExp); - } - developLongDigits(0, fractBits, halfULP); - return; - } - /* - * The following causes excess digits to be printed - * out in the single-float case. Our manipulation of - * halfULP here is apparently not correct. If we - * better understand how this works, perhaps we can - * use this special case again. But for the time being, - * we do not. - * else { - * fractBits >>>= expShift+1-nFractBits; - * fractBits *= long5pow[ nTinyBits ]; - * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); - * developLongDigits( -nTinyBits, fractBits, halfULP ); - * return; - * } - */ - } - } - /* - * This is the hard case. We are going to compute large positive - * integers B and S and integer decExp, s.t. - * d = ( B / S ) * 10^decExp - * 1 <= B / S < 10 - * Obvious choices are: - * decExp = floor( log10(d) ) - * B = d * 2^nTinyBits * 10^max( 0, -decExp ) - * S = 10^max( 0, decExp) * 2^nTinyBits - * (noting that nTinyBits has already been forced to non-negative) - * I am also going to compute a large positive integer - * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp ) - * i.e. M is (1/2) of the ULP of d, scaled like B. - * When we iterate through dividing B/S and picking off the - * quotient bits, we will know when to stop when the remainder - * is <= M. - * - * We keep track of powers of 2 and powers of 5. - */ - - /* - * Estimate decimal exponent. (If it is small-ish, - * we could double-check.) - * - * First, scale the mantissa bits such that 1 <= d2 < 2. - * We are then going to estimate - * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) - * and so we can estimate - * log10(d) ~=~ log10(d2) + binExp * log10(2) - * take the floor and call it decExp. - * FIXME -- use more precise constants here. It costs no more. - */ - double d2 = Double.longBitsToDouble( - expOne | (fractBits & ~fractHOB)); - decExp = (int) Math.floor( - (d2 - 1.5D) * 0.289529654D + 0.176091259 + (double) binExp * 0.301029995663981); - int B2, B5; // powers of 2 and powers of 5, respectively, in B - int S2, S5; // powers of 2 and powers of 5, respectively, in S - int M2, M5; // powers of 2 and powers of 5, respectively, in M - int Bbits; // binary digits needed to represent B, approx. - int tenSbits; // binary digits needed to represent 10*S, approx. - FDBigInt Sval, Bval, Mval; - - B5 = Math.max(0, -decExp); - B2 = B5 + nTinyBits + binExp; - - S5 = Math.max(0, decExp); - S2 = S5 + nTinyBits; - - M5 = B5; - M2 = B2 - nSignificantBits; - - /* - * the long integer fractBits contains the (nFractBits) interesting - * bits from the mantissa of d ( hidden 1 added if necessary) followed - * by (expShift+1-nFractBits) zeros. In the interest of compactness, - * I will shift out those zeros before turning fractBits into a - * FDBigInt. The resulting whole number will be - * d * 2^(nFractBits-1-binExp). - */ - fractBits >>>= (expShift + 1 - nFractBits); - B2 -= nFractBits - 1; - int common2factor = Math.min(B2, S2); - B2 -= common2factor; - S2 -= common2factor; - M2 -= common2factor; - - /* - * HACK!! For exact powers of two, the next smallest number - * is only half as far away as we think (because the meaning of - * ULP changes at power-of-two bounds) for this reason, we - * hack M2. Hope this works. - */ - if (nFractBits == 1) { - M2 -= 1; - } - - if (M2 < 0) { - // oops. - // since we cannot scale M down far enough, - // we must scale the other values up. - B2 -= M2; - S2 -= M2; - M2 = 0; - } - /* - * Construct, Scale, iterate. - * Some day, we'll write a stopping test that takes - * account of the asymmetry of the spacing of floating-point - * numbers below perfect powers of 2 - * 26 Sept 96 is not that day. - * So we use a symmetric test. - */ - char digits[] = this.digits = new char[18]; - int ndigit = 0; - boolean low, high; - long lowDigitDifference; - int q; - - /* - * Detect the special cases where all the numbers we are about - * to compute will fit in int or long integers. - * In these cases, we will avoid doing FDBigInt arithmetic. - * We use the same algorithms, except that we "normalize" - * our FDBigInts before iterating. This is to make division easier, - * as it makes our fist guess (quotient of high-order words) - * more accurate! - * - * Some day, we'll write a stopping test that takes - * account of the asymmetry of the spacing of floating-point - * numbers below perfect powers of 2 - * 26 Sept 96 is not that day. - * So we use a symmetric test. - */ - Bbits = nFractBits + B2 + ((B5 < n5bits.length) ? n5bits[B5] : (B5 * 3)); - tenSbits = S2 + 1 + (((S5 + 1) < n5bits.length) ? n5bits[(S5 + 1)] : ((S5 + 1) * 3)); - if (Bbits < 64 && tenSbits < 64) { - if (Bbits < 32 && tenSbits < 32) { - // wa-hoo! They're all ints! - int b = ((int) fractBits * small5pow[B5]) << B2; - int s = small5pow[S5] << S2; - int m = small5pow[M5] << M2; - int tens = s * 10; - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = b / s; - b = 10 * (b % s); - m *= 10; - low = (b < m); - high = (b + m > tens); - assert q < 10 : q; // excessively large digit - if ((q == 0) && !high) { - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char) ('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if (decExp <= -6 || decExp >= 8) { - high = low = false; - } - while (!low && !high) { - q = b / s; - b = 10 * (b % s); - m *= 10; - assert q < 10 : q; // excessively large digit - if (m > 0L) { - low = (b < m); - high = (b + m > tens); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char) ('0' + q); - } - lowDigitDifference = (b << 1) - tens; - } else { - // still good! they're all longs! - long b = (fractBits * long5pow[B5]) << B2; - long s = long5pow[S5] << S2; - long m = long5pow[M5] << M2; - long tens = s * 10L; - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = (int) (b / s); - b = 10L * (b % s); - m *= 10L; - low = (b < m); - high = (b + m > tens); - assert q < 10 : q; // excessively large digit - if ((q == 0) && !high) { - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char) ('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if (decExp <= -6 || decExp >= 8) { - high = low = false; - } - while (!low && !high) { - q = (int) (b / s); - b = 10 * (b % s); - m *= 10; - assert q < 10 : q; // excessively large digit - if (m > 0L) { - low = (b < m); - high = (b + m > tens); - } else { - // hack -- m might overflow! - // in this case, it is certainly > b, - // which won't - // and b+m > tens, too, since that has overflowed - // either! - low = true; - high = true; - } - digits[ndigit++] = (char) ('0' + q); - } - lowDigitDifference = (b << 1) - tens; - } - } else { - FDBigInt tenSval; - int shiftBias; - - /* - * We really must do FDBigInt arithmetic. - * Fist, construct our FDBigInt initial values. - */ - Bval = multPow52(new FDBigInt(fractBits), B5, B2); - Sval = constructPow52(S5, S2); - Mval = constructPow52(M5, M2); - - // normalize so that division works better - Bval.lshiftMe(shiftBias = Sval.normalizeMe()); - Mval.lshiftMe(shiftBias); - tenSval = Sval.mult(10); - /* - * Unroll the first iteration. If our decExp estimate - * was too high, our first quotient will be zero. In this - * case, we discard it and decrement decExp. - */ - ndigit = 0; - q = Bval.quoRemIteration(Sval); - Mval = Mval.mult(10); - low = (Bval.cmp(Mval) < 0); - high = (Bval.add(Mval).cmp(tenSval) > 0); - assert q < 10 : q; // excessively large digit - if ((q == 0) && !high) { - // oops. Usually ignore leading zero. - decExp--; - } else { - digits[ndigit++] = (char) ('0' + q); - } - /* - * HACK! Java spec sez that we always have at least - * one digit after the . in either F- or E-form output. - * Thus we will need more than one digit if we're using - * E-form - */ - if (decExp <= -6 || decExp >= 8) { - high = low = false; - } - while (!low && !high) { - q = Bval.quoRemIteration(Sval); - Mval = Mval.mult(10); - assert q < 10 : q; // excessively large digit - low = (Bval.cmp(Mval) < 0); - high = (Bval.add(Mval).cmp(tenSval) > 0); - digits[ndigit++] = (char) ('0' + q); - } - if (high && low) { - Bval.lshiftMe(1); - lowDigitDifference = Bval.cmp(tenSval); - } else { - lowDigitDifference = 0L; // this here only for flow analysis! - } - } - this.decExponent = decExp + 1; - this.digits = digits; - this.nDigits = ndigit; - /* - * Last digit gets rounded based on stopping condition. - */ - if (high) { - if (low) { - if (lowDigitDifference == 0L) { - // it's a tie! - // choose based on which digits we like. - if ((digits[nDigits - 1] & 1) != 0) { - roundup(); - } - } else if (lowDigitDifference > 0) { - roundup(); - } - } else { - roundup(); - } - } - } - - @Override - public String toString() { - // most brain-dead version - StringBuffer result = new StringBuffer(nDigits + 8); - if (isNegative) { - result.append('-'); - } - if (isExceptional) { - result.append(digits, 0, nDigits); - } else { - result.append("0."); - result.append(digits, 0, nDigits); - result.append('e'); - result.append(decExponent); - } - return new String(result); - } - - public String toJavaFormatString() { - char result[] = (char[]) (perThreadBuffer.get()); - int i = getChars(result); - return new String(result, 0, i); - } - - private int getChars(char[] result) { - assert nDigits <= 19 : nDigits; // generous bound on size of nDigits - int i = 0; - if (isNegative && (decExponent != 0 || digits != zero)) { - result[0] = '-'; - i = 1; - } - if (isExceptional) { - System.arraycopy(digits, 0, result, i, nDigits); - i += nDigits; - } else if (decExponent > 0 && decExponent < 22) { - // print digits.digits. - int charLength = Math.min(nDigits, decExponent); - System.arraycopy(digits, 0, result, i, charLength); - i += charLength; - if (charLength < decExponent) { - charLength = decExponent - charLength; - System.arraycopy(zero, 0, result, i, charLength); - i += charLength; - } else if (charLength < nDigits) { - result[i++] = '.'; - int t = nDigits - charLength; - System.arraycopy(digits, charLength, result, i, t); - i += t; - } - } else if (decExponent <= 0 && decExponent > -5) { - result[i++] = '0'; - if (digits != zero) { - result[i++] = '.'; - if (decExponent != 0) { - System.arraycopy(zero, 0, result, i, -decExponent); - i -= decExponent; - } - System.arraycopy(digits, 0, result, i, nDigits); - i += nDigits; - } - } else { - result[i++] = digits[0]; - result[i++] = '.'; - if (nDigits > 1) { - System.arraycopy(digits, 1, result, i, nDigits - 1); - i += nDigits - 1; - } else { - result[i++] = '0'; - } - result[i++] = 'e'; - int e; - if (decExponent <= 0) { - result[i++] = '-'; - e = -decExponent + 1; - } else { - e = decExponent - 1; - } - // decExponent has 1, 2, or 3, digits - if (e <= 9) { - result[i++] = (char) (e + '0'); - } else if (e <= 99) { - result[i++] = (char) (e / 10 + '0'); - result[i++] = (char) (e % 10 + '0'); - } else { - result[i++] = (char) (e / 100 + '0'); - e %= 100; - result[i++] = (char) (e / 10 + '0'); - result[i++] = (char) (e % 10 + '0'); - } - } - return i; - } - - // Per-thread buffer for string/stringbuffer conversion - private static ThreadLocal perThreadBuffer = new ThreadLocal() { - @Override - protected synchronized Object initialValue() { - return new char[26]; - } - }; - - public void appendTo(Appendable buf) { - char result[] = (char[]) (perThreadBuffer.get()); - int i = getChars(result); - if (buf instanceof StringBuilder) { - ((StringBuilder) buf).append(result, 0, i); - } else if (buf instanceof StringBuffer) { - ((StringBuffer) buf).append(result, 0, i); - } else { - assert false; - } - } - - /* - * All the positive powers of 10 that can be - * represented exactly in double/float. - */ - private static final double small10pow[] = { - 1.0e0, - 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, - 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, - 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, - 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20, - 1.0e21, 1.0e22 - }; - - private static final float singleSmall10pow[] = { - 1.0e0f, - 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f, - 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f - }; - - private static final double big10pow[] = { - 1e16, 1e32, 1e64, 1e128, 1e256}; - - private static final double tiny10pow[] = { - 1e-16, 1e-32, 1e-64, 1e-128, 1e-256}; - - private static final int maxSmallTen = small10pow.length - 1; - - private static final int singleMaxSmallTen = singleSmall10pow.length - 1; - - private static final int small5pow[] = { - 1, - 5, - 5 * 5, - 5 * 5 * 5, - 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 - }; - - private static final long long5pow[] = { - 1L, - 5L, - 5L * 5, - 5L * 5 * 5, - 5L * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, - 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,}; - - // approximately ceil( log2( long5pow[i] ) ) - private static final int n5bits[] = { - 0, - 3, - 5, - 7, - 10, - 12, - 14, - 17, - 19, - 21, - 24, - 26, - 28, - 31, - 33, - 35, - 38, - 40, - 42, - 45, - 47, - 49, - 52, - 54, - 56, - 59, - 61,}; - - private static final char infinity[] = {'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}; - - private static final char notANumber[] = {'N', 'a', 'N'}; - - private static final char zero[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; - - - /* - * Grammar is compatible with hexadecimal floating-point constants - * described in section 6.4.4.2 of the C99 specification. - */ - private static Pattern hexFloatPattern = Pattern.compile( - //1 234 56 7 8 9 - "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?" - ); - - /** - * Return s with any leading zeros removed. - */ - static String stripLeadingZeros(String s) { - return s.replaceFirst("^0+", ""); - } - - /** - * Extract a hexadecimal digit from position position of string - * s. - */ - static int getHexDigit(String s, int position) { - int value = Character.digit(s.charAt(position), 16); - if (value <= -1 || value >= 16) { - throw new AssertionError("Unexpected failure of digit conversion of " - + s.charAt(position)); - } - return value; - } -} - -/* - * A really, really simple bigint package - * tailored to the needs of floating base conversion. - */ -class FDBigInt { - - int nWords; // number of words used - - int data[]; // value: data[0] is least significant - - public FDBigInt(int v) { - nWords = 1; - data = new int[1]; - data[0] = v; - } - - public FDBigInt(long v) { - data = new int[2]; - data[0] = (int) v; - data[1] = (int) (v >>> 32); - nWords = (data[1] == 0) ? 1 : 2; - } - - public FDBigInt(FDBigInt other) { - data = new int[nWords = other.nWords]; - System.arraycopy(other.data, 0, data, 0, nWords); - } - - private FDBigInt(int[] d, int n) { - data = d; - nWords = n; - } - - public FDBigInt(long seed, char digit[], int nd0, int nd) { - int n = (nd + 8) / 9; // estimate size needed. - if (n < 2) { - n = 2; - } - data = new int[n]; // allocate enough space - data[0] = (int) seed; // starting value - data[1] = (int) (seed >>> 32); - nWords = (data[1] == 0) ? 1 : 2; - int i = nd0; - int limit = nd - 5; // slurp digits 5 at a time. - int v; - while (i < limit) { - int ilim = i + 5; - v = (int) digit[i++] - (int) '0'; - while (i < ilim) { - v = 10 * v + (int) digit[i++] - (int) '0'; - } - multaddMe(100000, v); // ... where 100000 is 10^5. - } - int factor = 1; - v = 0; - while (i < nd) { - v = 10 * v + (int) digit[i++] - (int) '0'; - factor *= 10; - } - if (factor != 1) { - multaddMe(factor, v); - } - } - - /* - * Left shift by c bits. - * Shifts this in place. - */ - public void lshiftMe(int c) throws IllegalArgumentException { - if (c <= 0) { - if (c == 0) { - return; // silly. - } else { - throw new IllegalArgumentException("negative shift count"); - } - } - int wordcount = c >> 5; - int bitcount = c & 0x1f; - int anticount = 32 - bitcount; - int t[] = data; - int s[] = data; - if (nWords + wordcount + 1 > t.length) { - // reallocate. - t = new int[nWords + wordcount + 1]; - } - int target = nWords + wordcount; - int src = nWords - 1; - if (bitcount == 0) { - // special hack, since an anticount of 32 won't go! - System.arraycopy(s, 0, t, wordcount, nWords); - target = wordcount - 1; - } else { - t[target--] = s[src] >>> anticount; - while (src >= 1) { - t[target--] = (s[src] << bitcount) | (s[--src] >>> anticount); - } - t[target--] = s[src] << bitcount; - } - while (target >= 0) { - t[target--] = 0; - } - data = t; - nWords += wordcount + 1; - // may have constructed high-order word of 0. - // if so, trim it - while (nWords > 1 && data[nWords - 1] == 0) { - nWords--; - } - } - - /* - * normalize this number by shifting until - * the MSB of the number is at 0x08000000. - * This is in preparation for quoRemIteration, below. - * The idea is that, to make division easier, we want the - * divisor to be "normalized" -- usually this means shifting - * the MSB into the high words sign bit. But because we know that - * the quotient will be 0 < q < 10, we would like to arrange that - * the dividend not span up into another word of precision. - * (This needs to be explained more clearly!) - */ - public int normalizeMe() throws IllegalArgumentException { - int src; - int wordcount = 0; - int bitcount = 0; - int v = 0; - for (src = nWords - 1; src >= 0 && (v = data[src]) == 0; src--) { - wordcount += 1; - } - if (src < 0) { - // oops. Value is zero. Cannot normalize it! - throw new IllegalArgumentException("zero value"); - } - /* - * In most cases, we assume that wordcount is zero. This only - * makes sense, as we try not to maintain any high-order - * words full of zeros. In fact, if there are zeros, we will - * simply SHORTEN our number at this point. Watch closely... - */ - nWords -= wordcount; - /* - * Compute how far left we have to shift v s.t. its highest- - * order bit is in the right place. Then call lshiftMe to - * do the work. - */ - if ((v & 0xf0000000) != 0) { - // will have to shift up into the next word. - // too bad. - for (bitcount = 32; (v & 0xf0000000) != 0; bitcount--) { - v >>>= 1; - } - } else { - while (v <= 0x000fffff) { - // hack: byte-at-a-time shifting - v <<= 8; - bitcount += 8; - } - while (v <= 0x07ffffff) { - v <<= 1; - bitcount += 1; - } - } - if (bitcount != 0) { - lshiftMe(bitcount); - } - return bitcount; - } - - /* - * Multiply a FDBigInt by an int. - * Result is a new FDBigInt. - */ - public FDBigInt mult(int iv) { - long v = iv; - int r[]; - long p; - - // guess adequate size of r. - r = new int[(v * ((long) data[nWords - 1] & 0xffffffffL) > 0xfffffffL) ? nWords + 1 : nWords]; - p = 0L; - for (int i = 0; i < nWords; i++) { - p += v * ((long) data[i] & 0xffffffffL); - r[i] = (int) p; - p >>>= 32; - } - if (p == 0L) { - return new FDBigInt(r, nWords); - } else { - r[nWords] = (int) p; - return new FDBigInt(r, nWords + 1); - } - } - - /* - * Multiply a FDBigInt by an int and add another int. - * Result is computed in place. - * Hope it fits! - */ - public void multaddMe(int iv, int addend) { - long v = iv; - long p; - - // unroll 0th iteration, doing addition. - p = v * ((long) data[0] & 0xffffffffL) + ((long) addend & 0xffffffffL); - data[0] = (int) p; - p >>>= 32; - for (int i = 1; i < nWords; i++) { - p += v * ((long) data[i] & 0xffffffffL); - data[i] = (int) p; - p >>>= 32; - } - if (p != 0L) { - data[nWords] = (int) p; // will fail noisily if illegal! - nWords++; - } - } - - /* - * Multiply a FDBigInt by another FDBigInt. - * Result is a new FDBigInt. - */ - public FDBigInt mult(FDBigInt other) { - // crudely guess adequate size for r - int r[] = new int[nWords + other.nWords]; - int i; - // I think I am promised zeros... - - for (i = 0; i < this.nWords; i++) { - long v = (long) this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION - long p = 0L; - int j; - for (j = 0; j < other.nWords; j++) { - p += ((long) r[i + j] & 0xffffffffL) + v * ((long) other.data[j] & 0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND. - r[i + j] = (int) p; - p >>>= 32; - } - r[i + j] = (int) p; - } - // compute how much of r we actually needed for all that. - for (i = r.length - 1; i > 0; i--) { - if (r[i] != 0) { - break; - } - } - return new FDBigInt(r, i + 1); - } - - /* - * Add one FDBigInt to another. Return a FDBigInt - */ - public FDBigInt add(FDBigInt other) { - int i; - int a[], b[]; - int n, m; - long c = 0L; - // arrange such that a.nWords >= b.nWords; - // n = a.nWords, m = b.nWords - if (this.nWords >= other.nWords) { - a = this.data; - n = this.nWords; - b = other.data; - m = other.nWords; - } else { - a = other.data; - n = other.nWords; - b = this.data; - m = this.nWords; - } - int r[] = new int[n]; - for (i = 0; i < n; i++) { - c += (long) a[i] & 0xffffffffL; - if (i < m) { - c += (long) b[i] & 0xffffffffL; - } - r[i] = (int) c; - c >>= 32; // signed shift. - } - if (c != 0L) { - // oops -- carry out -- need longer result. - int s[] = new int[r.length + 1]; - System.arraycopy(r, 0, s, 0, r.length); - s[i++] = (int) c; - return new FDBigInt(s, i); - } - return new FDBigInt(r, i); - } - - /* - * Subtract one FDBigInt from another. Return a FDBigInt - * Assert that the result is positive. - */ - public FDBigInt sub(FDBigInt other) { - int r[] = new int[this.nWords]; - int i; - int n = this.nWords; - int m = other.nWords; - int nzeros = 0; - long c = 0L; - for (i = 0; i < n; i++) { - c += (long) this.data[i] & 0xffffffffL; - if (i < m) { - c -= (long) other.data[i] & 0xffffffffL; - } - if ((r[i] = (int) c) == 0) { - nzeros++; - } else { - nzeros = 0; - } - c >>= 32; // signed shift - } - assert c == 0L : c; // borrow out of subtract - assert dataInRangeIsZero(i, m, other); // negative result of subtract - return new FDBigInt(r, n - nzeros); - } - - private static boolean dataInRangeIsZero(int i, int m, FDBigInt other) { - while (i < m) { - if (other.data[i++] != 0) { - return false; - } - } - return true; - } - - /* - * Compare FDBigInt with another FDBigInt. Return an integer - * >0: this > other - * 0: this == other - * <0: this < other - */ - public int cmp(FDBigInt other) { - int i; - if (this.nWords > other.nWords) { - // if any of my high-order words is non-zero, - // then the answer is evident - int j = other.nWords - 1; - for (i = this.nWords - 1; i > j; i--) { - if (this.data[i] != 0) { - return 1; - } - } - } else if (this.nWords < other.nWords) { - // if any of other's high-order words is non-zero, - // then the answer is evident - int j = this.nWords - 1; - for (i = other.nWords - 1; i > j; i--) { - if (other.data[i] != 0) { - return -1; - } - } - } else { - i = this.nWords - 1; - } - for (; i > 0; i--) { - if (this.data[i] != other.data[i]) { - break; - } - } - // careful! want unsigned compare! - // use brute force here. - int a = this.data[i]; - int b = other.data[i]; - if (a < 0) { - // a is really big, unsigned - if (b < 0) { - return a - b; // both big, negative - } else { - return 1; // b not big, answer is obvious; - } - } else // a is not really big - { - if (b < 0) { - // but b is really big - return -1; - } else { - return a - b; - } - } - } - - /* - * Compute - * q = (int)( this / S ) - * this = 10 * ( this mod S ) - * Return q. - * This is the iteration step of digit development for output. - * We assume that S has been normalized, as above, and that - * "this" has been lshift'ed accordingly. - * Also assume, of course, that the result, q, can be expressed - * as an integer, 0 <= q < 10. - */ - public int quoRemIteration(FDBigInt S) throws IllegalArgumentException { - // ensure that this and S have the same number of - // digits. If S is properly normalized and q < 10 then - // this must be so. - if (nWords != S.nWords) { - throw new IllegalArgumentException("disparate values"); - } - // estimate q the obvious way. We will usually be - // right. If not, then we're only off by a little and - // will re-add. - int n = nWords - 1; - long q = ((long) data[n] & 0xffffffffL) / (long) S.data[n]; - long diff = 0L; - for (int i = 0; i <= n; i++) { - diff += ((long) data[i] & 0xffffffffL) - q * ((long) S.data[i] & 0xffffffffL); - data[i] = (int) diff; - diff >>= 32; // N.B. SIGNED shift. - } - if (diff != 0L) { - // damn, damn, damn. q is too big. - // add S back in until this turns +. This should - // not be very many times! - long sum = 0L; - while (sum == 0L) { - sum = 0L; - for (int i = 0; i <= n; i++) { - sum += ((long) data[i] & 0xffffffffL) + ((long) S.data[i] & 0xffffffffL); - data[i] = (int) sum; - sum >>= 32; // Signed or unsigned, answer is 0 or 1 - } - /* - * Originally the following line read - * "if ( sum !=0 && sum != -1 )" - * but that would be wrong, because of the - * treatment of the two values as entirely unsigned, - * it would be impossible for a carry-out to be interpreted - * as -1 -- it would have to be a single-bit carry-out, or - * +1. - */ - assert sum == 0 || sum == 1 : sum; // carry out of division correction - q -= 1; - } - } - // finally, we can multiply this by 10. - // it cannot overflow, right, as the high-order word has - // at least 4 high-order zeros! - long p = 0L; - for (int i = 0; i <= n; i++) { - p += 10 * ((long) data[i] & 0xffffffffL); - data[i] = (int) p; - p >>= 32; // SIGNED shift. - } - assert p == 0L : p; // Carry out of *10 - return (int) q; - } - - public long longValue() { - // if this can be represented as a long, return the value - assert this.nWords > 0 : this.nWords; // longValue confused - - if (this.nWords == 1) { - return ((long) data[0] & 0xffffffffL); - } - - assert dataInRangeIsZero(2, this.nWords, this); // value too big - assert data[1] >= 0; // value too big - return ((long) (data[1]) << 32) | ((long) data[0] & 0xffffffffL); - } - - @Override - public String toString() { - StringBuffer r = new StringBuffer(30); - r.append('['); - int i = Math.min(nWords - 1, data.length - 1); - if (nWords > data.length) { - r.append("(" + data.length + "<" + nWords + "!)"); - } - for (; i > 0; i--) { - r.append(Integer.toHexString(data[i])); - r.append(' '); - } - r.append(Integer.toHexString(data[0])); - r.append(']'); - return new String(r); - } -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaNumberToString.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaNumberToString.java new file mode 100644 index 000000000..3b314fde5 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaNumberToString.java @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2010-2021 JPEXS, All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + */ +package com.jpexs.decompiler.flash.ecma; + +import java.math.BigInteger; + +/** + * EcmaScript number to string conversion, refinement of + * sun.misc.FloatingDecimal. + */ +public final class EcmaNumberToString { + + /** + * Is not a number flag + */ + private final boolean isNaN; + + /** + * Is a negative number flag. + */ + private boolean isNegative; + + /** + * Decimal exponent value (for E notation.) + */ + private int decimalExponent; + + /** + * Actual digits. + */ + private char digits[]; + + /** + * Number of digits to use. (nDigits <= digits.length). + */ + private int nDigits; + + /* + * IEEE-754 constants. + */ + //private static final long signMask = 0x8000000000000000L; + private static final int expMask = 0x7FF; + private static final long fractMask = 0x000F_FFFF_FFFF_FFFFL; + private static final int expShift = 52; + private static final int expBias = 1_023; + private static final long fractHOB = (1L << expShift); + private static final long expOne = ((long) expBias) << expShift; + private static final int maxSmallBinExp = 62; + private static final int minSmallBinExp = -(63 / 3); + + /** + * Powers of 5 fitting a long. + */ + private static final long powersOf5[] = { + 1L, + 5L, + 5L * 5, + 5L * 5 * 5, + 5L * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + + // Approximately ceil(log2(longPowers5[i])). + private static final int nBitsPowerOf5[] = { + 0, + 3, + 5, + 7, + 10, + 12, + 14, + 17, + 19, + 21, + 24, + 26, + 28, + 31, + 33, + 35, + 38, + 40, + 42, + 45, + 47, + 49, + 52, + 54, + 56, + 59, + 61 + }; + + /** + * Digits used for infinity result. + */ + private static final char infinityDigits[] = {'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'}; + + /** + * Digits used for NaN result. + */ + private static final char nanDigits[] = {'N', 'a', 'N'}; + + /** + * Zeros used to pad result. + */ + private static final char zeroes[] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + + /** + * Convert a number into a JavaScript string. + * + * @param value Double to convert. + * @return JavaScript formated number. + */ + public static String stringFor(final double value) { + return new EcmaNumberToString(value).toString(); + } + + /* + * Constructor. + */ + private EcmaNumberToString(final double value) { + // Double as bits. + long bits = Double.doubleToLongBits(value); + + // Get upper word. + final int upper = (int) (bits >> 32); + + // Detect sign. + isNegative = upper < 0; + + // Extract exponent. + int exponent = (upper >> (expShift - 32)) & expMask; + + // Clear sign and exponent. + bits &= fractMask; + + // Detect NaN. + if (exponent == expMask) { + isNaN = true; + + // Detect Infinity. + if (bits == 0L) { + digits = infinityDigits; + } else { + digits = nanDigits; + isNegative = false; + } + + nDigits = digits.length; + + return; + } + + // We have a working double. + isNaN = false; + + int nSignificantBits; + + // Detect denormalized value. + if (exponent == 0) { + // Detect zero value. + if (bits == 0L) { + decimalExponent = 0; + digits = zeroes; + nDigits = 1; + + return; + } + + // Normalize value, using highest significant bit as HOB. + while ((bits & fractHOB) == 0L) { + bits <<= 1; + exponent -= 1; + } + + // Compute number of significant bits. + nSignificantBits = expShift + exponent + 1; + // Bias exponent by HOB. + exponent += 1; + } else { + // Add implicit HOB. + bits |= fractHOB; + // Compute number of significant bits. + nSignificantBits = expShift + 1; + } + + // Unbias exponent (represents bit shift). + exponent -= expBias; + + // Determine the number of significant bits in the fraction. + final int nFractBits = countSignificantBits(bits); + + // Number of bits to the right of the decimal. + final int nTinyBits = Math.max(0, nFractBits - exponent - 1); + + // Computed decimal exponent. + int decExponent; + + if (exponent <= maxSmallBinExp && exponent >= minSmallBinExp) { + // Look more closely at the number to decide if, + // with scaling by 10^nTinyBits, the result will fit in + // a long. + if (nTinyBits < powersOf5.length && (nFractBits + nBitsPowerOf5[nTinyBits]) < 64) { + /* + * We can do this: + * take the fraction bits, which are normalized. + * (a) nTinyBits == 0: Shift left or right appropriately + * to align the binary point at the extreme right, i.e. + * where a long int point is expected to be. The integer + * result is easily converted to a string. + * (b) nTinyBits > 0: Shift right by expShift - nFractBits, + * which effectively converts to long and scales by + * 2^nTinyBits. Then multiply by 5^nTinyBits to + * complete the scaling. We know this won't overflow + * because we just counted the number of bits necessary + * in the result. The integer you get from this can + * then be converted to a string pretty easily. + */ + + if (nTinyBits == 0) { + long halfULP; + + if (exponent > nSignificantBits) { + halfULP = 1L << (exponent - nSignificantBits - 1); + } else { + halfULP = 0L; + } + + if (exponent >= expShift) { + bits <<= exponent - expShift; + } else { + bits >>>= expShift - exponent; + } + + // Discard non-significant low-order bits, while rounding, + // up to insignificant value. + int i; + for (i = 0; halfULP >= 10L; i++) { + halfULP /= 10L; + } + + /** + * This is the easy subcase -- all the significant bits, + * after scaling, are held in bits. isNegative and + * decExponent tell us what processing and scaling has + * already been done. Exceptional cases have already been + * stripped out. In particular: bits is a finite number (not + * Infinite, nor NaN) bits > 0L (not zero, nor negative). + * + * The only reason that we develop the digits here, rather + * than calling on Long.toString() is that we can do it a + * little faster, and besides want to treat trailing 0s + * specially. If Long.toString changes, we should + * re-evaluate this strategy! + */ + int decExp = 0; + + if (i != 0) { + // 10^i == 5^i * 2^i + final long powerOf10 = powersOf5[i] << i; + final long residue = bits % powerOf10; + bits /= powerOf10; + decExp += i; + + if (residue >= (powerOf10 >> 1)) { + // Round up based on the low-order bits we're discarding. + bits++; + } + } + + int ndigits = 20; + final char[] digits0 = new char[26]; + int digitno = ndigits - 1; + int c = (int) (bits % 10L); + bits /= 10L; + + while (c == 0) { + decExp++; + c = (int) (bits % 10L); + bits /= 10L; + } + + while (bits != 0L) { + digits0[digitno--] = (char) (c + '0'); + decExp++; + c = (int) (bits % 10L); + bits /= 10; + } + + digits0[digitno] = (char) (c + '0'); + + ndigits -= digitno; + final char[] result = new char[ndigits]; + System.arraycopy(digits0, digitno, result, 0, ndigits); + + this.digits = result; + this.decimalExponent = decExp + 1; + this.nDigits = ndigits; + + return; + } + } + } + + /* + * This is the hard case. We are going to compute large positive + * integers B and S and integer decExp, s.t. + * d = (B / S) * 10^decExp + * 1 <= B / S < 10 + * Obvious choices are: + * decExp = floor(log10(d)) + * B = d * 2^nTinyBits * 10^max(0, -decExp) + * S = 10^max(0, decExp) * 2^nTinyBits + * (noting that nTinyBits has already been forced to non-negative) + * I am also going to compute a large positive integer + * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max(0, -decExp) + * i.e. M is (1/2) of the ULP of d, scaled like B. + * When we iterate through dividing B/S and picking off the + * quotient bits, we will know when to stop when the remainder + * is <= M. + * + * We keep track of powers of 2 and powers of 5. + */ + + /* + * Estimate decimal exponent. (If it is small-ish, + * we could double-check.) + * + * First, scale the mantissa bits such that 1 <= d2 < 2. + * We are then going to estimate + * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) + * and so we can estimate + * log10(d) ~=~ log10(d2) + binExp * log10(2) + * take the floor and call it decExp. + */ + final double d2 = Double.longBitsToDouble(expOne | (bits & ~fractHOB)); + decExponent = (int) Math.floor((d2 - 1.5D) * 0.289529654D + 0.176091259D + exponent * 0.301029995663981D); + + // Powers of 2 and powers of 5, respectively, in B. + final int B5 = Math.max(0, -decExponent); + int B2 = B5 + nTinyBits + exponent; + + // Powers of 2 and powers of 5, respectively, in S. + final int S5 = Math.max(0, decExponent); + int S2 = S5 + nTinyBits; + + // Powers of 2 and powers of 5, respectively, in M. + final int M5 = B5; + int M2 = B2 - nSignificantBits; + + /* + * The long integer fractBits contains the (nFractBits) interesting + * bits from the mantissa of d (hidden 1 added if necessary) followed + * by (expShift + 1 - nFractBits) zeros. In the interest of compactness, + * I will shift out those zeros before turning fractBits into a + * BigInteger. The resulting whole number will be + * d * 2^(nFractBits - 1 - binExp). + */ + bits >>>= expShift + 1 - nFractBits; + B2 -= nFractBits - 1; + final int common2factor = Math.min(B2, S2); + B2 -= common2factor; + S2 -= common2factor; + M2 -= common2factor; + + /* + * HACK!!For exact powers of two, the next smallest number + * is only half as far away as we think (because the meaning of + * ULP changes at power-of-two bounds) for this reason, we + * hack M2. Hope this works. + */ + if (nFractBits == 1) { + M2 -= 1; + } + + if (M2 < 0) { + // Oops. Since we cannot scale M down far enough, + // we must scale the other values up. + B2 -= M2; + S2 -= M2; + M2 = 0; + } + + /* + * Construct, Scale, iterate. + * Some day, we'll write a stopping test that takes + * account of the asymmetry of the spacing of floating-point + * numbers below perfect powers of 2 + * 26 Sept 96 is not that day. + * So we use a symmetric test. + */ + final char digits0[] = this.digits = new char[32]; + int ndigit; + boolean low, high; + long lowDigitDifference; + int q; + + /* + * Detect the special cases where all the numbers we are about + * to compute will fit in int or long integers. + * In these cases, we will avoid doing BigInteger arithmetic. + * We use the same algorithms, except that we "normalize" + * our FDBigInts before iterating. This is to make division easier, + * as it makes our fist guess (quotient of high-order words) + * more accurate! + */ + // Binary digits needed to represent B, approx. + final int Bbits = nFractBits + B2 + ((B5 < nBitsPowerOf5.length) ? nBitsPowerOf5[B5] : (B5 * 3)); + // Binary digits needed to represent 10*S, approx. + final int tenSbits = S2 + 1 + (((S5 + 1) < nBitsPowerOf5.length) ? nBitsPowerOf5[(S5 + 1)] : ((S5 + 1) * 3)); + + if (Bbits < 64 && tenSbits < 64) { + long b = (bits * powersOf5[B5]) << B2; + final long s = powersOf5[S5] << S2; + long m = powersOf5[M5] << M2; + final long tens = s * 10L; + + /* + * Unroll the first iteration. If our decExp estimate + * was too high, our first quotient will be zero. In this + * case, we discard it and decrement decExp. + */ + ndigit = 0; + q = (int) (b / s); + b = 10L * (b % s); + m *= 10L; + low = b < m; + high = (b + m) > tens; + + if (q == 0 && !high) { + // Ignore leading zero. + decExponent--; + } else { + digits0[ndigit++] = (char) ('0' + q); + } + + if (decExponent < -3 || decExponent >= 8) { + high = low = false; + } + + while (!low && !high) { + q = (int) (b / s); + b = 10 * (b % s); + m *= 10; + + if (m > 0L) { + low = b < m; + high = (b + m) > tens; + } else { + low = true; + high = true; + } + + if (low && q == 0) { + break; + } + digits0[ndigit++] = (char) ('0' + q); + } + + lowDigitDifference = (b << 1) - tens; + } else { + /* + * We must do BigInteger arithmetic. + * First, construct our BigInteger initial values. + */ + + BigInteger Bval = multiplyPowerOf5And2(BigInteger.valueOf(bits), B5, B2); + BigInteger Sval = constructPowerOf5And2(S5, S2); + BigInteger Mval = constructPowerOf5And2(M5, M2); + + // Normalize so that BigInteger division works better. + final int shiftBias = Long.numberOfLeadingZeros(bits) - 4; + Bval = Bval.shiftLeft(shiftBias); + Mval = Mval.shiftLeft(shiftBias); + Sval = Sval.shiftLeft(shiftBias); + final BigInteger tenSval = Sval.multiply(BigInteger.TEN); + + /* + * Unroll the first iteration. If our decExp estimate + * was too high, our first quotient will be zero. In this + * case, we discard it and decrement decExp. + */ + ndigit = 0; + + BigInteger[] quoRem = Bval.divideAndRemainder(Sval); + q = quoRem[0].intValue(); + Bval = quoRem[1].multiply(BigInteger.TEN); + Mval = Mval.multiply(BigInteger.TEN); + low = (Bval.compareTo(Mval) < 0); + high = (Bval.add(Mval).compareTo(tenSval) > 0); + + if (q == 0 && !high) { + // Ignore leading zero. + decExponent--; + } else { + digits0[ndigit++] = (char) ('0' + q); + } + + if (decExponent < -3 || decExponent >= 8) { + high = low = false; + } + + while (!low && !high) { + quoRem = Bval.divideAndRemainder(Sval); + q = quoRem[0].intValue(); + Bval = quoRem[1].multiply(BigInteger.TEN); + Mval = Mval.multiply(BigInteger.TEN); + low = (Bval.compareTo(Mval) < 0); + high = (Bval.add(Mval).compareTo(tenSval) > 0); + + if (low && q == 0) { + break; + } + digits0[ndigit++] = (char) ('0' + q); + } + + if (high && low) { + Bval = Bval.shiftLeft(1); + lowDigitDifference = Bval.compareTo(tenSval); + } else { + lowDigitDifference = 0L; + } + } + + this.decimalExponent = decExponent + 1; + this.digits = digits0; + this.nDigits = ndigit; + + /* + * Last digit gets rounded based on stopping condition. + */ + if (high) { + if (low) { + if (lowDigitDifference == 0L) { + // it's a tie! + // choose based on which digits we like. + if ((digits0[nDigits - 1] & 1) != 0) { + roundup(); + } + } else if (lowDigitDifference > 0) { + roundup(); + } + } else { + roundup(); + } + } + } + + /** + * Count number of significant bits. + * + * @param bits Double's fraction. + * @return Number of significant bits. + */ + private static int countSignificantBits(final long bits) { + if (bits != 0) { + return 64 - Long.numberOfLeadingZeros(bits) - Long.numberOfTrailingZeros(bits); + } + + return 0; + } + + /* + * Cache big powers of 5 handy for future reference. + */ + private static BigInteger powerOf5Cache[]; + + /** + * Determine the largest power of 5 needed (as BigInteger.) + * + * @param power Power of 5. + * @return BigInteger of power of 5. + */ + private static BigInteger bigPowerOf5(final int power) { + if (powerOf5Cache == null) { + powerOf5Cache = new BigInteger[power + 1]; + } else if (powerOf5Cache.length <= power) { + final BigInteger t[] = new BigInteger[power + 1]; + System.arraycopy(powerOf5Cache, 0, t, 0, powerOf5Cache.length); + powerOf5Cache = t; + } + + if (powerOf5Cache[power] != null) { + return powerOf5Cache[power]; + } else if (power < powersOf5.length) { + return powerOf5Cache[power] = BigInteger.valueOf(powersOf5[power]); + } else { + // Construct the value recursively. + // in order to compute 5^p, + // compute its square root, 5^(p/2) and square. + // or, let q = p / 2, r = p -q, then + // 5^p = 5^(q+r) = 5^q * 5^r + final int q = power >> 1; + final int r = power - q; + BigInteger bigQ = powerOf5Cache[q]; + + if (bigQ == null) { + bigQ = bigPowerOf5(q); + } + + if (r < powersOf5.length) { + return (powerOf5Cache[power] = bigQ.multiply(BigInteger.valueOf(powersOf5[r]))); + } + BigInteger bigR = powerOf5Cache[r]; + + if (bigR == null) { + bigR = bigPowerOf5(r); + } + + return (powerOf5Cache[power] = bigQ.multiply(bigR)); + } + } + + /** + * Multiply BigInteger by powers of 5 and 2 (i.e., 10) + * + * @param value Value to multiply. + * @param p5 Power of 5. + * @param p2 Power of 2. + * @return Result. + */ + private static BigInteger multiplyPowerOf5And2(final BigInteger value, final int p5, final int p2) { + BigInteger returnValue = value; + + if (p5 != 0) { + returnValue = returnValue.multiply(bigPowerOf5(p5)); + } + + if (p2 != 0) { + returnValue = returnValue.shiftLeft(p2); + } + + return returnValue; + } + + /** + * Construct a BigInteger power of 5 and 2 (i.e., 10) + * + * @param p5 Power of 5. + * @param p2 Power of 2. + * @return Result. + */ + private static BigInteger constructPowerOf5And2(final int p5, final int p2) { + BigInteger v = bigPowerOf5(p5); + + if (p2 != 0) { + v = v.shiftLeft(p2); + } + + return v; + } + + /** + * Round up last digit by adding one to the least significant digit. In the + * unlikely event there is a carry out, deal with it. assert that this will + * only happen where there is only one digit, e.g. (float)1e-44 seems to do + * it. + */ + private void roundup() { + int i; + int q = digits[i = (nDigits - 1)]; + + while (q == '9' && i > 0) { + if (decimalExponent < 0) { + nDigits--; + } else { + digits[i] = '0'; + } + + q = digits[--i]; + } + + if (q == '9') { + // Carryout! High-order 1, rest 0s, larger exp. + decimalExponent += 1; + digits[0] = '1'; + + return; + } + + digits[i] = (char) (q + 1); + } + + /** + * Format final number string. + * + * @return Formatted string. + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(32); + + if (isNegative) { + sb.append('-'); + } + + if (isNaN) { + sb.append(digits, 0, nDigits); + } else { + if (decimalExponent > 0 && decimalExponent <= 21) { + final int charLength = Math.min(nDigits, decimalExponent); + sb.append(digits, 0, charLength); + + if (charLength < decimalExponent) { + sb.append(zeroes, 0, decimalExponent - charLength); + } else if (charLength < nDigits) { + sb.append('.'); + sb.append(digits, charLength, nDigits - charLength); + } + } else if (decimalExponent <= 0 && decimalExponent > -6) { + sb.append('0'); + sb.append('.'); + + if (decimalExponent != 0) { + sb.append(zeroes, 0, -decimalExponent); + } + + sb.append(digits, 0, nDigits); + } else { + sb.append(digits[0]); + + if (nDigits > 1) { + sb.append('.'); + sb.append(digits, 1, nDigits - 1); + } + + sb.append('e'); + final int exponent; + int e; + + if (decimalExponent <= 0) { + sb.append('-'); + exponent = e = -decimalExponent + 1; + } else { + sb.append('+'); + exponent = e = decimalExponent - 1; + } + + if (exponent > 99) { + sb.append((char) (e / 100 + '0')); + e %= 100; + } + + if (exponent > 9) { + sb.append((char) (e / 10 + '0')); + e %= 10; + } + + sb.append((char) (e + '0')); + } + } + + return sb.toString(); + } +} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaScript.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaScript.java index 418517558..0cb95cf2c 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaScript.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/ecma/EcmaScript.java @@ -12,7 +12,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library. */ + * License along with this library. + */ package com.jpexs.decompiler.flash.ecma; import com.jpexs.decompiler.flash.action.swf4.ConstantIndex; @@ -443,7 +444,33 @@ public class EcmaScript { } public static String toString(Object o) { - return toString(o, false); + if (o == null) { + return "null"; + } + + if (o instanceof Number) { + // http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1 + Number n = (Number) o; + double dn = n.doubleValue(); + if ((int) dn == dn) { //isRepresentableAsInt + return Integer.toString((int) dn); + } + + if (dn == Double.POSITIVE_INFINITY) { + return "Infinity"; + } + + if (dn == Double.NEGATIVE_INFINITY) { + return "-Infinity"; + } + + if (Double.isNaN(dn)) { + return "NaN"; + } + return EcmaNumberToString.stringFor(dn); + } + + return o.toString(); } public static String toString(Object o, List constantPool) { @@ -453,21 +480,7 @@ public class EcmaScript { return constantPool.get(index); } } - return toString(o, false); - } - - public static String toString(Object o, boolean maxPrecision) { - if (o == null) { - return "null"; - } - - if (o instanceof Number) { - // http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1 - Number n = (Number) o; - return new EcmaFloatingDecimal(n.doubleValue(), maxPrecision).toJavaFormatString(); - } - - return o.toString(); + return toString(o); } public static Double parseFloat(Object string) {