// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #if !__MonoCS__ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; namespace System { public partial struct Decimal { // Low level accessors used by a DecCalc and formatting internal uint High => (uint)hi; internal uint Low => (uint)lo; internal uint Mid => (uint)mid; internal bool IsNegative => flags < 0; internal int Scale => (byte)(flags >> ScaleShift); private ulong Low64 => BitConverter.IsLittleEndian ? (ulong)ulomidLE : ((ulong)Mid << 32) | Low; private static ref DecCalc AsMutable(ref decimal d) => ref Unsafe.As(ref d); #region APIs need by number formatting. internal static uint DecDivMod1E9(ref decimal value) { return DecCalc.DecDivMod1E9(ref AsMutable(ref value)); } #endregion /// /// Class that contains all the mathematical calculations for decimal. Most of which have been ported from oleaut32. /// [StructLayout(LayoutKind.Explicit)] private struct DecCalc { // NOTE: Do not change the offsets of these fields. This structure must have the same layout as Decimal. [FieldOffset(0)] private uint uflags; [FieldOffset(4)] private uint uhi; [FieldOffset(8)] private uint ulo; [FieldOffset(12)] private uint umid; /// /// The low and mid fields combined in little-endian order /// [FieldOffset(8)] private ulong ulomidLE; private uint High { get => uhi; set => uhi = value; } private uint Low { get => ulo; set => ulo = value; } private uint Mid { get => umid; set => umid = value; } private bool IsNegative => (int)uflags < 0; private int Scale => (byte)(uflags >> ScaleShift); private ulong Low64 { get { return BitConverter.IsLittleEndian ? ulomidLE : (((ulong)umid << 32) | ulo); } set { if (BitConverter.IsLittleEndian) { ulomidLE = value; } else { umid = (uint)(value >> 32); ulo = (uint)value; } } } private const uint SignMask = 0x80000000; private const uint ScaleMask = 0x00FF0000; private const int DEC_SCALE_MAX = 28; private const uint TenToPowerNine = 1000000000; private const ulong TenToPowerEighteen = 1000000000000000000; // The maximum power of 10 that a 32 bit integer can store private const int MaxInt32Scale = 9; // The maximum power of 10 that a 64 bit integer can store private const int MaxInt64Scale = 19; // Fast access for 10^n where n is 0-9 private static readonly uint[] s_powers10 = new uint[] { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; // Fast access for 10^n where n is 1-19 private static readonly ulong[] s_ulongPowers10 = new ulong[] { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000, }; private static readonly double[] s_doublePowers10 = new double[] { 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80 }; #region Decimal Math Helpers private static unsafe uint GetExponent(float f) { // Based on pulling out the exp from this single struct layout //typedef struct { // ULONG mant:23; // ULONG exp:8; // ULONG sign:1; //} SNGSTRUCT; return (byte)(*(uint*)&f >> 23); } private static unsafe uint GetExponent(double d) { // Based on pulling out the exp from this double struct layout //typedef struct { // DWORDLONG mant:52; // DWORDLONG signexp:12; // } DBLSTRUCT; return (uint)(*(ulong*)&d >> 52) & 0x7FFu; } private static ulong UInt32x32To64(uint a, uint b) { return (ulong)a * (ulong)b; } private static void UInt64x64To128(ulong a, ulong b, ref DecCalc result) { ulong low = UInt32x32To64((uint)a, (uint)b); // lo partial prod ulong mid = UInt32x32To64((uint)a, (uint)(b >> 32)); // mid 1 partial prod ulong high = UInt32x32To64((uint)(a >> 32), (uint)(b >> 32)); high += mid >> 32; low += mid <<= 32; if (low < mid) // test for carry high++; mid = UInt32x32To64((uint)(a >> 32), (uint)b); high += mid >> 32; low += mid <<= 32; if (low < mid) // test for carry high++; if (high > uint.MaxValue) throw new OverflowException(SR.Overflow_Decimal); result.Low64 = low; result.High = (uint)high; } /// /// Do full divide, yielding 96-bit result and 32-bit remainder. /// /// 96-bit dividend as array of uints, least-sig first /// 32-bit divisor /// Returns remainder. Quotient overwrites dividend. private static uint Div96By32(ref Buf12 bufNum, uint den) { // TODO: https://github.com/dotnet/coreclr/issues/3439 ulong tmp, div; if (bufNum.U2 != 0) { tmp = bufNum.High64; div = tmp / den; bufNum.High64 = div; tmp = ((tmp - (uint)div * den) << 32) | bufNum.U0; if (tmp == 0) return 0; uint div32 = (uint)(tmp / den); bufNum.U0 = div32; return (uint)tmp - div32 * den; } tmp = bufNum.Low64; if (tmp == 0) return 0; div = tmp / den; bufNum.Low64 = div; return (uint)(tmp - div * den); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Div96ByConst(ref ulong high64, ref uint low, uint pow) { #if BIT64 ulong div64 = high64 / pow; uint div = (uint)((((high64 - div64 * pow) << 32) + low) / pow); if (low == div * pow) { high64 = div64; low = div; return true; } #else // 32-bit RyuJIT doesn't convert 64-bit division by constant into multiplication by reciprocal. Do half-width divisions instead. Debug.Assert(pow <= ushort.MaxValue); uint num, mid32, low16, div; if (high64 <= uint.MaxValue) { num = (uint)high64; mid32 = num / pow; num = (num - mid32 * pow) << 16; num += low >> 16; low16 = num / pow; num = (num - low16 * pow) << 16; num += (ushort)low; div = num / pow; if (num == div * pow) { high64 = mid32; low = (low16 << 16) + div; return true; } } else { num = (uint)(high64 >> 32); uint high32 = num / pow; num = (num - high32 * pow) << 16; num += (uint)high64 >> 16; mid32 = num / pow; num = (num - mid32 * pow) << 16; num += (ushort)high64; div = num / pow; num = (num - div * pow) << 16; mid32 = div + (mid32 << 16); num += low >> 16; low16 = num / pow; num = (num - low16 * pow) << 16; num += (ushort)low; div = num / pow; if (num == div * pow) { high64 = ((ulong)high32 << 32) | mid32; low = (low16 << 16) + div; return true; } } #endif return false; } /// /// Normalize (unscale) the number by trying to divide out 10^8, 10^4, 10^2, and 10^1. /// If a division by one of these powers returns a zero remainder, then we keep the quotient. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Unscale(ref uint low, ref ulong high64, ref int scale) { // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 we can extract. // We use this as a quick test on whether to try a given power. #if BIT64 while ((byte)low == 0 && scale >= 8 && Div96ByConst(ref high64, ref low, 100000000)) scale -= 8; if ((low & 0xF) == 0 && scale >= 4 && Div96ByConst(ref high64, ref low, 10000)) scale -= 4; #else while ((low & 0xF) == 0 && scale >= 4 && Div96ByConst(ref high64, ref low, 10000)) scale -= 4; #endif if ((low & 3) == 0 && scale >= 2 && Div96ByConst(ref high64, ref low, 100)) scale -= 2; if ((low & 1) == 0 && scale >= 1 && Div96ByConst(ref high64, ref low, 10)) scale--; } /// /// Do partial divide, yielding 32-bit result and 64-bit remainder. /// Divisor must be larger than upper 64 bits of dividend. /// /// 96-bit dividend as array of uints, least-sig first /// 64-bit divisor /// Returns quotient. Remainder overwrites lower 64-bits of dividend. private static uint Div96By64(ref Buf12 bufNum, ulong den) { Debug.Assert(den > bufNum.High64); uint quo; ulong num; uint num2 = bufNum.U2; if (num2 == 0) { num = bufNum.Low64; if (num < den) // Result is zero. Entire dividend is remainder. return 0; // TODO: https://github.com/dotnet/coreclr/issues/3439 quo = (uint)(num / den); num -= quo * den; // remainder bufNum.Low64 = num; return quo; } uint denHigh32 = (uint)(den >> 32); if (num2 >= denHigh32) { // Divide would overflow. Assume a quotient of 2^32, and set // up remainder accordingly. // num = bufNum.Low64; num -= den << 32; quo = 0; // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // do { quo--; num += den; } while (num >= den); bufNum.Low64 = num; return quo; } // Hardware divide won't overflow // ulong num64 = bufNum.High64; if (num64 < denHigh32) // Result is zero. Entire dividend is remainder. // return 0; // TODO: https://github.com/dotnet/coreclr/issues/3439 quo = (uint)(num64 / denHigh32); num = bufNum.U0 | ((num64 - quo * denHigh32) << 32); // remainder // Compute full remainder, rem = dividend - (quo * divisor). // ulong prod = UInt32x32To64(quo, (uint)den); // quo * lo divisor num -= prod; if (num > ~prod) { // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // do { quo--; num += den; } while (num >= den); } bufNum.Low64 = num; return quo; } /// /// Do partial divide, yielding 32-bit result and 96-bit remainder. /// Top divisor uint must be larger than top dividend uint. This is /// assured in the initial call because the divisor is normalized /// and the dividend can't be. In subsequent calls, the remainder /// is multiplied by 10^9 (max), so it can be no more than 1/4 of /// the divisor which is effectively multiplied by 2^32 (4 * 10^9). /// /// 128-bit dividend as array of uints, least-sig first /// 96-bit divisor /// Returns quotient. Remainder overwrites lower 96-bits of dividend. private static uint Div128By96(ref Buf16 bufNum, ref Buf12 bufDen) { Debug.Assert(bufDen.U2 > bufNum.U3); ulong dividend = bufNum.High64; uint den = bufDen.U2; if (dividend < den) // Result is zero. Entire dividend is remainder. // return 0; // TODO: https://github.com/dotnet/coreclr/issues/3439 uint quo = (uint)(dividend / den); uint remainder = (uint)dividend - quo * den; // Compute full remainder, rem = dividend - (quo * divisor). // ulong prod1 = UInt32x32To64(quo, bufDen.U0); // quo * lo divisor ulong prod2 = UInt32x32To64(quo, bufDen.U1); // quo * mid divisor prod2 += prod1 >> 32; prod1 = (uint)prod1 | (prod2 << 32); prod2 >>= 32; ulong num = bufNum.Low64; num -= prod1; remainder -= (uint)prod2; // Propagate carries // if (num > ~prod1) { remainder--; if (remainder < ~(uint)prod2) goto PosRem; } else if (remainder <= ~(uint)prod2) goto PosRem; { // Remainder went negative. Add divisor back in until it's positive, // a max of 2 times. // prod1 = bufDen.Low64; for (;;) { quo--; num += prod1; remainder += den; if (num < prod1) { // Detected carry. Check for carry out of top // before adding it in. // if (remainder++ < den) break; } if (remainder < den) break; // detected carry } } PosRem: bufNum.Low64 = num; bufNum.U2 = remainder; return quo; } /// /// Multiply the two numbers. The low 96 bits of the result overwrite /// the input. The last 32 bits of the product are the return value. /// /// 96-bit number as array of uints, least-sig first /// Scale factor to multiply by /// Returns highest 32 bits of product private static uint IncreaseScale(ref Buf12 bufNum, uint power) { ulong tmp = UInt32x32To64(bufNum.U0, power); bufNum.U0 = (uint)tmp; tmp >>= 32; tmp += UInt32x32To64(bufNum.U1, power); bufNum.U1 = (uint)tmp; tmp >>= 32; tmp += UInt32x32To64(bufNum.U2, power); bufNum.U2 = (uint)tmp; return (uint)(tmp >> 32); } private static void IncreaseScale64(ref Buf12 bufNum, uint power) { ulong tmp = UInt32x32To64(bufNum.U0, power); bufNum.U0 = (uint)tmp; tmp >>= 32; tmp += UInt32x32To64(bufNum.U1, power); bufNum.High64 = tmp; } /// /// See if we need to scale the result to fit it in 96 bits. /// Perform needed scaling. Adjust scale factor accordingly. /// /// Array of uints with value, least-significant first /// Index of last non-zero value in bufRes /// Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX /// Returns new scale factor. bufRes updated in place, always 3 uints. private static unsafe int ScaleResult(Buf24* bufRes, uint hiRes, int scale) { Debug.Assert(hiRes < bufRes->Length); uint* result = (uint*)bufRes; // See if we need to scale the result. The combined scale must // be <= DEC_SCALE_MAX and the upper 96 bits must be zero. // // Start by figuring a lower bound on the scaling needed to make // the upper 96 bits zero. hiRes is the index into result[] // of the highest non-zero uint. // int newScale = 0; if (hiRes > 2) { newScale = (int)hiRes * 32 - 64 - 1; newScale -= LeadingZeroCount(result[hiRes]); // Multiply bit position by log10(2) to figure it's power of 10. // We scale the log by 256. log(2) = .30103, * 256 = 77. Doing this // with a multiply saves a 96-byte lookup table. The power returned // is <= the power of the number, so we must add one power of 10 // to make it's integer part zero after dividing by 256. // // Note: the result of this multiplication by an approximation of // log10(2) have been exhaustively checked to verify it gives the // correct result. (There were only 95 to check...) // newScale = ((newScale * 77) >> 8) + 1; // newScale = min scale factor to make high 96 bits zero, 0 - 29. // This reduces the scale factor of the result. If it exceeds the // current scale of the result, we'll overflow. // if (newScale > scale) goto ThrowOverflow; } // Make sure we scale by enough to bring the current scale factor // into valid range. // if (newScale < scale - DEC_SCALE_MAX) newScale = scale - DEC_SCALE_MAX; if (newScale != 0) { // Scale by the power of 10 given by newScale. Note that this is // NOT guaranteed to bring the number within 96 bits -- it could // be 1 power of 10 short. // scale -= newScale; uint sticky = 0; uint quotient, remainder = 0; for (;;) { sticky |= remainder; // record remainder as sticky bit uint power; // Scaling loop specialized for each power of 10 because division by constant is an order of magnitude faster (especially for 64-bit division that's actually done by 128bit DIV on x64) switch (newScale) { case 1: power = DivByConst(result, hiRes, out quotient, out remainder, 10); break; case 2: power = DivByConst(result, hiRes, out quotient, out remainder, 100); break; case 3: power = DivByConst(result, hiRes, out quotient, out remainder, 1000); break; case 4: power = DivByConst(result, hiRes, out quotient, out remainder, 10000); break; #if BIT64 case 5: power = DivByConst(result, hiRes, out quotient, out remainder, 100000); break; case 6: power = DivByConst(result, hiRes, out quotient, out remainder, 1000000); break; case 7: power = DivByConst(result, hiRes, out quotient, out remainder, 10000000); break; case 8: power = DivByConst(result, hiRes, out quotient, out remainder, 100000000); break; default: power = DivByConst(result, hiRes, out quotient, out remainder, TenToPowerNine); break; #else default: goto case 4; #endif } result[hiRes] = quotient; // If first quotient was 0, update hiRes. // if (quotient == 0 && hiRes != 0) hiRes--; #if BIT64 newScale -= MaxInt32Scale; #else newScale -= 4; #endif if (newScale > 0) continue; // scale some more // If we scaled enough, hiRes would be 2 or less. If not, // divide by 10 more. // if (hiRes > 2) { if (scale == 0) goto ThrowOverflow; newScale = 1; scale--; continue; // scale by 10 } // Round final result. See if remainder >= 1/2 of divisor. // If remainder == 1/2 divisor, round up if odd or sticky bit set. // power >>= 1; // power of 10 always even if (power <= remainder && (power < remainder || ((result[0] & 1) | sticky) != 0) && ++result[0] == 0) { uint cur = 0; do { Debug.Assert(cur + 1 < bufRes->Length); } while (++result[++cur] == 0); if (cur > 2) { // The rounding caused us to carry beyond 96 bits. // Scale by 10 more. // if (scale == 0) goto ThrowOverflow; hiRes = cur; sticky = 0; // no sticky bit remainder = 0; // or remainder newScale = 1; scale--; continue; // scale by 10 } } break; } // for(;;) } return scale; ThrowOverflow: throw new OverflowException(SR.Overflow_Decimal); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe uint DivByConst(uint* result, uint hiRes, out uint quotient, out uint remainder, uint power) { uint high = result[hiRes]; remainder = high - (quotient = high / power) * power; for (uint i = hiRes - 1; (int)i >= 0; i--) { #if BIT64 ulong num = result[i] + ((ulong)remainder << 32); remainder = (uint)num - (result[i] = (uint)(num / power)) * power; #else // 32-bit RyuJIT doesn't convert 64-bit division by constant into multiplication by reciprocal. Do half-width divisions instead. Debug.Assert(power <= ushort.MaxValue); int low16 = BitConverter.IsLittleEndian ? 0 : 2, high16 = BitConverter.IsLittleEndian ? 2 : 0; // byte* is used here because Roslyn doesn't do constant propagation for pointer arithmetic uint num = *(ushort*)((byte*)result + i * 4 + high16) + (remainder << 16); uint div = num / power; remainder = num - div * power; *(ushort*)((byte*)result + i * 4 + high16) = (ushort)div; num = *(ushort*)((byte*)result + i * 4 + low16) + (remainder << 16); div = num / power; remainder = num - div * power; *(ushort*)((byte*)result + i * 4 + low16) = (ushort)div; #endif } return power; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LeadingZeroCount(uint value) { Debug.Assert(value > 0); int c = 1; if ((value & 0xFFFF0000) == 0) { value <<= 16; c += 16; } if ((value & 0xFF000000) == 0) { value <<= 8; c += 8; } if ((value & 0xF0000000) == 0) { value <<= 4; c += 4; } if ((value & 0xC0000000) == 0) { value <<= 2; c += 2; } return c + ((int)value >> 31); } /// /// Adjust the quotient to deal with an overflow. /// We need to divide by 10, feed in the high bit to undo the overflow and then round as required. /// private static int OverflowUnscale(ref Buf12 bufQuo, int scale, bool sticky) { if (--scale < 0) throw new OverflowException(SR.Overflow_Decimal); Debug.Assert(bufQuo.U2 == 0); // We have overflown, so load the high bit with a one. const ulong highbit = 1UL << 32; bufQuo.U2 = (uint)(highbit / 10); ulong tmp = ((highbit % 10) << 32) + bufQuo.U1; uint div = (uint)(tmp / 10); bufQuo.U1 = div; tmp = ((tmp - div * 10) << 32) + bufQuo.U0; div = (uint)(tmp / 10); bufQuo.U0 = div; uint remainder = (uint)(tmp - div * 10); // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up if (remainder > 5 || remainder == 5 && (sticky || (bufQuo.U0 & 1) != 0)) Add32To96(ref bufQuo, 1); return scale; } /// /// Determine the max power of 10, <= 9, that the quotient can be scaled /// up by and still fit in 96 bits. /// /// 96-bit quotient /// Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX-1 /// power of 10 to scale by private static int SearchScale(ref Buf12 bufQuo, int scale) { const uint OVFL_MAX_9_HI = 4; const uint OVFL_MAX_8_HI = 42; const uint OVFL_MAX_7_HI = 429; const uint OVFL_MAX_6_HI = 4294; const uint OVFL_MAX_5_HI = 42949; const uint OVFL_MAX_4_HI = 429496; const uint OVFL_MAX_3_HI = 4294967; const uint OVFL_MAX_2_HI = 42949672; const uint OVFL_MAX_1_HI = 429496729; const ulong OVFL_MAX_9_MIDLO = 5441186219426131129; uint resHi = bufQuo.U2; ulong resMidLo = bufQuo.Low64; int curScale = 0; // Quick check to stop us from trying to scale any more. // if (resHi > OVFL_MAX_1_HI) { goto HaveScale; } var powerOvfl = PowerOvflValues; if (scale > DEC_SCALE_MAX - 9) { // We can't scale by 10^9 without exceeding the max scale factor. // See if we can scale to the max. If not, we'll fall into // standard search for scale factor. // curScale = DEC_SCALE_MAX - scale; if (resHi < powerOvfl[curScale - 1].Hi) goto HaveScale; } else if (resHi < OVFL_MAX_9_HI || resHi == OVFL_MAX_9_HI && resMidLo <= OVFL_MAX_9_MIDLO) return 9; // Search for a power to scale by < 9. Do a binary search. // if (resHi > OVFL_MAX_5_HI) { if (resHi > OVFL_MAX_3_HI) { curScale = 2; if (resHi > OVFL_MAX_2_HI) curScale--; } else { curScale = 4; if (resHi > OVFL_MAX_4_HI) curScale--; } } else { if (resHi > OVFL_MAX_7_HI) { curScale = 6; if (resHi > OVFL_MAX_6_HI) curScale--; } else { curScale = 8; if (resHi > OVFL_MAX_8_HI) curScale--; } } // In all cases, we already found we could not use the power one larger. // So if we can use this power, it is the biggest, and we're done. If // we can't use this power, the one below it is correct for all cases // unless it's 10^1 -- we might have to go to 10^0 (no scaling). // if (resHi == powerOvfl[curScale - 1].Hi && resMidLo > powerOvfl[curScale - 1].MidLo) curScale--; HaveScale: // curScale = largest power of 10 we can scale by without overflow, // curScale < 9. See if this is enough to make scale factor // positive if it isn't already. // if (curScale + scale < 0) throw new OverflowException(SR.Overflow_Decimal); return curScale; } /// /// Add a 32-bit uint to an array of 3 uints representing a 96-bit integer. /// /// Returns false if there is an overflow private static bool Add32To96(ref Buf12 bufNum, uint value) { if ((bufNum.Low64 += value) < value) { if (++bufNum.U2 == 0) return false; } return true; } /// /// Adds or subtracts two decimal values. /// On return, d1 contains the result of the operation and d2 is trashed. /// /// True means subtract and false means add. internal static unsafe void DecAddSub(ref DecCalc d1, ref DecCalc d2, bool sign) { ulong low64 = d1.Low64; uint high = d1.High, flags = d1.uflags, d2flags = d2.uflags; uint xorflags = d2flags ^ flags; sign ^= (xorflags & SignMask) != 0; if ((xorflags & ScaleMask) == 0) { // Scale factors are equal, no alignment necessary. // goto AlignedAdd; } else { // Scale factors are not equal. Assume that a larger scale // factor (more decimal places) is likely to mean that number // is smaller. Start by guessing that the right operand has // the larger scale factor. The result will have the larger // scale factor. // uint d1flags = flags; flags = d2flags & ScaleMask | flags & SignMask; // scale factor of "smaller", but sign of "larger" int scale = (int)(flags - d1flags) >> ScaleShift; if (scale < 0) { // Guessed scale factor wrong. Swap operands. // scale = -scale; flags = d1flags; if (sign) flags ^= SignMask; low64 = d2.Low64; high = d2.High; d2 = d1; } uint power; ulong tmp64, tmpLow; // d1 will need to be multiplied by 10^scale so // it will have the same scale as d2. We could be // extending it to up to 192 bits of precision. // Scan for zeros in the upper words. // if (high == 0) { if (low64 <= uint.MaxValue) { if ((uint)low64 == 0) { // Left arg is zero, return right. // uint signFlags = flags & SignMask; if (sign) signFlags ^= SignMask; d1 = d2; d1.uflags = d2.uflags & ScaleMask | signFlags; return; } do { if (scale <= MaxInt32Scale) { low64 = UInt32x32To64((uint)low64, s_powers10[scale]); goto AlignedAdd; } scale -= MaxInt32Scale; low64 = UInt32x32To64((uint)low64, TenToPowerNine); } while (low64 <= uint.MaxValue); } do { power = TenToPowerNine; if (scale < MaxInt32Scale) power = s_powers10[scale]; tmpLow = UInt32x32To64((uint)low64, power); tmp64 = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32); low64 = (uint)tmpLow + (tmp64 << 32); high = (uint)(tmp64 >> 32); if ((scale -= MaxInt32Scale) <= 0) goto AlignedAdd; } while (high == 0); } while (true) { // Scaling won't make it larger than 4 uints // power = TenToPowerNine; if (scale < MaxInt32Scale) power = s_powers10[scale]; tmpLow = UInt32x32To64((uint)low64, power); tmp64 = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32); low64 = (uint)tmpLow + (tmp64 << 32); tmp64 >>= 32; tmp64 += UInt32x32To64(high, power); scale -= MaxInt32Scale; if (tmp64 > uint.MaxValue) break; high = (uint)tmp64; // Result fits in 96 bits. Use standard aligned add. if (scale <= 0) goto AlignedAdd; } // Have to scale by a bunch. Move the number to a buffer where it has room to grow as it's scaled. // Buf24 bufNum; _ = &bufNum; // workaround for CS0165 bufNum.Low64 = low64; bufNum.Mid64 = tmp64; uint hiProd = 3; // Scaling loop, up to 10^9 at a time. hiProd stays updated with index of highest non-zero uint. // for (; scale > 0; scale -= MaxInt32Scale) { power = TenToPowerNine; if (scale < MaxInt32Scale) power = s_powers10[scale]; tmp64 = 0; uint* rgulNum = (uint*)&bufNum; for (uint cur = 0; ;) { Debug.Assert(cur < bufNum.Length); tmp64 += UInt32x32To64(rgulNum[cur], power); rgulNum[cur] = (uint)tmp64; cur++; tmp64 >>= 32; if (cur > hiProd) break; } if ((uint)tmp64 != 0) { // We're extending the result by another uint. Debug.Assert(hiProd + 1 < bufNum.Length); rgulNum[++hiProd] = (uint)tmp64; } } // Scaling complete, do the add. Could be subtract if signs differ. // tmp64 = bufNum.Low64; low64 = d2.Low64; uint tmpHigh = bufNum.U2; high = d2.High; if (sign) { // Signs differ, subtract. // low64 = tmp64 - low64; high = tmpHigh - high; // Propagate carry // if (low64 > tmp64) { high--; if (high < tmpHigh) goto NoCarry; } else if (high <= tmpHigh) goto NoCarry; // Carry the subtraction into the higher bits. // uint* number = (uint*)&bufNum; uint cur = 3; do { Debug.Assert(cur < bufNum.Length); } while (number[cur++]-- == 0); Debug.Assert(hiProd < bufNum.Length); if (number[hiProd] == 0 && --hiProd <= 2) goto ReturnResult; } else { // Signs the same, add. // low64 += tmp64; high += tmpHigh; // Propagate carry // if (low64 < tmp64) { high++; if (high > tmpHigh) goto NoCarry; } else if (high >= tmpHigh) goto NoCarry; uint* number = (uint*)&bufNum; for (uint cur = 3; ++number[cur++] == 0;) { Debug.Assert(cur < bufNum.Length); if (hiProd < cur) { number[cur] = 1; hiProd = cur; break; } } } NoCarry: bufNum.Low64 = low64; bufNum.U2 = high; scale = ScaleResult(&bufNum, hiProd, (byte)(flags >> ScaleShift)); flags = (flags & ~ScaleMask) | ((uint)scale << ScaleShift); low64 = bufNum.Low64; high = bufNum.U2; goto ReturnResult; } SignFlip: { // Got negative result. Flip its sign. flags ^= SignMask; high = ~high; low64 = (ulong)-(long)low64; if (low64 == 0) high++; goto ReturnResult; } AlignedScale: { // The addition carried above 96 bits. // Divide the value by 10, dropping the scale factor. // if ((flags & ScaleMask) == 0) throw new OverflowException(SR.Overflow_Decimal); flags -= 1 << ScaleShift; const uint den = 10; ulong num = high + (1UL << 32); high = (uint)(num / den); num = ((num - high * den) << 32) + (low64 >> 32); uint div = (uint)(num / den); num = ((num - div * den) << 32) + (uint)low64; low64 = div; low64 <<= 32; div = (uint)(num / den); low64 += div; div = (uint)num - div * den; // See if we need to round up. // if (div >= 5 && (div > 5 || (low64 & 1) != 0)) { if (++low64 == 0) high++; } goto ReturnResult; } AlignedAdd: { ulong d1Low64 = low64; uint d1High = high; if (sign) { // Signs differ - subtract // low64 = d1Low64 - d2.Low64; high = d1High - d2.High; // Propagate carry // if (low64 > d1Low64) { high--; if (high >= d1High) goto SignFlip; } else if (high > d1High) goto SignFlip; } else { // Signs are the same - add // low64 = d1Low64 + d2.Low64; high = d1High + d2.High; // Propagate carry // if (low64 < d1Low64) { high++; if (high <= d1High) goto AlignedScale; } else if (high < d1High) goto AlignedScale; } goto ReturnResult; } ReturnResult: d1.uflags = flags; d1.High = high; d1.Low64 = low64; return; } #endregion /// /// Convert Decimal to Currency (similar to OleAut32 api.) /// internal static long VarCyFromDec(ref DecCalc pdecIn) { long value; int scale = pdecIn.Scale - 4; // Need to scale to get 4 decimal places. -4 <= scale <= 24. // if (scale < 0) { if (pdecIn.High != 0) goto ThrowOverflow; uint pwr = s_powers10[-scale]; ulong high = UInt32x32To64(pwr, pdecIn.Mid); if (high > uint.MaxValue) goto ThrowOverflow; ulong low = UInt32x32To64(pwr, pdecIn.Low); low += high <<= 32; if (low < high) goto ThrowOverflow; value = (long)low; } else { if (scale != 0) InternalRound(ref pdecIn, (uint)scale, RoundingMode.ToEven); if (pdecIn.High != 0) goto ThrowOverflow; value = (long)pdecIn.Low64; } if (value < 0 && (value != long.MinValue || !pdecIn.IsNegative)) goto ThrowOverflow; if (pdecIn.IsNegative) value = -value; return value; ThrowOverflow: throw new OverflowException(SR.Overflow_Currency); } /// /// Decimal Compare updated to return values similar to ICompareTo /// internal static int VarDecCmp(in decimal d1, in decimal d2) { if ((d2.Low | d2.Mid | d2.High) == 0) { if ((d1.Low | d1.Mid | d1.High) == 0) return 0; return (d1.flags >> 31) | 1; } if ((d1.Low | d1.Mid | d1.High) == 0) return -((d2.flags >> 31) | 1); int sign = (d1.flags >> 31) - (d2.flags >> 31); if (sign != 0) return sign; return VarDecCmpSub(in d1, in d2); } private static int VarDecCmpSub(in decimal d1, in decimal d2) { int flags = d2.flags; int sign = (flags >> 31) | 1; int scale = flags - d1.flags; ulong low64 = d1.Low64; uint high = d1.High; ulong d2Low64 = d2.Low64; uint d2High = d2.High; if (scale != 0) { scale >>= ScaleShift; // Scale factors are not equal. Assume that a larger scale factor (more decimal places) is likely to mean that number is smaller. // Start by guessing that the right operand has the larger scale factor. if (scale < 0) { // Guessed scale factor wrong. Swap operands. scale = -scale; sign = -sign; ulong tmp64 = low64; low64 = d2Low64; d2Low64 = tmp64; uint tmp = high; high = d2High; d2High = tmp; } // d1 will need to be multiplied by 10^scale so it will have the same scale as d2. // Scaling loop, up to 10^9 at a time. do { uint power = scale >= MaxInt32Scale ? TenToPowerNine : s_powers10[scale]; ulong tmpLow = UInt32x32To64((uint)low64, power); ulong tmp = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32); low64 = (uint)tmpLow + (tmp << 32); tmp >>= 32; tmp += UInt32x32To64(high, power); // If the scaled value has more than 96 significant bits then it's greater than d2 if (tmp > uint.MaxValue) return sign; high = (uint)tmp; } while ((scale -= MaxInt32Scale) > 0); } uint cmpHigh = high - d2High; if (cmpHigh != 0) { // check for overflow if (cmpHigh > high) sign = -sign; return sign; } ulong cmpLow64 = low64 - d2Low64; if (cmpLow64 == 0) sign = 0; // check for overflow else if (cmpLow64 > low64) sign = -sign; return sign; } /// /// Decimal Multiply /// internal static unsafe void VarDecMul(ref DecCalc d1, ref DecCalc d2) { int scale = (byte)(d1.uflags + d2.uflags >> ScaleShift); ulong tmp; uint hiProd; Buf24 bufProd; _ = &bufProd; // workaround for CS0165 if ((d1.High | d1.Mid) == 0) { if ((d2.High | d2.Mid) == 0) { // Upper 64 bits are zero. // ulong low64 = UInt32x32To64(d1.Low, d2.Low); if (scale > DEC_SCALE_MAX) { // Result scale is too big. Divide result by power of 10 to reduce it. // If the amount to divide by is > 19 the result is guaranteed // less than 1/2. [max value in 64 bits = 1.84E19] // if (scale > DEC_SCALE_MAX + MaxInt64Scale) goto ReturnZero; scale -= DEC_SCALE_MAX + 1; ulong power = s_ulongPowers10[scale]; // TODO: https://github.com/dotnet/coreclr/issues/3439 tmp = low64 / power; ulong remainder = low64 - tmp * power; low64 = tmp; // Round result. See if remainder >= 1/2 of divisor. // Divisor is a power of 10, so it is always even. // power >>= 1; if (remainder >= power && (remainder > power || ((uint)low64 & 1) > 0)) low64++; scale = DEC_SCALE_MAX; } d1.Low64 = low64; d1.uflags = ((d2.uflags ^ d1.uflags) & SignMask) | ((uint)scale << ScaleShift); return; } else { // Left value is 32-bit, result fits in 4 uints tmp = UInt32x32To64(d1.Low, d2.Low); bufProd.U0 = (uint)tmp; tmp = UInt32x32To64(d1.Low, d2.Mid) + (tmp >> 32); bufProd.U1 = (uint)tmp; tmp >>= 32; if (d2.High != 0) { tmp += UInt32x32To64(d1.Low, d2.High); if (tmp > uint.MaxValue) { bufProd.Mid64 = tmp; hiProd = 3; goto SkipScan; } } if ((uint)tmp != 0) { bufProd.U2 = (uint)tmp; hiProd = 2; goto SkipScan; } hiProd = 1; } } else if ((d2.High | d2.Mid) == 0) { // Right value is 32-bit, result fits in 4 uints tmp = UInt32x32To64(d2.Low, d1.Low); bufProd.U0 = (uint)tmp; tmp = UInt32x32To64(d2.Low, d1.Mid) + (tmp >> 32); bufProd.U1 = (uint)tmp; tmp >>= 32; if (d1.High != 0) { tmp += UInt32x32To64(d2.Low, d1.High); if (tmp > uint.MaxValue) { bufProd.Mid64 = tmp; hiProd = 3; goto SkipScan; } } if ((uint)tmp != 0) { bufProd.U2 = (uint)tmp; hiProd = 2; goto SkipScan; } hiProd = 1; } else { // Both operands have bits set in the upper 64 bits. // // Compute and accumulate the 9 partial products into a // 192-bit (24-byte) result. // // [l-h][l-m][l-l] left high, middle, low // x [r-h][r-m][r-l] right high, middle, low // ------------------------------ // // [0-h][0-l] l-l * r-l // [1ah][1al] l-l * r-m // [1bh][1bl] l-m * r-l // [2ah][2al] l-m * r-m // [2bh][2bl] l-l * r-h // [2ch][2cl] l-h * r-l // [3ah][3al] l-m * r-h // [3bh][3bl] l-h * r-m // [4-h][4-l] l-h * r-h // ------------------------------ // [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array // tmp = UInt32x32To64(d1.Low, d2.Low); bufProd.U0 = (uint)tmp; ulong tmp2 = UInt32x32To64(d1.Low, d2.Mid) + (tmp >> 32); tmp = UInt32x32To64(d1.Mid, d2.Low); tmp += tmp2; // this could generate carry bufProd.U1 = (uint)tmp; if (tmp < tmp2) // detect carry tmp2 = (tmp >> 32) | (1UL << 32); else tmp2 = tmp >> 32; tmp = UInt32x32To64(d1.Mid, d2.Mid) + tmp2; if ((d1.High | d2.High) > 0) { // Highest 32 bits is non-zero. Calculate 5 more partial products. // tmp2 = UInt32x32To64(d1.Low, d2.High); tmp += tmp2; // this could generate carry uint tmp3 = 0; if (tmp < tmp2) // detect carry tmp3 = 1; tmp2 = UInt32x32To64(d1.High, d2.Low); tmp += tmp2; // this could generate carry bufProd.U2 = (uint)tmp; if (tmp < tmp2) // detect carry tmp3++; tmp2 = ((ulong)tmp3 << 32) | (tmp >> 32); tmp = UInt32x32To64(d1.Mid, d2.High); tmp += tmp2; // this could generate carry tmp3 = 0; if (tmp < tmp2) // detect carry tmp3 = 1; tmp2 = UInt32x32To64(d1.High, d2.Mid); tmp += tmp2; // this could generate carry bufProd.U3 = (uint)tmp; if (tmp < tmp2) // detect carry tmp3++; tmp = ((ulong)tmp3 << 32) | (tmp >> 32); bufProd.High64 = UInt32x32To64(d1.High, d2.High) + tmp; hiProd = 5; } else if (tmp != 0) { bufProd.Mid64 = tmp; hiProd = 3; } else hiProd = 1; } // Check for leading zero uints on the product // uint* product = (uint*)&bufProd; while (product[(int)hiProd] == 0) { if (hiProd == 0) goto ReturnZero; hiProd--; } SkipScan: if (hiProd > 2 || scale > DEC_SCALE_MAX) { scale = ScaleResult(&bufProd, hiProd, scale); } d1.Low64 = bufProd.Low64; d1.High = bufProd.U2; d1.uflags = ((d2.uflags ^ d1.uflags) & SignMask) | ((uint)scale << ScaleShift); return; ReturnZero: d1 = default; } /// /// Convert float to Decimal /// internal static void VarDecFromR4(float input, out DecCalc result) { result = default; // The most we can scale by is 10^28, which is just slightly more // than 2^93. So a float with an exponent of -94 could just // barely reach 0.5, but smaller exponents will always round to zero. // const uint SNGBIAS = 126; int exp = (int)(GetExponent(input) - SNGBIAS); if (exp < -94) return; // result should be zeroed out if (exp > 96) throw new OverflowException(SR.Overflow_Decimal); uint flags = 0; if (input < 0) { input = -input; flags = SignMask; } // Round the input to a 7-digit integer. The R4 format has // only 7 digits of precision, and we want to keep garbage digits // out of the Decimal were making. // // Calculate max power of 10 input value could have by multiplying // the exponent by log10(2). Using scaled integer multiplcation, // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3. // double dbl = input; int power = 6 - ((exp * 19728) >> 16); // power is between -22 and 35 if (power >= 0) { // We have less than 7 digits, scale input up. // if (power > DEC_SCALE_MAX) power = DEC_SCALE_MAX; dbl *= s_doublePowers10[power]; } else { if (power != -1 || dbl >= 1E7) dbl /= s_doublePowers10[-power]; else power = 0; // didn't scale it } Debug.Assert(dbl < 1E7); if (dbl < 1E6 && power < DEC_SCALE_MAX) { dbl *= 10; power++; Debug.Assert(dbl >= 1E6); } // Round to integer // uint mant; mant = (uint)(int)dbl; dbl -= (int)mant; // difference between input & integer if (dbl > 0.5 || dbl == 0.5 && (mant & 1) != 0) mant++; if (mant == 0) return; // result should be zeroed out if (power < 0) { // Add -power factors of 10, -power <= (29 - 7) = 22. // power = -power; if (power < 10) { result.Low64 = UInt32x32To64(mant, s_powers10[power]); } else { // Have a big power of 10. // if (power > 18) { ulong low64 = UInt32x32To64(mant, s_powers10[power - 18]); UInt64x64To128(low64, TenToPowerEighteen, ref result); } else { ulong low64 = UInt32x32To64(mant, s_powers10[power - 9]); ulong hi64 = UInt32x32To64(TenToPowerNine, (uint)(low64 >> 32)); low64 = UInt32x32To64(TenToPowerNine, (uint)low64); result.Low = (uint)low64; hi64 += low64 >> 32; result.Mid = (uint)hi64; hi64 >>= 32; result.High = (uint)hi64; } } } else { // Factor out powers of 10 to reduce the scale, if possible. // The maximum number we could factor out would be 6. This // comes from the fact we have a 7-digit number, and the // MSD must be non-zero -- but the lower 6 digits could be // zero. Note also the scale factor is never negative, so // we can't scale by any more than the power we used to // get the integer. // int lmax = power; if (lmax > 6) lmax = 6; if ((mant & 0xF) == 0 && lmax >= 4) { const uint den = 10000; uint div = mant / den; if (mant == div * den) { mant = div; power -= 4; lmax -= 4; } } if ((mant & 3) == 0 && lmax >= 2) { const uint den = 100; uint div = mant / den; if (mant == div * den) { mant = div; power -= 2; lmax -= 2; } } if ((mant & 1) == 0 && lmax >= 1) { const uint den = 10; uint div = mant / den; if (mant == div * den) { mant = div; power--; } } flags |= (uint)power << ScaleShift; result.Low = mant; } result.uflags = flags; } /// /// Convert double to Decimal /// internal static void VarDecFromR8(double input, out DecCalc result) { result = default; // The most we can scale by is 10^28, which is just slightly more // than 2^93. So a float with an exponent of -94 could just // barely reach 0.5, but smaller exponents will always round to zero. // const uint DBLBIAS = 1022; int exp = (int)(GetExponent(input) - DBLBIAS); if (exp < -94) return; // result should be zeroed out if (exp > 96) throw new OverflowException(SR.Overflow_Decimal); uint flags = 0; if (input < 0) { input = -input; flags = SignMask; } // Round the input to a 15-digit integer. The R8 format has // only 15 digits of precision, and we want to keep garbage digits // out of the Decimal were making. // // Calculate max power of 10 input value could have by multiplying // the exponent by log10(2). Using scaled integer multiplcation, // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3. // double dbl = input; int power = 14 - ((exp * 19728) >> 16); // power is between -14 and 43 if (power >= 0) { // We have less than 15 digits, scale input up. // if (power > DEC_SCALE_MAX) power = DEC_SCALE_MAX; dbl *= s_doublePowers10[power]; } else { if (power != -1 || dbl >= 1E15) dbl /= s_doublePowers10[-power]; else power = 0; // didn't scale it } Debug.Assert(dbl < 1E15); if (dbl < 1E14 && power < DEC_SCALE_MAX) { dbl *= 10; power++; Debug.Assert(dbl >= 1E14); } // Round to int64 // ulong mant; mant = (ulong)(long)dbl; dbl -= (long)mant; // difference between input & integer if (dbl > 0.5 || dbl == 0.5 && (mant & 1) != 0) mant++; if (mant == 0) return; // result should be zeroed out if (power < 0) { // Add -power factors of 10, -power <= (29 - 15) = 14. // power = -power; if (power < 10) { var pow10 = s_powers10[power]; ulong low64 = UInt32x32To64((uint)mant, pow10); ulong hi64 = UInt32x32To64((uint)(mant >> 32), pow10); result.Low = (uint)low64; hi64 += low64 >> 32; result.Mid = (uint)hi64; hi64 >>= 32; result.High = (uint)hi64; } else { // Have a big power of 10. // Debug.Assert(power <= 14); UInt64x64To128(mant, s_ulongPowers10[power - 1], ref result); } } else { // Factor out powers of 10 to reduce the scale, if possible. // The maximum number we could factor out would be 14. This // comes from the fact we have a 15-digit number, and the // MSD must be non-zero -- but the lower 14 digits could be // zero. Note also the scale factor is never negative, so // we can't scale by any more than the power we used to // get the integer. // int lmax = power; if (lmax > 14) lmax = 14; if ((byte)mant == 0 && lmax >= 8) { const uint den = 100000000; ulong div = mant / den; if ((uint)mant == (uint)(div * den)) { mant = div; power -= 8; lmax -= 8; } } if (((uint)mant & 0xF) == 0 && lmax >= 4) { const uint den = 10000; ulong div = mant / den; if ((uint)mant == (uint)(div * den)) { mant = div; power -= 4; lmax -= 4; } } if (((uint)mant & 3) == 0 && lmax >= 2) { const uint den = 100; ulong div = mant / den; if ((uint)mant == (uint)(div * den)) { mant = div; power -= 2; lmax -= 2; } } if (((uint)mant & 1) == 0 && lmax >= 1) { const uint den = 10; ulong div = mant / den; if ((uint)mant == (uint)(div * den)) { mant = div; power--; } } flags |= (uint)power << ScaleShift; result.Low64 = mant; } result.uflags = flags; } /// /// Convert Decimal to float /// internal static float VarR4FromDec(in decimal value) { return (float)VarR8FromDec(in value); } /// /// Convert Decimal to double /// internal static double VarR8FromDec(in decimal value) { // Value taken via reverse engineering the double that corresponds to 2^64. (oleaut32 has ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0)) const double ds2to64 = 1.8446744073709552e+019; double dbl = ((double)value.Low64 + (double)value.High * ds2to64) / s_doublePowers10[value.Scale]; if (value.IsNegative) dbl = -dbl; return dbl; } internal static int GetHashCode(in decimal d) { if ((d.Low | d.Mid | d.High) == 0) return 0; uint flags = (uint)d.flags; if ((flags & ScaleMask) == 0 || (d.Low & 1) != 0) return (int)(flags ^ d.High ^ d.Mid ^ d.Low); int scale = (byte)(flags >> ScaleShift); uint low = d.Low; ulong high64 = ((ulong)d.High << 32) | d.Mid; Unscale(ref low, ref high64, ref scale); flags = ((flags) & ~ScaleMask) | (uint)scale << ScaleShift; return (int)(flags ^ (uint)(high64 >> 32) ^ (uint)high64 ^ low); } /// /// Divides two decimal values. /// On return, d1 contains the result of the operation. /// internal static unsafe void VarDecDiv(ref DecCalc d1, ref DecCalc d2) { Buf12 bufQuo; _ = &bufQuo; // workaround for CS0165 uint power; int curScale; int scale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift); bool unscale = false; uint tmp; if ((d2.High | d2.Mid) == 0) { // Divisor is only 32 bits. Easy divide. // uint den = d2.Low; if (den == 0) throw new DivideByZeroException(); bufQuo.Low64 = d1.Low64; bufQuo.U2 = d1.High; uint remainder = Div96By32(ref bufQuo, den); for (;;) { if (remainder == 0) { if (scale < 0) { curScale = Math.Min(9, -scale); goto HaveScale; } break; } // We need to unscale if and only if we have a non-zero remainder unscale = true; // We have computed a quotient based on the natural scale // ( - ). We have a non-zero // remainder, so now we should increase the scale if possible to // include more quotient bits. // // If it doesn't cause overflow, we'll loop scaling by 10^9 and // computing more quotient bits as long as the remainder stays // non-zero. If scaling by that much would cause overflow, we'll // drop out of the loop and scale by as much as we can. // // Scaling by 10^9 will overflow if bufQuo[2].bufQuo[1] >= 2^32 / 10^9 // = 4.294 967 296. So the upper limit is bufQuo[2] == 4 and // bufQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since // quotient bits in bufQuo[0] could be all 1's, then 1,266,874,888 // is the largest value in bufQuo[1] (when bufQuo[2] == 4) that is // assured not to overflow. // if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // tmp = remainder << 1; if (tmp < remainder || tmp >= den && (tmp > den || (bufQuo.U0 & 1) != 0)) goto RoundUp; break; } HaveScale: power = s_powers10[curScale]; scale += curScale; if (IncreaseScale(ref bufQuo, power) != 0) goto ThrowOverflow; ulong num = UInt32x32To64(remainder, power); // TODO: https://github.com/dotnet/coreclr/issues/3439 uint div = (uint)(num / den); remainder = (uint)num - div * den; if (!Add32To96(ref bufQuo, div)) { scale = OverflowUnscale(ref bufQuo, scale, remainder != 0); break; } } // for (;;) } else { // Divisor has bits set in the upper 64 bits. // // Divisor must be fully normalized (shifted so bit 31 of the most // significant uint is 1). Locate the MSB so we know how much to // normalize by. The dividend will be shifted by the same amount so // the quotient is not changed. // tmp = d2.High; if (tmp == 0) tmp = d2.Mid; curScale = LeadingZeroCount(tmp); // Shift both dividend and divisor left by curScale. // Buf16 bufRem; _ = &bufRem; // workaround for CS0165 bufRem.Low64 = d1.Low64 << curScale; bufRem.High64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - curScale); ulong divisor = d2.Low64 << curScale; if (d2.High == 0) { // Have a 64-bit divisor in sdlDivisor. The remainder // (currently 96 bits spread over 4 uints) will be < divisor. // bufQuo.U1 = Div96By64(ref *(Buf12*)&bufRem.U1, divisor); bufQuo.U0 = Div96By64(ref *(Buf12*)&bufRem, divisor); for (;;) { if (bufRem.Low64 == 0) { if (scale < 0) { curScale = Math.Min(9, -scale); goto HaveScale64; } break; } // We need to unscale if and only if we have a non-zero remainder unscale = true; // Remainder is non-zero. Scale up quotient and remainder by // powers of 10 so we can compute more significant bits. // if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // ulong tmp64 = bufRem.Low64; if ((long)tmp64 < 0 || (tmp64 <<= 1) > divisor || (tmp64 == divisor && (bufQuo.U0 & 1) != 0)) goto RoundUp; break; } HaveScale64: power = s_powers10[curScale]; scale += curScale; if (IncreaseScale(ref bufQuo, power) != 0) goto ThrowOverflow; IncreaseScale64(ref *(Buf12*)&bufRem, power); tmp = Div96By64(ref *(Buf12*)&bufRem, divisor); if (!Add32To96(ref bufQuo, tmp)) { scale = OverflowUnscale(ref bufQuo, scale, bufRem.Low64 != 0); break; } } // for (;;) } else { // Have a 96-bit divisor in bufDivisor. // // Start by finishing the shift left by curScale. // Buf12 bufDivisor; _ = &bufDivisor; // workaround for CS0165 bufDivisor.Low64 = divisor; bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - curScale)); // The remainder (currently 96 bits spread over 4 uints) will be < divisor. // bufQuo.Low64 = Div128By96(ref bufRem, ref bufDivisor); for (;;) { if ((bufRem.Low64 | bufRem.U2) == 0) { if (scale < 0) { curScale = Math.Min(9, -scale); goto HaveScale96; } break; } // We need to unscale if and only if we have a non-zero remainder unscale = true; // Remainder is non-zero. Scale up quotient and remainder by // powers of 10 so we can compute more significant bits. // if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0) { // No more scaling to be done, but remainder is non-zero. // Round quotient. // if ((int)bufRem.U2 < 0) { goto RoundUp; } tmp = bufRem.U1 >> 31; bufRem.Low64 <<= 1; bufRem.U2 = (bufRem.U2 << 1) + tmp; if (bufRem.U2 > bufDivisor.U2 || bufRem.U2 == bufDivisor.U2 && (bufRem.Low64 > bufDivisor.Low64 || bufRem.Low64 == bufDivisor.Low64 && (bufQuo.U0 & 1) != 0)) goto RoundUp; break; } HaveScale96: power = s_powers10[curScale]; scale += curScale; if (IncreaseScale(ref bufQuo, power) != 0) goto ThrowOverflow; bufRem.U3 = IncreaseScale(ref *(Buf12*)&bufRem, power); tmp = Div128By96(ref bufRem, ref bufDivisor); if (!Add32To96(ref bufQuo, tmp)) { scale = OverflowUnscale(ref bufQuo, scale, (bufRem.Low64 | bufRem.High64) != 0); break; } } // for (;;) } } Unscale: if (unscale) { uint low = bufQuo.U0; ulong high64 = bufQuo.High64; Unscale(ref low, ref high64, ref scale); d1.Low = low; d1.Mid = (uint)high64; d1.High = (uint)(high64 >> 32); } else { d1.Low64 = bufQuo.Low64; d1.High = bufQuo.U2; } d1.uflags = ((d1.uflags ^ d2.uflags) & SignMask) | ((uint)scale << ScaleShift); return; RoundUp: { if (++bufQuo.Low64 == 0 && ++bufQuo.U2 == 0) { scale = OverflowUnscale(ref bufQuo, scale, true); } goto Unscale; } ThrowOverflow: throw new OverflowException(SR.Overflow_Decimal); } /// /// Computes the remainder between two decimals. /// On return, d1 contains the result of the operation and d2 is trashed. /// internal static void VarDecMod(ref DecCalc d1, ref DecCalc d2) { if ((d2.ulo | d2.umid | d2.uhi) == 0) throw new DivideByZeroException(); if ((d1.ulo | d1.umid | d1.uhi) == 0) return; // In the operation x % y the sign of y does not matter. Result will have the sign of x. d2.uflags = (d2.uflags & ~SignMask) | (d1.uflags & SignMask); int cmp = VarDecCmpSub(in Unsafe.As(ref d1), in Unsafe.As(ref d2)); if (cmp == 0) { d1.ulo = 0; d1.umid = 0; d1.uhi = 0; if (d2.uflags > d1.uflags) d1.uflags = d2.uflags; return; } if ((cmp ^ (int)(d1.uflags & SignMask)) < 0) return; // The divisor is smaller than the dividend and both are non-zero. Calculate the integer remainder using the larger scaling factor. int scale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift); if (scale > 0) { // Divisor scale can always be increased to dividend scale for remainder calculation. do { uint power = scale >= MaxInt32Scale ? TenToPowerNine : s_powers10[scale]; ulong tmp = UInt32x32To64(d2.Low, power); d2.Low = (uint)tmp; tmp >>= 32; tmp += (d2.Mid + ((ulong)d2.High << 32)) * power; d2.Mid = (uint)tmp; d2.High = (uint)(tmp >> 32); } while ((scale -= MaxInt32Scale) > 0); scale = 0; } do { if (scale < 0) { d1.uflags = d2.uflags; // Try to scale up dividend to match divisor. Buf12 bufQuo; unsafe { _ = &bufQuo; } // workaround for CS0165 bufQuo.Low64 = d1.Low64; bufQuo.U2 = d1.High; do { int iCurScale = SearchScale(ref bufQuo, DEC_SCALE_MAX + scale); if (iCurScale == 0) break; uint power = iCurScale >= MaxInt32Scale ? TenToPowerNine : s_powers10[iCurScale]; scale += iCurScale; ulong tmp = UInt32x32To64(bufQuo.U0, power); bufQuo.U0 = (uint)tmp; tmp >>= 32; bufQuo.High64 = tmp + bufQuo.High64 * power; if (power != TenToPowerNine) break; } while (scale < 0); d1.Low64 = bufQuo.Low64; d1.High = bufQuo.U2; } if (d1.High == 0) { Debug.Assert(d2.High == 0); Debug.Assert(scale == 0); d1.Low64 %= d2.Low64; return; } else if ((d2.High | d2.Mid) == 0) { uint den = d2.Low; ulong tmp = ((ulong)d1.High << 32) | d1.Mid; tmp = ((tmp % den) << 32) | d1.Low; d1.Low64 = tmp % den; d1.High = 0; } else { VarDecModFull(ref d1, ref d2, scale); return; } } while (scale < 0); } private static unsafe void VarDecModFull(ref DecCalc d1, ref DecCalc d2, int scale) { // Divisor has bits set in the upper 64 bits. // // Divisor must be fully normalized (shifted so bit 31 of the most significant uint is 1). // Locate the MSB so we know how much to normalize by. // The dividend will be shifted by the same amount so the quotient is not changed. // uint tmp = d2.High; if (tmp == 0) tmp = d2.Mid; int shift = LeadingZeroCount(tmp); Buf28 b; _ = &b; // workaround for CS0165 b.Buf24.Low64 = d1.Low64 << shift; b.Buf24.Mid64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - shift); // The dividend might need to be scaled up to 221 significant bits. // Maximum scaling is required when the divisor is 2^64 with scale 28 and is left shifted 31 bits // and the dividend is decimal.MaxValue: (2^96 - 1) * 10^28 << 31 = 221 bits. uint high = 3; while (scale < 0) { uint power = scale <= -MaxInt32Scale ? TenToPowerNine : s_powers10[-scale]; uint* buf = (uint*)&b; ulong tmp64 = UInt32x32To64(b.Buf24.U0, power); b.Buf24.U0 = (uint)tmp64; for (int i = 1; i <= high; i++) { tmp64 >>= 32; tmp64 += UInt32x32To64(buf[i], power); buf[i] = (uint)tmp64; } // The high bit of the dividend must not be set. if (tmp64 > int.MaxValue) { Debug.Assert(high + 1 < b.Length); buf[++high] = (uint)(tmp64 >> 32); } scale += MaxInt32Scale; } if (d2.High == 0) { ulong divisor = d2.Low64 << shift; switch (high) { case 6: Div96By64(ref *(Buf12*)&b.Buf24.U4, divisor); goto case 5; case 5: Div96By64(ref *(Buf12*)&b.Buf24.U3, divisor); goto case 4; case 4: Div96By64(ref *(Buf12*)&b.Buf24.U2, divisor); break; } Div96By64(ref *(Buf12*)&b.Buf24.U1, divisor); Div96By64(ref *(Buf12*)&b, divisor); d1.Low64 = b.Buf24.Low64 >> shift; d1.High = 0; } else { Buf12 bufDivisor; _ = &bufDivisor; // workaround for CS0165 bufDivisor.Low64 = d2.Low64 << shift; bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - shift)); switch (high) { case 6: Div128By96(ref *(Buf16*)&b.Buf24.U3, ref bufDivisor); goto case 5; case 5: Div128By96(ref *(Buf16*)&b.Buf24.U2, ref bufDivisor); goto case 4; case 4: Div128By96(ref *(Buf16*)&b.Buf24.U1, ref bufDivisor); break; } Div128By96(ref *(Buf16*)&b, ref bufDivisor); d1.Low64 = (b.Buf24.Low64 >> shift) + ((ulong)b.Buf24.U2 << (32 - shift) << 32); d1.High = b.Buf24.U2 >> shift; } } internal enum RoundingMode { ToEven = 0, AwayFromZero = 1, Truncate = 2, Floor = 3, Ceiling = 4, } /// /// Does an in-place round by the specified scale /// internal static void InternalRound(ref DecCalc d, uint scale, RoundingMode mode) { // the scale becomes the desired decimal count d.uflags -= scale << ScaleShift; uint remainder, sticky = 0, power; // First divide the value by constant 10^9 up to three times while (scale >= MaxInt32Scale) { scale -= MaxInt32Scale; const uint divisor = TenToPowerNine; uint n = d.uhi; if (n == 0) { ulong tmp = d.Low64; ulong div = tmp / divisor; d.Low64 = div; remainder = (uint)(tmp - div * divisor); } else { uint q; d.uhi = q = n / divisor; remainder = n - q * divisor; n = d.umid; if ((n | remainder) != 0) { d.umid = q = (uint)((((ulong)remainder << 32) | n) / divisor); remainder = n - q * divisor; } n = d.ulo; if ((n | remainder) != 0) { d.ulo = q = (uint)((((ulong)remainder << 32) | n) / divisor); remainder = n - q * divisor; } } power = divisor; if (scale == 0) goto checkRemainder; sticky |= remainder; } { power = s_powers10[scale]; // TODO: https://github.com/dotnet/coreclr/issues/3439 uint n = d.uhi; if (n == 0) { ulong tmp = d.Low64; if (tmp == 0) { if (mode <= RoundingMode.Truncate) goto done; remainder = 0; goto checkRemainder; } ulong div = tmp / power; d.Low64 = div; remainder = (uint)(tmp - div * power); } else { uint q; d.uhi = q = n / power; remainder = n - q * power; n = d.umid; if ((n | remainder) != 0) { d.umid = q = (uint)((((ulong)remainder << 32) | n) / power); remainder = n - q * power; } n = d.ulo; if ((n | remainder) != 0) { d.ulo = q = (uint)((((ulong)remainder << 32) | n) / power); remainder = n - q * power; } } } checkRemainder: if (mode == RoundingMode.Truncate) goto done; else if (mode == RoundingMode.ToEven) { // To do IEEE rounding, we add LSB of result to sticky bits so either causes round up if remainder * 2 == last divisor. remainder <<= 1; if ((sticky | d.ulo & 1) != 0) remainder++; if (power >= remainder) goto done; } else if (mode == RoundingMode.AwayFromZero) { // Round away from zero at the mid point. remainder <<= 1; if (power > remainder) goto done; } else if (mode == RoundingMode.Floor) { // Round toward -infinity if we have chopped off a non-zero amount from a negative value. if ((remainder | sticky) == 0 || !d.IsNegative) goto done; } else { Debug.Assert(mode == RoundingMode.Ceiling); // Round toward infinity if we have chopped off a non-zero amount from a positive value. if ((remainder | sticky) == 0 || d.IsNegative) goto done; } if (++d.Low64 == 0) d.uhi++; done: return; } internal static uint DecDivMod1E9(ref DecCalc value) { ulong high64 = ((ulong)value.uhi << 32) + value.umid; ulong div64 = high64 / TenToPowerNine; value.uhi = (uint)(div64 >> 32); value.umid = (uint)div64; ulong num = ((high64 - (uint)div64 * TenToPowerNine) << 32) + value.ulo; uint div = (uint)(num / TenToPowerNine); value.ulo = div; return (uint)num - div * TenToPowerNine; } struct PowerOvfl { public readonly uint Hi; public readonly ulong MidLo; public PowerOvfl(uint hi, uint mid, uint lo) { Hi = hi; MidLo = ((ulong)mid << 32) + lo; } } static readonly PowerOvfl[] PowerOvflValues = new[] { // This is a table of the largest values that can be in the upper two // uints of a 96-bit number that will not overflow when multiplied // by a given power. For the upper word, this is a table of // 2^32 / 10^n for 1 <= n <= 8. For the lower word, this is the // remaining fraction part * 2^32. 2^32 = 4294967296. // new PowerOvfl(429496729, 2576980377, 2576980377), // 10^1 remainder 0.6 new PowerOvfl(42949672, 4123168604, 687194767), // 10^2 remainder 0.16 new PowerOvfl(4294967, 1271310319, 2645699854), // 10^3 remainder 0.616 new PowerOvfl(429496, 3133608139, 694066715), // 10^4 remainder 0.1616 new PowerOvfl(42949, 2890341191, 2216890319), // 10^5 remainder 0.51616 new PowerOvfl(4294, 4154504685, 2369172679), // 10^6 remainder 0.551616 new PowerOvfl(429, 2133437386, 4102387834), // 10^7 remainder 0.9551616 new PowerOvfl(42, 4078814305, 410238783), // 10^8 remainder 0.09991616 }; [StructLayout(LayoutKind.Explicit)] private struct Buf12 { [FieldOffset(0 * 4)] public uint U0; [FieldOffset(1 * 4)] public uint U1; [FieldOffset(2 * 4)] public uint U2; [FieldOffset(0)] private ulong ulo64LE; [FieldOffset(4)] private ulong uhigh64LE; public ulong Low64 { get => BitConverter.IsLittleEndian ? ulo64LE : (((ulong)U1 << 32) | U0); set { if (BitConverter.IsLittleEndian) { ulo64LE = value; } else { U1 = (uint)(value >> 32); U0 = (uint)value; } } } /// /// U1-U2 combined (overlaps with Low64) /// public ulong High64 { get => BitConverter.IsLittleEndian ? uhigh64LE : (((ulong)U2 << 32) | U1); set { if (BitConverter.IsLittleEndian) { uhigh64LE = value; } else { U2 = (uint)(value >> 32); U1 = (uint)value; } } } } [StructLayout(LayoutKind.Explicit)] private struct Buf16 { [FieldOffset(0 * 4)] public uint U0; [FieldOffset(1 * 4)] public uint U1; [FieldOffset(2 * 4)] public uint U2; [FieldOffset(3 * 4)] public uint U3; [FieldOffset(0 * 8)] private ulong ulo64LE; [FieldOffset(1 * 8)] private ulong uhigh64LE; public ulong Low64 { get => BitConverter.IsLittleEndian ? ulo64LE : (((ulong)U1 << 32) | U0); set { if (BitConverter.IsLittleEndian) { ulo64LE = value; } else { U1 = (uint)(value >> 32); U0 = (uint)value; } } } public ulong High64 { get => BitConverter.IsLittleEndian ? uhigh64LE : (((ulong)U3 << 32) | U2); set { if (BitConverter.IsLittleEndian) { uhigh64LE = value; } else { U3 = (uint)(value >> 32); U2 = (uint)value; } } } } [StructLayout(LayoutKind.Explicit)] private struct Buf24 { [FieldOffset(0 * 4)] public uint U0; [FieldOffset(1 * 4)] public uint U1; [FieldOffset(2 * 4)] public uint U2; [FieldOffset(3 * 4)] public uint U3; [FieldOffset(4 * 4)] public uint U4; [FieldOffset(5 * 4)] public uint U5; [FieldOffset(0 * 8)] private ulong ulo64LE; [FieldOffset(1 * 8)] private ulong umid64LE; [FieldOffset(2 * 8)] private ulong uhigh64LE; public ulong Low64 { get => BitConverter.IsLittleEndian ? ulo64LE : (((ulong)U1 << 32) | U0); set { if (BitConverter.IsLittleEndian) { ulo64LE = value; } else { U1 = (uint)(value >> 32); U0 = (uint)value; } } } public ulong Mid64 { get => BitConverter.IsLittleEndian ? umid64LE : (((ulong)U3 << 32) | U2); set { if (BitConverter.IsLittleEndian) { umid64LE = value; } else { U3 = (uint)(value >> 32); U2 = (uint)value; } } } public ulong High64 { get => BitConverter.IsLittleEndian ? uhigh64LE : (((ulong)U5 << 32) | U4); set { if (BitConverter.IsLittleEndian) { uhigh64LE = value; } else { U5 = (uint)(value >> 32); U4 = (uint)value; } } } public int Length => 6; } private struct Buf28 { public Buf24 Buf24; public uint U6; public int Length => 7; } } } } #endif