Example #1
0
        public void TestSubnormal()
        {
            ExpandedDouble hd = new ExpandedDouble(0x0000000000000001L);

            if (hd.GetBinaryExponent() == -1023)
            {
                throw new AssertionException("identified bug - subnormal numbers not decoded properly");
            }
            Assert.AreEqual(-1086, hd.GetBinaryExponent());
            BigInteger frac = hd.GetSignificand();
            Assert.AreEqual(64, frac.BitLength());
            Assert.AreEqual(1, frac.BitCount());
        }
Example #2
0
        public void TestNegative()
        {
            ExpandedDouble hd = new ExpandedDouble(unchecked((long)0xC010000000000000L));

            if (hd.GetBinaryExponent() == -2046)
            {
                throw new AssertionException("identified bug - sign bit not masked out of exponent");
            }
            Assert.AreEqual(2, hd.GetBinaryExponent());
            BigInteger frac = hd.GetSignificand();
            Assert.AreEqual(64, frac.BitLength());
            Assert.AreEqual(1, frac.BitCount());
        }
Example #3
0
        /* namespace */
        public static String RawDoubleBitsToText(long pRawBits)
        {
            long rawBits    = pRawBits;
            bool isNegative = rawBits < 0; // sign bit is in the same place for long and double

            if (isNegative)
            {
                rawBits &= 0x7FFFFFFFFFFFFFFFL;
            }
            if (rawBits == 0)
            {
                return(isNegative ? "-0" : "0");
            }
            ExpandedDouble ed = new ExpandedDouble(rawBits);

            if (ed.GetBinaryExponent() < -1022)
            {
                // value is 'denormalised' which means it is less than 2^-1022
                // excel displays all these numbers as zero, even though calculations work OK
                return(isNegative ? "-0" : "0");
            }
            if (ed.GetBinaryExponent() == 1024)
            {
                // Special number NaN /InfInity
                // Normally one would not create HybridDecimal objects from these values
                // except in these cases Excel really tries to render them as if they were normal numbers
                if (rawBits == EXCEL_NAN_BITS)
                {
                    return("3.484840871308E+308");
                }
                // This is where excel really Gets it wrong
                // Special numbers like InfInity and NaN are interpreted according to
                // the standard rules below.
                isNegative = false; // except that the sign bit is ignored
            }
            NormalisedDecimal nd = ed.NormaliseBaseTen();
            StringBuilder     sb = new StringBuilder(MAX_TEXT_LEN + 1);

            if (isNegative)
            {
                sb.Append('-');
            }
            ConvertToText(sb, nd);
            return(sb.ToString());
        }
Example #4
0
        public static bool ConfirmRoundTrip(int i, long rawBitsA)
        {
            double a = BitConverter.Int64BitsToDouble(rawBitsA);
            if (a == 0.0)
            {
                // Can't represent 0.0 or -0.0 with NormalisedDecimal
                return true;
            }
            ExpandedDouble ed1;
            NormalisedDecimal nd2;
            ExpandedDouble ed3;
            try
            {
                ed1 = new ExpandedDouble(rawBitsA);
                nd2 = ed1.NormaliseBaseTen();
                CheckNormaliseBaseTenResult(ed1, nd2);

                ed3 = nd2.NormaliseBaseTwo();
            }
            catch (Exception e)
            {
                Console.WriteLine("example[" + i + "] ("
                        + FormatDoubleAsHex(a) + ") exception: " + e.Message);
                return false;
            }
            if (ed3.GetBinaryExponent() != ed1.GetBinaryExponent())
            {
                Console.WriteLine("example[" + i + "] ("
                        + FormatDoubleAsHex(a) + ") bin exp mismatch");
                return false;
            }
            BigInteger diff = ed3.GetSignificand() - (ed1.GetSignificand()).Abs();
            if (diff.Signum() == 0)
            {
                return true;
            }
            // original quantity only has 53 bits of precision
            // these quantities may have errors in the 64th bit, which hopefully don't make any difference

            if (diff.BitCount() < 2)
            {
                // errors in the 64th bit happen from time to time
                // this is well below the 53 bits of precision required
                return true;
            }

            // but bigger errors are a concern
            Console.WriteLine("example[" + i + "] ("
                    + FormatDoubleAsHex(a) + ") frac mismatch: " + diff.ToString());

            for (int j = -2; j < 3; j++)
            {
                Console.WriteLine((j < 0 ? "" : "+") + j + ": " + GetNearby(ed1, j));
            }
            for (int j = -2; j < 3; j++)
            {
                Console.WriteLine((j < 0 ? "" : "+") + j + ": " + GetNearby(nd2, j));
            }

            return false;
        }
