public static BigDecimal DivideToIntegralValue(BigDecimal dividend, BigDecimal divisor, MathContext mc) { int mcPrecision = mc.Precision; int diffPrecision = dividend.Precision - divisor.Precision; int lastPow = BigDecimal.TenPow.Length - 1; long diffScale = (long)dividend.Scale - divisor.Scale; long newScale = diffScale; long quotPrecision = diffPrecision - diffScale + 1; BigInteger quotient; BigInteger remainder; // In special cases it call the dual method if ((mcPrecision == 0) || (dividend.IsZero) || (divisor.IsZero)) { return(DivideToIntegralValue(dividend, divisor)); } // Let be: this = [u1,s1] and divisor = [u2,s2] if (quotPrecision <= 0) { quotient = BigInteger.Zero; } else if (diffScale == 0) { // CASE s1 == s2: to calculate u1 / u2 quotient = dividend.UnscaledValue / divisor.UnscaledValue; } else if (diffScale > 0) { // CASE s1 >= s2: to calculate u1 / (u2 * 10^(s1-s2) quotient = dividend.UnscaledValue / (divisor.UnscaledValue * Multiplication.PowerOf10(diffScale)); // To chose 10^newScale to get a quotient with at least 'mc.precision()' digits newScale = System.Math.Min(diffScale, System.Math.Max(mcPrecision - quotPrecision + 1, 0)); // To calculate: (u1 / (u2 * 10^(s1-s2)) * 10^newScale quotient = quotient * Multiplication.PowerOf10(newScale); } else { // CASE s2 > s1: /* To calculate the minimum power of ten, such that the quotient * (u1 * 10^exp) / u2 has at least 'mc.precision()' digits. */ long exp = System.Math.Min(-diffScale, System.Math.Max((long)mcPrecision - diffPrecision, 0)); long compRemDiv; // Let be: (u1 * 10^exp) / u2 = [q,r] quotient = BigMath.DivideAndRemainder(dividend.UnscaledValue * Multiplication.PowerOf10(exp), divisor.UnscaledValue, out remainder); newScale += exp; // To fix the scale exp = -newScale; // The remaining power of ten // If after division there is a remainder... if ((remainder.Sign != 0) && (exp > 0)) { // Log10(r) + ((s2 - s1) - exp) > mc.precision ? compRemDiv = (new BigDecimal(remainder)).Precision + exp - divisor.Precision; if (compRemDiv == 0) { // To calculate: (r * 10^exp2) / u2 remainder = (remainder * Multiplication.PowerOf10(exp)) / divisor.UnscaledValue; compRemDiv = System.Math.Abs(remainder.Sign); } if (compRemDiv > 0) { // The quotient won't fit in 'mc.precision()' digits // math.06=Division impossible throw new ArithmeticException(Messages.math06); //$NON-NLS-1$ } } } // Fast return if the quotient is zero if (quotient.Sign == 0) { return(BigDecimal.GetZeroScaledBy(diffScale)); } BigInteger strippedBI = quotient; BigDecimal integralValue = new BigDecimal(quotient); long resultPrecision = integralValue.Precision; int i = 1; // To strip trailing zeros until the specified precision is reached while (!BigInteger.TestBit(strippedBI, 0)) { quotient = BigMath.DivideAndRemainder(strippedBI, BigDecimal.TenPow[i], out remainder); if ((remainder.Sign == 0) && ((resultPrecision - i >= mcPrecision) || (newScale - i >= diffScale))) { resultPrecision -= i; newScale -= i; if (i < lastPow) { i++; } strippedBI = quotient; } else { if (i == 1) { break; } i = 1; } } // To check if the result fit in 'mc.precision()' digits if (resultPrecision > mcPrecision) { // math.06=Division impossible throw new ArithmeticException(Messages.math06); //$NON-NLS-1$ } integralValue.Scale = BigDecimal.ToIntScale(newScale); integralValue.SetUnscaledValue(strippedBI); return(integralValue); }
public static bool TryParse(char[] inData, int offset, int len, IFormatProvider provider, out BigDecimal value, out Exception exception) { if (inData == null || inData.Length == 0) { exception = new FormatException("Cannot parse an empty string."); value = null; return(false); } var numberformatInfo = provider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo; if (numberformatInfo == null) { numberformatInfo = NumberFormatInfo.CurrentInfo; } var decSep = numberformatInfo.NumberDecimalSeparator; if (decSep.Length > 1) { exception = new NotSupportedException("More than one decimal separator not yet supported"); value = null; return(false); } var cDecSep = decSep[0]; int begin = offset; // first index to be copied int last = offset + (len - 1); // last index to be copied if ((last >= inData.Length) || (offset < 0) || (len <= 0) || (last < 0)) { exception = new FormatException(); value = null; return(false); } var v = new BigDecimal(); try { var unscaledBuffer = new StringBuilder(len); int bufLength = 0; // To skip a possible '+' symbol if ((offset <= last) && (inData[offset] == '+')) { offset++; begin++; } int counter = 0; bool wasNonZero = false; // Accumulating all digits until a possible decimal point for (; (offset <= last) && (inData[offset] != cDecSep) && (inData[offset] != 'e') && (inData[offset] != 'E'); offset++) { if (!wasNonZero) { if (inData[offset] == '0') { counter++; } else { wasNonZero = true; } } } unscaledBuffer.Append(inData, begin, offset - begin); bufLength += offset - begin; // A decimal point was found if ((offset <= last) && (inData[offset] == cDecSep)) { offset++; // Accumulating all digits until a possible exponent begin = offset; for (; (offset <= last) && (inData[offset] != 'e') && (inData[offset] != 'E'); offset++) { if (!wasNonZero) { if (inData[offset] == '0') { counter++; } else { wasNonZero = true; } } } v.Scale = offset - begin; bufLength += v.Scale; unscaledBuffer.Append(inData, begin, v.Scale); } else { v.Scale = 0; } // An exponent was found if ((offset <= last) && ((inData[offset] == 'e') || (inData[offset] == 'E'))) { offset++; // Checking for a possible sign of scale begin = offset; if ((offset <= last) && (inData[offset] == '+')) { offset++; if ((offset <= last) && (inData[offset] != '-')) { begin++; } } // Accumulating all remaining digits string scaleString = new String(inData, begin, last + 1 - begin); // buffer for scale // Checking if the scale is defined long newScale = (long)v.Scale - Int32.Parse(scaleString, provider); // the new scale v.Scale = (int)newScale; if (newScale != v.Scale) { // math.02=Scale out of range. throw new FormatException(Messages.math02); //$NON-NLS-1$ } } // Parsing the unscaled value if (bufLength < 19) { long smallValue; if (!Int64.TryParse(unscaledBuffer.ToString(), NumberStyles.Integer, provider, out smallValue)) { value = null; exception = new FormatException(); return(false); } v.SmallValue = smallValue; v.BitLength = BigDecimal.CalcBitLength(v.SmallValue); } else { v.SetUnscaledValue(BigInteger.Parse(unscaledBuffer.ToString())); } v.Precision = unscaledBuffer.Length - counter; if (unscaledBuffer[0] == '-') { v.Precision--; } value = v; exception = null; return(true); } catch (Exception ex) { exception = ex; value = null; return(false); } }