private static void ConvertToText(StringBuilder sb, NormalisedDecimal pnd) { NormalisedDecimal rnd = pnd.RoundUnits(); int decExponent = rnd.GetDecimalExponent(); String decimalDigits; if (Math.Abs(decExponent) > 98) { decimalDigits = rnd.GetSignificantDecimalDigitsLastDigitRounded(); if (decimalDigits.Length == 16) { // rounding caused carry decExponent++; } } else { decimalDigits = rnd.GetSignificantDecimalDigits(); } int countSigDigits = CountSignifantDigits(decimalDigits); if (decExponent < 0) { FormatLessThanOne(sb, decimalDigits, decExponent, countSigDigits); } else { FormatGreaterThanOne(sb, decimalDigits, decExponent, countSigDigits); } }
/* 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()); }
/** * assumes both this and other are normalised */ public int CompareNormalised(NormalisedDecimal other) { int cmp = _relativeDecimalExponent - other._relativeDecimalExponent; if (cmp != 0) { return(cmp); } if (_wholePart > other._wholePart) { return(1); } if (_wholePart < other._wholePart) { return(-1); } return(_fractionalPart - other._fractionalPart); }
/** * Convert to an equivalent {@link NormalisedDecimal} representation having 15 decimal digits of precision in the * non-fractional bits of the significand. */ public NormalisedDecimal NormaliseBaseTen() { return(NormalisedDecimal.Create(_significand, _binaryExponent)); }
/** * 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); }