private static void DecShiftLeft(ref MutableDecimal value) { uint c0 = (value.Low & 0x80000000) != 0 ? 1u : 0u; uint c1 = (value.Mid & 0x80000000) != 0 ? 1u : 0u; value.Low = value.Low << 1; value.Mid = (value.Mid << 1) | c0; value.High = (value.High << 1) | c1; }
internal static void DecMul10(ref MutableDecimal value) { MutableDecimal d = value; DecShiftLeft(ref value); DecShiftLeft(ref value); DecAdd(ref value, d); DecShiftLeft(ref value); }
internal static void DecAddInt32(ref MutableDecimal value, uint i) { if (D32AddCarry(ref value.Low, i)) { if (D32AddCarry(ref value.Mid, 1)) { D32AddCarry(ref value.High, 1); } } }
private static void DecAdd(ref MutableDecimal value, MutableDecimal d) { if (D32AddCarry(ref value.Low, d.Low)) { if (D32AddCarry(ref value.Mid, 1)) { D32AddCarry(ref value.High, 1); } } if (D32AddCarry(ref value.Mid, d.Mid)) { D32AddCarry(ref value.High, 1); } D32AddCarry(ref value.High, d.High); }
public static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) { MutableDecimal d = new MutableDecimal(); byte *p = number.UnsafeDigits; int e = number.Scale; if (*p == 0) { // To avoid risking an app-compat issue with pre 4.5 (where some app was illegally using Reflection to examine the internal scale bits), we'll only force // the scale to 0 if the scale was previously positive (previously, such cases were unparsable to a bug.) if (e > 0) { e = 0; } } else { if (e > DECIMAL_PRECISION) { return(false); } while (((e > 0) || ((*p != 0) && (e > -28))) && ((d.High < 0x19999999) || ((d.High == 0x19999999) && ((d.Mid < 0x99999999) || ((d.Mid == 0x99999999) && ((d.Low < 0x99999999) || ((d.Low == 0x99999999) && (*p <= '5')))))))) { DecimalDecCalc.DecMul10(ref d); if (*p != 0) { DecimalDecCalc.DecAddInt32(ref d, (uint)(*p++ - '0')); } e--; } if (*p++ >= '5') { bool round = true; if ((*(p - 1) == '5') && ((*(p - 2) % 2) == 0)) { // Check if previous digit is even, only if the when we are unsure whether hows to do // Banker's rounding. For digits > 5 we will be rounding up anyway. int count = 20; // Look at the next 20 digits to check to round while ((*p == '0') && (count != 0)) { p++; count--; } if ((*p == '\0') || (count == 0)) { round = false;// Do nothing } } if (round) { DecimalDecCalc.DecAddInt32(ref d, 1); if ((d.High | d.Mid | d.Low) == 0) { // If we got here, the magnitude portion overflowed and wrapped back to 0 as the magnitude was already at the MaxValue point: // // 79,228,162,514,264,337,593,543,950,335e+X // // Manually force it to the correct result: // // 7,922,816,251,426,433,759,354,395,034e+(X+1) // // This code path can be reached by trying to parse the following as a Decimal: // // 0.792281625142643375935439503355e28 // d.High = 0x19999999; d.Mid = 0x99999999; d.Low = 0x9999999A; e++; } } } } if (e > 0) { return(false); // Rounding may have caused its own overflow. For example, parsing "0.792281625142643375935439503355e29" will get here. } if (e <= -DECIMAL_PRECISION) { // Parsing a large scale zero can give you more precision than fits in the decimal. // This should only happen for actual zeros or very small numbers that round to zero. d.High = 0; d.Low = 0; d.Mid = 0; d.Scale = DECIMAL_PRECISION - 1; } else { d.Scale = -e; } d.IsNegative = number.IsNegative; value = Unsafe.As <MutableDecimal, decimal>(ref d); return(true); }
// Performs the equivalent of: // // uint modulo = value % 1e9; // value = value / 1e9; // return modulo; // internal static uint DecDivMod1E9(ref MutableDecimal value) { return(D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0, ref value.High), ref value.Mid), ref value.Low)); }