Example #5
0
        private static void CheckNormaliseBaseTenResult(ExpandedDouble orig, NormalisedDecimal result)
        {
            String sigDigs = result.GetSignificantDecimalDigits();
            BigInteger frac = orig.GetSignificand();
            while (frac.BitLength() + orig.GetBinaryExponent() < 200)
            {
                frac = frac * (BIG_POW_10);
            }
            int binaryExp = orig.GetBinaryExponent() - orig.GetSignificand().BitLength();

            String origDigs = (frac << (binaryExp + 1)).ToString(10);

            if (!origDigs.StartsWith(sigDigs))
            {
                throw new AssertionException("Expected '" + origDigs + "' but got '" + sigDigs + "'.");
            }

            double dO = Double.Parse("0." + origDigs.Substring(sigDigs.Length));
            double d1 = Double.Parse(result.GetFractionalPart().ToString());
            BigInteger subDigsO = new BigInteger((int)(dO * 32768 + 0.5));
            BigInteger subDigsB = new BigInteger((int)(d1 * 32768 + 0.5));

            if (subDigsO.Equals(subDigsB))
            {
                return;
            }
            BigInteger diff = (subDigsB - subDigsO).Abs();
            if (diff.IntValue() > 100)
            {
                // 100/32768 ~= 0.003
                throw new AssertionException("minor mistake");
            }
        }
Example #6
0
 public static BigInteger GetNearby(ExpandedDouble hd, int offset)
 {
     return GetNearby(hd.GetSignificand(), hd.GetBinaryExponent(), offset);
 }
Example #7
0
 public static String GetBaseDecimal(ExpandedDouble hd)
 {
     /*int gg = 64 - hd.GetBinaryExponent() - 1;
     BigDecimal bd = new BigDecimal(hd.GetSignificand()).divide(new BigDecimal(BigInteger.ONE<<gg));
     int excessPrecision = bd.precision() - 23;
     if (excessPrecision > 0)
     {
         bd = bd.SetScale(bd.scale() - excessPrecision, BigDecimal.ROUND_HALF_UP);
     }
     return bd.unscaledValue().ToString();*/
     throw new NotImplementedException("This Method need BigDecimal class");
 }
Example #8
0
        /**
         * This class attempts to reproduce Excel's behaviour for comparing numbers.  Results are
         * mostly the same as those from {@link Double#compare(double, double)} but with some
         * rounding.  For numbers that are very close, this code converts to a format having 15
         * decimal digits of precision and a decimal exponent, before completing the comparison.
         * <p/>
         * In Excel formula evaluation, expressions like "(0.06-0.01)=0.05" evaluate to "TRUE" even
         * though the equivalent java expression is <c>false</c>.  In examples like this,
         * Excel achieves the effect by having additional logic for comparison operations.
         * <p/>
         * <p/>
         * Note - Excel also gives special treatment to expressions like "0.06-0.01-0.05" which
         * evaluates to "0" (in java, rounding anomalies give a result of 6.9E-18).  The special
         * behaviour here is for different reasons to the example above:  If the last operator in a
         * cell formula is '+' or '-' and the result is less than 2<sup>50</sup> times smaller than
         * first operand, the result is rounded to zero.
         * Needless to say, the two rules are not consistent and it is relatively easy to find
         * examples that satisfy<br/>
         * "A=B" is "TRUE" but "A-B" is not "0"<br/>
         * and<br/>
         * "A=B" is "FALSE" but "A-B" is "0"<br/>
         * <br/>
         * This rule (for rounding the result of a final addition or subtraction), has not been
         * implemented in POI (as of Jul-2009).
         *
         * @return <code>negative, 0, or positive</code> according to the standard Excel comparison
         * of values <c>a</c> and <c>b</c>.
         */
        public static int Compare(double a, double b)
        {
            long rawBitsA = BitConverter.DoubleToInt64Bits(a);
            long rawBitsB = BitConverter.DoubleToInt64Bits(b);

            int biasedExponentA = IEEEDouble.GetBiasedExponent(rawBitsA);
            int biasedExponentB = IEEEDouble.GetBiasedExponent(rawBitsB);

            if (biasedExponentA == IEEEDouble.BIASED_EXPONENT_SPECIAL_VALUE)
            {
                throw new ArgumentException("Special double values are not allowed: " + ToHex(a));
            }
            if (biasedExponentB == IEEEDouble.BIASED_EXPONENT_SPECIAL_VALUE)
            {
                throw new ArgumentException("Special double values are not allowed: " + ToHex(a));
            }

            int cmp;

            // sign bit is in the same place for long and double:
            bool aIsNegative = rawBitsA < 0;
            bool bIsNegative = rawBitsB < 0;

            // compare signs
            if (aIsNegative != bIsNegative)
            {
                // Excel seems to have 'normal' comparison behaviour around zero (no rounding)
                // even -0.0 < +0.0 (which is not quite the initial conclusion of bug 47198)
                return(aIsNegative ? -1 : +1);
            }

            // then compare magnitudes (IEEE 754 has exponent bias specifically to allow this)
            cmp = biasedExponentA - biasedExponentB;
            int absExpDiff = Math.Abs(cmp);

            if (absExpDiff > 1)
            {
                return(aIsNegative ? -cmp : cmp);
            }

            if (absExpDiff == 1)
            {
                // special case exponent differs by 1.  There is still a chance that with rounding the two quantities could end up the same
            }
            else
            {
                // else - sign and exponents equal
                if (rawBitsA == rawBitsB)
                {
                    // fully equal - exit here
                    return(0);
                }
            }
            if (biasedExponentA == 0)
            {
                if (biasedExponentB == 0)
                {
                    return(CompareSubnormalNumbers(rawBitsA & IEEEDouble.FRAC_MASK, rawBitsB & IEEEDouble.FRAC_MASK, aIsNegative));
                }
                // else biasedExponentB is 1
                return(-CompareAcrossSubnormalThreshold(rawBitsB, rawBitsA, aIsNegative));
            }
            if (biasedExponentB == 0)
            {
                // else biasedExponentA is 1
                return(+CompareAcrossSubnormalThreshold(rawBitsA, rawBitsB, aIsNegative));
            }

            // sign and exponents same, but fractional bits are different

            ExpandedDouble    edA = ExpandedDouble.FromRawBitsAndExponent(rawBitsA, biasedExponentA - IEEEDouble.EXPONENT_BIAS);
            ExpandedDouble    edB = ExpandedDouble.FromRawBitsAndExponent(rawBitsB, biasedExponentB - IEEEDouble.EXPONENT_BIAS);
            NormalisedDecimal ndA = edA.NormaliseBaseTen().RoundUnits();
            NormalisedDecimal ndB = edB.NormaliseBaseTen().RoundUnits();

            cmp = ndA.CompareNormalised(ndB);
            if (aIsNegative)
            {
                return(-cmp);
            }
            return(cmp);
        }
Example #9
0
        /* namespace */
        public static String RawDoubleBitsToText(long pRawBits)
        {

            long rawBits = pRawBits;
            bool isNegative = rawBits < 0; // sign bit is in the same place for long and double
            if (isNegative)
            {
                rawBits &= 0x7FFFFFFFFFFFFFFFL;
            }
            if (rawBits == 0)
            {
                return isNegative ? "-0" : "0";
            }
            ExpandedDouble ed = new ExpandedDouble(rawBits);
            if (ed.GetBinaryExponent() < -1022)
            {
                // value is 'denormalised' which means it is less than 2^-1022
                // excel displays all these numbers as zero, even though calculations work OK
                return isNegative ? "-0" : "0";
            }
            if (ed.GetBinaryExponent() == 1024)
            {
                // Special number NaN /InfInity
                // Normally one would not create HybridDecimal objects from these values
                // except in these cases Excel really tries to render them as if they were normal numbers
                if (rawBits == EXCEL_NAN_BITS)
                {
                    return "3.484840871308E+308";
                }
                // This is where excel really Gets it wrong
                // Special numbers like InfInity and NaN are interpreted according to
                // the standard rules below.
                isNegative = false; // except that the sign bit is ignored
            }
            NormalisedDecimal nd = ed.NormaliseBaseTen();
            StringBuilder sb = new StringBuilder(MAX_TEXT_LEN + 1);
            if (isNegative)
            {
                sb.Append('-');
            }
            ConvertToText(sb, nd);
            return sb.ToString();
        }