From 2b50bba8131acca2ab535e144796941ad93487b7 Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Tue, 17 Jul 2018 18:41:39 +0300 Subject: Move Decimal to shared (#18948) * Move Decimal to shared * Remove DecimalCanonicalize{Internal} --- .../System.Private.CoreLib.csproj | 2 - .../shared/System.Private.CoreLib.Shared.projitems | 2 + .../shared/System/Decimal.DecCalc.cs | 2711 ++++++++++++++++++++ .../shared/System/Decimal.cs | 1119 ++++++++ .../shared/System/Number.Parsing.cs | 77 + src/System.Private.CoreLib/src/System/Currency.cs | 49 +- .../src/System/Decimal.DecCalc.cs | 96 - src/System.Private.CoreLib/src/System/Decimal.cs | 1337 ---------- .../src/System/Number.CoreCLR.cs | 3 - .../src/System/StubHelpers.cs | 3 - src/classlibnative/bcltype/CMakeLists.txt | 2 - src/classlibnative/bcltype/currency.cpp | 42 - src/classlibnative/bcltype/currency.h | 24 - src/classlibnative/bcltype/decimal.cpp | 2534 ------------------ src/classlibnative/bcltype/decimal.h | 52 - src/classlibnative/bcltype/number.cpp | 26 +- src/classlibnative/bcltype/number.h | 1 - src/inc/utilcode.h | 65 +- src/palrt/CMakeLists.txt | 2 - src/palrt/decarith.cpp | 1267 --------- src/palrt/decconv.cpp | 602 ----- src/vm/ecalllist.h | 24 - src/vm/fieldmarshaler.cpp | 7 +- src/vm/ilmarshalers.cpp | 5 - src/vm/mscorlib.cpp | 2 - src/vm/mscorlib.h | 1 - src/vm/olevariant.cpp | 18 +- src/vm/stubhelpers.cpp | 11 - src/vm/stubhelpers.h | 1 - 29 files changed, 3946 insertions(+), 6139 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs create mode 100644 src/System.Private.CoreLib/shared/System/Decimal.cs delete mode 100644 src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs delete mode 100644 src/System.Private.CoreLib/src/System/Decimal.cs delete mode 100644 src/classlibnative/bcltype/currency.cpp delete mode 100644 src/classlibnative/bcltype/currency.h delete mode 100644 src/classlibnative/bcltype/decimal.cpp delete mode 100644 src/classlibnative/bcltype/decimal.h delete mode 100644 src/palrt/decarith.cpp delete mode 100644 src/palrt/decconv.cpp (limited to 'src') diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index c807f94d43..9a2fc68170 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -267,8 +267,6 @@ - - diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 526e1360a1..743a70361f 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -116,6 +116,8 @@ + + diff --git a/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs new file mode 100644 index 0000000000..75ddbfc231 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs @@ -0,0 +1,2711 @@ +// 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. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; +using X86 = System.Runtime.Intrinsics.X86; + +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); + +#if BIGENDIAN + private ulong Low64 => ((ulong)Mid << 32) | Low; +#else + private ulong Low64 => Unsafe.As(ref Unsafe.AsRef(in lo)); +#endif + + 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)); + } + + internal static void DecAddInt32(ref decimal value, uint i) + { + DecCalc.DecAddInt32(ref AsMutable(ref value), i); + } + + internal static void DecMul10(ref decimal value) + { + DecCalc.DecMul10(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 + { +#if BIGENDIAN + get { return ((ulong)umid << 32) | ulo; } + set { umid = (uint)(value >> 32); ulo = (uint)value; } +#else + get => ulomidLE; + set => ulomidLE = value; +#endif + } + + 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 pdecOut) + { + 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); + pdecOut.Low64 = low; + pdecOut.High = (uint)high; + } + + /*** + * Div96By32 + * + * Entry: + * bufNum - 96-bit dividend as array of ULONGs, least-sig first + * ulDen - 32-bit divisor. + * + * Purpose: + * Do full divide, yielding 96-bit result and 32-bit remainder. + * + * Exit: + * Quotient overwrites dividend. + * Returns remainder. + * + * Exceptions: + * None. + * + ***********************************************************************/ + private static uint Div96By32(ref Buf12 bufNum, uint ulDen) + { + // TODO: https://github.com/dotnet/coreclr/issues/3439 + ulong tmp, div; + if (bufNum.U2 != 0) + { + tmp = bufNum.High64; + div = tmp / ulDen; + bufNum.High64 = div; + tmp = ((tmp - (uint)div * ulDen) << 32) | bufNum.U0; + if (tmp == 0) + return 0; + uint div32 = (uint)(tmp / ulDen); + bufNum.U0 = div32; + return (uint)tmp - div32 * ulDen; + } + + tmp = bufNum.Low64; + if (tmp == 0) + return 0; + div = tmp / ulDen; + bufNum.Low64 = div; + return (uint)(tmp - div * ulDen); + } + + [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. + // + // 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. + // + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Unscale(ref uint low, ref ulong high64, ref int scale) + { +#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--; + } + + /*** + * Div96By64 + * + * Entry: + * bufNum - 96-bit dividend as array of ULONGs, least-sig first + * sdlDen - 64-bit divisor. + * + * Purpose: + * Do partial divide, yielding 32-bit result and 64-bit remainder. + * Divisor must be larger than upper 64 bits of dividend. + * + * Exit: + * Remainder overwrites lower 64-bits of dividend. + * Returns quotient. + * + * Exceptions: + * None. + * + ***********************************************************************/ + private static uint Div96By64(ref Buf12 bufNum, ulong den) + { + 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; + } + + /*** + * Div128By96 + * + * Entry: + * bufNum - 128-bit dividend as array of ULONGs, least-sig first + * bufDen - 96-bit divisor. + * + * Purpose: + * Do partial divide, yielding 32-bit result and 96-bit remainder. + * Top divisor ULONG must be larger than top dividend ULONG. 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). + * + * Exit: + * Remainder overwrites lower 96-bits of dividend. + * Returns quotient. + * + * Exceptions: + * None. + * + ***********************************************************************/ + private static uint Div128By96(ref Buf16 bufNum, ref Buf12 bufDen) + { + 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; + } + + /*** + * IncreaseScale + * + * Entry: + * bufNum - 96-bit number as array of ULONGs, least-sig first + * ulPwr - Scale factor to multiply by + * + * Purpose: + * 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. + * + * Exit: + * Returns highest 32 bits of product. + * + * Exceptions: + * None. + * + ***********************************************************************/ + private static uint IncreaseScale(ref Buf12 bufNum, uint ulPwr) + { + ulong tmp = UInt32x32To64(bufNum.U0, ulPwr); + bufNum.U0 = (uint)tmp; + tmp >>= 32; + tmp += UInt32x32To64(bufNum.U1, ulPwr); + bufNum.U1 = (uint)tmp; + tmp >>= 32; + tmp += UInt32x32To64(bufNum.U2, ulPwr); + bufNum.U2 = (uint)tmp; + return (uint)(tmp >> 32); + } + + private static void IncreaseScale64(ref Buf12 bufNum, uint ulPwr) + { + ulong tmp = UInt32x32To64(bufNum.U0, ulPwr); + bufNum.U0 = (uint)tmp; + tmp >>= 32; + tmp += UInt32x32To64(bufNum.U1, ulPwr); + bufNum.High64 = tmp; + } + + /*** + * ScaleResult + * + * Entry: + * bufRes - Array of ULONGs with value, least-significant first. + * iHiRes - Index of last non-zero value in bufRes. + * iScale - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX + * + * Purpose: + * See if we need to scale the result to fit it in 96 bits. + * Perform needed scaling. Adjust scale factor accordingly. + * + * Exit: + * bufRes updated in place, always 3 ULONGs. + * New scale factor returned. + * + ***********************************************************************/ + private static unsafe int ScaleResult(Buf24* bufRes, uint iHiRes, int iScale) + { + Debug.Assert(iHiRes < bufRes->Length); + uint* rgulRes = (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. iHiRes is the index into rgulRes[] + // of the highest non-zero ULONG. + // + int iNewScale = 0; + if (iHiRes > 2) + { + iNewScale = (int)iHiRes * 32 - 64 - 1; + iNewScale -= X86.Lzcnt.IsSupported ? (int)X86.Lzcnt.LeadingZeroCount(rgulRes[iHiRes]) : LeadingZeroCount(rgulRes[iHiRes]); + + // 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...) + // + iNewScale = ((iNewScale * 77) >> 8) + 1; + + // iNewScale = 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 (iNewScale > iScale) + goto ThrowOverflow; + } + + // Make sure we scale by enough to bring the current scale factor + // into valid range. + // + if (iNewScale < iScale - DEC_SCALE_MAX) + iNewScale = iScale - DEC_SCALE_MAX; + + if (iNewScale != 0) + { + // Scale by the power of 10 given by iNewScale. Note that this is + // NOT guaranteed to bring the number within 96 bits -- it could + // be 1 power of 10 short. + // + iScale -= iNewScale; + uint ulSticky = 0; + uint quotient, remainder = 0; + + for (;;) + { + ulSticky |= remainder; // record remainder as sticky bit + + uint ulPwr; + // 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 (iNewScale) + { + case 1: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 10); + break; + case 2: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 100); + break; + case 3: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 1000); + break; + case 4: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 10000); + break; +#if BIT64 + case 5: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 100000); + break; + case 6: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 1000000); + break; + case 7: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 10000000); + break; + case 8: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, 100000000); + break; + default: + ulPwr = DivByConst(rgulRes, iHiRes, out quotient, out remainder, TenToPowerNine); + break; +#else + default: + goto case 4; +#endif + } + rgulRes[iHiRes] = quotient; + // If first quotient was 0, update iHiRes. + // + if (quotient == 0 && iHiRes != 0) + iHiRes--; + +#if BIT64 + iNewScale -= MaxInt32Scale; +#else + iNewScale -= 4; +#endif + if (iNewScale > 0) + continue; // scale some more + + // If we scaled enough, iHiRes would be 2 or less. If not, + // divide by 10 more. + // + if (iHiRes > 2) + { + if (iScale == 0) + goto ThrowOverflow; + iNewScale = 1; + iScale--; + 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. + // + ulPwr >>= 1; // power of 10 always even + if (ulPwr <= remainder && (ulPwr < remainder || ((rgulRes[0] & 1) | ulSticky) != 0) && ++rgulRes[0] == 0) + { + uint iCur = 0; + do + { + Debug.Assert(iCur + 1 < bufRes->Length); + } + while (++rgulRes[++iCur] == 0); + + if (iCur > 2) + { + // The rounding caused us to carry beyond 96 bits. + // Scale by 10 more. + // + if (iScale == 0) + goto ThrowOverflow; + iHiRes = iCur; + ulSticky = 0; // no sticky bit + remainder = 0; // or remainder + iNewScale = 1; + iScale--; + continue; // scale by 10 + } + } + + break; + } // for(;;) + } + return iScale; + +ThrowOverflow: + throw new OverflowException(SR.Overflow_Decimal); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint DivByConst(uint* rgulRes, uint iHiRes, out uint quotient, out uint remainder, uint power) + { + uint high = rgulRes[iHiRes]; + remainder = high - (quotient = high / power) * power; + for (uint i = iHiRes - 1; (int)i >= 0; i--) + { +#if BIT64 + ulong num = rgulRes[i] + ((ulong)remainder << 32); + remainder = (uint)num - (rgulRes[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); +#if BIGENDIAN + const int low16 = 2, high16 = 0; +#else + const int low16 = 0, high16 = 2; +#endif + // byte* is used here because Roslyn doesn't do constant propagation for pointer arithmetic + uint num = *(ushort*)((byte*)rgulRes + i * 4 + high16) + (remainder << 16); + uint div = num / power; + remainder = num - div * power; + *(ushort*)((byte*)rgulRes + i * 4 + high16) = (ushort)div; + + num = *(ushort*)((byte*)rgulRes + i * 4 + low16) + (remainder << 16); + div = num / power; + remainder = num - div * power; + *(ushort*)((byte*)rgulRes + 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 iScale, bool fRemainder) + { + if (--iScale < 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 && (fRemainder || (bufQuo.U0 & 1) != 0)) + Add32To96(ref bufQuo, 1); + return iScale; + } + + /*** + * SearchScale + * + * Entry: + * bufQuo - 96-bit quotient + * iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX-1 + * + * Purpose: + * Determine the max power of 10, <= 9, that the quotient can be scaled + * up by and still fit in 96 bits. + * + * Exit: + * Returns power of 10 to scale by. + * + ***********************************************************************/ + private static int SearchScale(ref Buf12 bufQuo, int iScale) + { + 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 ulResHi = bufQuo.U2; + ulong ulResMidLo = bufQuo.Low64; + int iCurScale = 0; + + // Quick check to stop us from trying to scale any more. + // + if (ulResHi > OVFL_MAX_1_HI) + { + goto HaveScale; + } + + var powerOvfl = PowerOvflValues; + if (iScale > 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. + // + iCurScale = DEC_SCALE_MAX - iScale; + if (ulResHi < powerOvfl[iCurScale - 1].Hi) + goto HaveScale; + } + else if (ulResHi < OVFL_MAX_9_HI || ulResHi == OVFL_MAX_9_HI && ulResMidLo <= OVFL_MAX_9_MIDLO) + return 9; + + // Search for a power to scale by < 9. Do a binary search. + // + if (ulResHi > OVFL_MAX_5_HI) + { + if (ulResHi > OVFL_MAX_3_HI) + { + iCurScale = 2; + if (ulResHi > OVFL_MAX_2_HI) + iCurScale--; + } + else + { + iCurScale = 4; + if (ulResHi > OVFL_MAX_4_HI) + iCurScale--; + } + } + else + { + if (ulResHi > OVFL_MAX_7_HI) + { + iCurScale = 6; + if (ulResHi > OVFL_MAX_6_HI) + iCurScale--; + } + else + { + iCurScale = 8; + if (ulResHi > OVFL_MAX_8_HI) + iCurScale--; + } + } + + // 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 (ulResHi == powerOvfl[iCurScale - 1].Hi && ulResMidLo > powerOvfl[iCurScale - 1].MidLo) + iCurScale--; + + HaveScale: + // iCurScale = largest power of 10 we can scale by without overflow, + // iCurScale < 9. See if this is enough to make scale factor + // positive if it isn't already. + // + if (iCurScale + iScale < 0) + throw new OverflowException(SR.Overflow_Decimal); + + return iCurScale; + } + + // Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer + // Returns false if there is an overflow + private static bool Add32To96(ref Buf12 bufNum, uint ulValue) + { + if ((bufNum.Low64 += ulValue) < ulValue) + { + if (++bufNum.U2 == 0) + return false; + } + return true; + } + + // DecAddSub adds or subtracts two decimal values. + // On return, d1 contains the result of the operation and d2 is trashed. + // Passing in true for bSign means subtract and false means add. + internal static unsafe void DecAddSub(ref DecCalc d1, ref DecCalc d2, bool bSign) + { + ulong low64 = d1.Low64; + uint high = d1.High, flags = d1.uflags, d2flags = d2.uflags; + + uint xorflags = d2flags ^ flags; + bSign ^= (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 iScale = (int)(flags - d1flags) >> ScaleShift; + + if (iScale < 0) + { + // Guessed scale factor wrong. Swap operands. + // + iScale = -iScale; + flags = d1flags; + if (bSign) + flags ^= SignMask; + low64 = d2.Low64; + high = d2.High; + d2 = d1; + } + + uint ulPwr; + ulong tmp64, tmpLow; + + // d1 will need to be multiplied by 10^iScale 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 (bSign) + signFlags ^= SignMask; + d1 = d2; + d1.uflags = d2.uflags & ScaleMask | signFlags; + return; + } + + do + { + if (iScale <= MaxInt32Scale) + { + low64 = UInt32x32To64((uint)low64, s_powers10[iScale]); + goto AlignedAdd; + } + iScale -= MaxInt32Scale; + low64 = UInt32x32To64((uint)low64, TenToPowerNine); + } while (low64 <= uint.MaxValue); + } + + do + { + ulPwr = TenToPowerNine; + if (iScale < MaxInt32Scale) + ulPwr = s_powers10[iScale]; + tmpLow = UInt32x32To64((uint)low64, ulPwr); + tmp64 = UInt32x32To64((uint)(low64 >> 32), ulPwr) + (tmpLow >> 32); + low64 = (uint)tmpLow + (tmp64 << 32); + high = (uint)(tmp64 >> 32); + if ((iScale -= MaxInt32Scale) <= 0) + goto AlignedAdd; + } while (high == 0); + } + + while (true) + { + // Scaling won't make it larger than 4 ULONGs + // + ulPwr = TenToPowerNine; + if (iScale < MaxInt32Scale) + ulPwr = s_powers10[iScale]; + tmpLow = UInt32x32To64((uint)low64, ulPwr); + tmp64 = UInt32x32To64((uint)(low64 >> 32), ulPwr) + (tmpLow >> 32); + low64 = (uint)tmpLow + (tmp64 << 32); + tmp64 >>= 32; + tmp64 += UInt32x32To64(high, ulPwr); + + iScale -= MaxInt32Scale; + if (tmp64 > uint.MaxValue) + break; + + high = (uint)tmp64; + // Result fits in 96 bits. Use standard aligned add. + if (iScale <= 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 iHiProd = 3; + + // Scaling loop, up to 10^9 at a time. iHiProd stays updated with index of highest non-zero ULONG. + // + for (; iScale > 0; iScale -= MaxInt32Scale) + { + ulPwr = TenToPowerNine; + if (iScale < MaxInt32Scale) + ulPwr = s_powers10[iScale]; + tmp64 = 0; + uint* rgulNum = (uint*)&bufNum; + for (uint iCur = 0; ;) + { + Debug.Assert(iCur < bufNum.Length); + tmp64 += UInt32x32To64(rgulNum[iCur], ulPwr); + rgulNum[iCur] = (uint)tmp64; + iCur++; + tmp64 >>= 32; + if (iCur > iHiProd) + break; + } + + if ((uint)tmp64 != 0) + { + // We're extending the result by another ULONG. + Debug.Assert(iHiProd + 1 < bufNum.Length); + rgulNum[++iHiProd] = (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 (bSign) + { + // 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* rgulNum = (uint*)&bufNum; + uint iCur = 3; + do + { + Debug.Assert(iCur < bufNum.Length); + } while (rgulNum[iCur++]-- == 0); + Debug.Assert(iHiProd < bufNum.Length); + if (rgulNum[iHiProd] == 0 && --iHiProd <= 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* rgulNum = (uint*)&bufNum; + for (uint iCur = 3; ++rgulNum[iCur++] == 0;) + { + Debug.Assert(iCur < bufNum.Length); + if (iHiProd < iCur) + { + rgulNum[iCur] = 1; + iHiProd = iCur; + break; + } + } + } +NoCarry: + + bufNum.Low64 = low64; + bufNum.U2 = high; + int scale = ScaleResult(&bufNum, iHiProd, (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 (bSign) + { + // 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 + + //********************************************************************** + // VarCyFromDec - Convert Currency to Decimal (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); + } + + //********************************************************************** + // VarDecCmp - Decimal Compare updated to return values similar to ICompareTo + //********************************************************************** + internal static int VarDecCmp(in decimal pdecL, in decimal pdecR) + { + if ((pdecR.Low | pdecR.Mid | pdecR.High) == 0) + { + if ((pdecL.Low | pdecL.Mid | pdecL.High) == 0) + return 0; + return (pdecL.flags >> 31) | 1; + } + if ((pdecL.Low | pdecL.Mid | pdecL.High) == 0) + return -((pdecR.flags >> 31) | 1); + + int sign = (pdecL.flags >> 31) - (pdecR.flags >> 31); + if (sign != 0) + return sign; + return VarDecCmpSub(in pdecL, in pdecR); + } + + private static int VarDecCmpSub(in decimal d1, in decimal d2) + { + int flags = d2.flags; + int sign = (flags >> 31) | 1; + int iScale = flags - d1.flags; + + ulong low64 = d1.Low64; + uint high = d1.High; + + ulong d2Low64 = d2.Low64; + uint d2High = d2.High; + + if (iScale != 0) + { + iScale >>= 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 (iScale < 0) + { + // Guessed scale factor wrong. Swap operands. + iScale = -iScale; + sign = -sign; + + ulong tmp64 = low64; + low64 = d2Low64; + d2Low64 = tmp64; + + uint tmp = high; + high = d2High; + d2High = tmp; + } + + // d1 will need to be multiplied by 10^iScale so it will have the same scale as d2. + // Scaling loop, up to 10^9 at a time. + do + { + uint ulPwr = iScale >= MaxInt32Scale ? TenToPowerNine : s_powers10[iScale]; + ulong tmpLow = UInt32x32To64((uint)low64, ulPwr); + ulong tmp = UInt32x32To64((uint)(low64 >> 32), ulPwr) + (tmpLow >> 32); + low64 = (uint)tmpLow + (tmp << 32); + tmp >>= 32; + tmp += UInt32x32To64(high, ulPwr); + // 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 ((iScale -= 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; + } + + //********************************************************************** + // VarDecMul - Decimal Multiply + //********************************************************************** + internal static unsafe void VarDecMul(ref DecCalc pdecL, ref DecCalc pdecR) + { + int iScale = (byte)(pdecL.uflags + pdecR.uflags >> ScaleShift); + + ulong tmp; + uint iHiProd; + Buf24 bufProd; + _ = &bufProd; // workaround for CS0165 + + if ((pdecL.High | pdecL.Mid) == 0) + { + if ((pdecR.High | pdecR.Mid) == 0) + { + // Upper 64 bits are zero. + // + ulong low64 = UInt32x32To64(pdecL.Low, pdecR.Low); + if (iScale > DEC_SCALE_MAX) + { + // Result iScale 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 (iScale > DEC_SCALE_MAX + MaxInt64Scale) + goto ReturnZero; + + iScale -= DEC_SCALE_MAX + 1; + ulong ulPwr = s_ulongPowers10[iScale]; + + // TODO: https://github.com/dotnet/coreclr/issues/3439 + tmp = low64 / ulPwr; + ulong remainder = low64 - tmp * ulPwr; + low64 = tmp; + + // Round result. See if remainder >= 1/2 of divisor. + // Divisor is a power of 10, so it is always even. + // + ulPwr >>= 1; + if (remainder >= ulPwr && (remainder > ulPwr || ((uint)low64 & 1) > 0)) + low64++; + + iScale = DEC_SCALE_MAX; + } + pdecL.Low64 = low64; + pdecL.uflags = ((pdecR.uflags ^ pdecL.uflags) & SignMask) | ((uint)iScale << ScaleShift); + return; + } + else + { + // Left value is 32-bit, result fits in 4 uints + tmp = UInt32x32To64(pdecL.Low, pdecR.Low); + bufProd.U0 = (uint)tmp; + + tmp = UInt32x32To64(pdecL.Low, pdecR.Mid) + (tmp >> 32); + bufProd.U1 = (uint)tmp; + tmp >>= 32; + + if (pdecR.High != 0) + { + tmp += UInt32x32To64(pdecL.Low, pdecR.High); + if (tmp > uint.MaxValue) + { + bufProd.Mid64 = tmp; + iHiProd = 3; + goto SkipScan; + } + } + if ((uint)tmp != 0) + { + bufProd.U2 = (uint)tmp; + iHiProd = 2; + goto SkipScan; + } + iHiProd = 1; + } + } + else if ((pdecR.High | pdecR.Mid) == 0) + { + // Right value is 32-bit, result fits in 4 uints + tmp = UInt32x32To64(pdecR.Low, pdecL.Low); + bufProd.U0 = (uint)tmp; + + tmp = UInt32x32To64(pdecR.Low, pdecL.Mid) + (tmp >> 32); + bufProd.U1 = (uint)tmp; + tmp >>= 32; + + if (pdecL.High != 0) + { + tmp += UInt32x32To64(pdecR.Low, pdecL.High); + if (tmp > uint.MaxValue) + { + bufProd.Mid64 = tmp; + iHiProd = 3; + goto SkipScan; + } + } + if ((uint)tmp != 0) + { + bufProd.U2 = (uint)tmp; + iHiProd = 2; + goto SkipScan; + } + iHiProd = 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(pdecL.Low, pdecR.Low); + bufProd.U0 = (uint)tmp; + + ulong tmp2 = UInt32x32To64(pdecL.Low, pdecR.Mid) + (tmp >> 32); + + tmp = UInt32x32To64(pdecL.Mid, pdecR.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(pdecL.Mid, pdecR.Mid) + tmp2; + + if ((pdecL.High | pdecR.High) > 0) + { + // Highest 32 bits is non-zero. Calculate 5 more partial products. + // + tmp2 = UInt32x32To64(pdecL.Low, pdecR.High); + tmp += tmp2; // this could generate carry + uint tmp3 = 0; + if (tmp < tmp2) // detect carry + tmp3 = 1; + + tmp2 = UInt32x32To64(pdecL.High, pdecR.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(pdecL.Mid, pdecR.High); + tmp += tmp2; // this could generate carry + tmp3 = 0; + if (tmp < tmp2) // detect carry + tmp3 = 1; + + tmp2 = UInt32x32To64(pdecL.High, pdecR.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(pdecL.High, pdecR.High) + tmp; + + iHiProd = 5; + } + else if (tmp != 0) + { + bufProd.Mid64 = tmp; + iHiProd = 3; + } + else + iHiProd = 1; + } + + // Check for leading zero ULONGs on the product + // + uint* rgulProd = (uint*)&bufProd; + while (rgulProd[(int)iHiProd] == 0) + { + if (iHiProd == 0) + goto ReturnZero; + iHiProd--; + } + +SkipScan: + if (iHiProd > 2 || iScale > DEC_SCALE_MAX) + { + iScale = ScaleResult(&bufProd, iHiProd, iScale); + } + + pdecL.Low64 = bufProd.Low64; + pdecL.High = bufProd.U2; + pdecL.uflags = ((pdecR.uflags ^ pdecL.uflags) & SignMask) | ((uint)iScale << ScaleShift); + return; + +ReturnZero: + pdecL = default; + } + + //********************************************************************** + // VarDecFromR4 - Convert float to Decimal + //********************************************************************** + internal static void VarDecFromR4(float input, out DecCalc pdecOut) + { + pdecOut = 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 iExp = (int)(GetExponent(input) - SNGBIAS); + if (iExp < -94) + return; // result should be zeroed out + + if (iExp > 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 iPower = 6 - ((iExp * 19728) >> 16); + // iPower is between -22 and 35 + + if (iPower >= 0) + { + // We have less than 7 digits, scale input up. + // + if (iPower > DEC_SCALE_MAX) + iPower = DEC_SCALE_MAX; + + dbl *= s_doublePowers10[iPower]; + } + else + { + if (iPower != -1 || dbl >= 1E7) + dbl /= s_doublePowers10[-iPower]; + else + iPower = 0; // didn't scale it + } + + Debug.Assert(dbl < 1E7); + if (dbl < 1E6 && iPower < DEC_SCALE_MAX) + { + dbl *= 10; + iPower++; + Debug.Assert(dbl >= 1E6); + } + + // Round to integer + // + uint ulMant; + // with SSE4.1 support ROUNDSD can be used + if (X86.Sse41.IsSupported) + ulMant = (uint)(int)Math.Round(dbl); + else + { + ulMant = (uint)(int)dbl; + dbl -= (int)ulMant; // difference between input & integer + if (dbl > 0.5 || dbl == 0.5 && (ulMant & 1) != 0) + ulMant++; + } + + if (ulMant == 0) + return; // result should be zeroed out + + if (iPower < 0) + { + // Add -iPower factors of 10, -iPower <= (29 - 7) = 22. + // + iPower = -iPower; + if (iPower < 10) + { + pdecOut.Low64 = UInt32x32To64(ulMant, s_powers10[iPower]); + } + else + { + // Have a big power of 10. + // + if (iPower > 18) + { + ulong low64 = UInt32x32To64(ulMant, s_powers10[iPower - 18]); + UInt64x64To128(low64, TenToPowerEighteen, ref pdecOut); + } + else + { + ulong low64 = UInt32x32To64(ulMant, s_powers10[iPower - 9]); + ulong hi64 = UInt32x32To64(TenToPowerNine, (uint)(low64 >> 32)); + low64 = UInt32x32To64(TenToPowerNine, (uint)low64); + pdecOut.Low = (uint)low64; + hi64 += low64 >> 32; + pdecOut.Mid = (uint)hi64; + hi64 >>= 32; + pdecOut.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 = iPower; + if (lmax > 6) + lmax = 6; + + if ((ulMant & 0xF) == 0 && lmax >= 4) + { + const uint den = 10000; + uint div = ulMant / den; + if (ulMant == div * den) + { + ulMant = div; + iPower -= 4; + lmax -= 4; + } + } + + if ((ulMant & 3) == 0 && lmax >= 2) + { + const uint den = 100; + uint div = ulMant / den; + if (ulMant == div * den) + { + ulMant = div; + iPower -= 2; + lmax -= 2; + } + } + + if ((ulMant & 1) == 0 && lmax >= 1) + { + const uint den = 10; + uint div = ulMant / den; + if (ulMant == div * den) + { + ulMant = div; + iPower--; + } + } + + flags |= (uint)iPower << ScaleShift; + pdecOut.Low = ulMant; + } + + pdecOut.uflags = flags; + } + + //********************************************************************** + // VarDecFromR8 - Convert double to Decimal + //********************************************************************** + internal static void VarDecFromR8(double input, out DecCalc pdecOut) + { + pdecOut = 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 iExp = (int)(GetExponent(input) - DBLBIAS); + if (iExp < -94) + return; // result should be zeroed out + + if (iExp > 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 iPower = 14 - ((iExp * 19728) >> 16); + // iPower is between -14 and 43 + + if (iPower >= 0) + { + // We have less than 15 digits, scale input up. + // + if (iPower > DEC_SCALE_MAX) + iPower = DEC_SCALE_MAX; + + dbl *= s_doublePowers10[iPower]; + } + else + { + if (iPower != -1 || dbl >= 1E15) + dbl /= s_doublePowers10[-iPower]; + else + iPower = 0; // didn't scale it + } + + Debug.Assert(dbl < 1E15); + if (dbl < 1E14 && iPower < DEC_SCALE_MAX) + { + dbl *= 10; + iPower++; + Debug.Assert(dbl >= 1E14); + } + + // Round to int64 + // + ulong ulMant; + // with SSE4.1 support ROUNDSD can be used + if (X86.Sse41.IsSupported) + ulMant = (ulong)(long)Math.Round(dbl); + else + { + ulMant = (ulong)(long)dbl; + dbl -= (long)ulMant; // difference between input & integer + if (dbl > 0.5 || dbl == 0.5 && (ulMant & 1) != 0) + ulMant++; + } + + if (ulMant == 0) + return; // result should be zeroed out + + if (iPower < 0) + { + // Add -iPower factors of 10, -iPower <= (29 - 15) = 14. + // + iPower = -iPower; + if (iPower < 10) + { + var pow10 = s_powers10[iPower]; + ulong low64 = UInt32x32To64((uint)ulMant, pow10); + ulong hi64 = UInt32x32To64((uint)(ulMant >> 32), pow10); + pdecOut.Low = (uint)low64; + hi64 += low64 >> 32; + pdecOut.Mid = (uint)hi64; + hi64 >>= 32; + pdecOut.High = (uint)hi64; + } + else + { + // Have a big power of 10. + // + Debug.Assert(iPower <= 14); + UInt64x64To128(ulMant, s_ulongPowers10[iPower - 1], ref pdecOut); + } + } + 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 = iPower; + if (lmax > 14) + lmax = 14; + + if ((byte)ulMant == 0 && lmax >= 8) + { + const uint den = 100000000; + ulong div = ulMant / den; + if ((uint)ulMant == (uint)(div * den)) + { + ulMant = div; + iPower -= 8; + lmax -= 8; + } + } + + if (((uint)ulMant & 0xF) == 0 && lmax >= 4) + { + const uint den = 10000; + ulong div = ulMant / den; + if ((uint)ulMant == (uint)(div * den)) + { + ulMant = div; + iPower -= 4; + lmax -= 4; + } + } + + if (((uint)ulMant & 3) == 0 && lmax >= 2) + { + const uint den = 100; + ulong div = ulMant / den; + if ((uint)ulMant == (uint)(div * den)) + { + ulMant = div; + iPower -= 2; + lmax -= 2; + } + } + + if (((uint)ulMant & 1) == 0 && lmax >= 1) + { + const uint den = 10; + ulong div = ulMant / den; + if ((uint)ulMant == (uint)(div * den)) + { + ulMant = div; + iPower--; + } + } + + flags |= (uint)iPower << ScaleShift; + pdecOut.Low64 = ulMant; + } + + pdecOut.uflags = flags; + } + + //********************************************************************** + // VarR4ToDec - Convert Decimal to float + //********************************************************************** + internal static float VarR4FromDec(ref decimal pdecIn) + { + return (float)VarR8FromDec(ref pdecIn); + } + + //********************************************************************** + // VarR8ToDec - Convert Decimal to double + //********************************************************************** + internal static double VarR8FromDec(ref decimal pdecIn) + { + // 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)pdecIn.Low64 + + (double)pdecIn.High * ds2to64) / s_doublePowers10[pdecIn.Scale]; + + if (pdecIn.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); + } + + // VarDecDiv 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, bufDivisor; + _ = &bufQuo; // workaround for CS0165 + _ = &bufDivisor; // workaround for CS0165 + uint ulPwr; + int iCurScale; + + int iScale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift); + bool fUnscale = false; + uint ulTmp; + + if (d2.High == 0 && 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 (iScale < 0) + { + iCurScale = Math.Min(9, -iScale); + goto HaveScale; + } + break; + } + + // We need to unscale if and only if we have a non-zero remainder + fUnscale = 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 rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9 + // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and + // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since + // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888 + // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is + // assured not to overflow. + // + if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + ulTmp = remainder << 1; + if (ulTmp < remainder || ulTmp >= den && (ulTmp > den || (bufQuo.U0 & 1) != 0)) + goto RoundUp; + break; + } + + HaveScale: + ulPwr = s_powers10[iCurScale]; + iScale += iCurScale; + + if (IncreaseScale(ref bufQuo, ulPwr) != 0) + goto ThrowOverflow; + + ulong num = UInt32x32To64(remainder, ulPwr); + // TODO: https://github.com/dotnet/coreclr/issues/3439 + uint div = (uint)(num / den); + remainder = (uint)num - div * den; + + if (!Add32To96(ref bufQuo, div)) + { + iScale = OverflowUnscale(ref bufQuo, iScale, 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 ULONG 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. + // + bufDivisor.Low64 = d2.Low64; + ulTmp = d2.High; + bufDivisor.U2 = ulTmp; + if (ulTmp == 0) + ulTmp = d2.Mid; + + iCurScale = X86.Lzcnt.IsSupported ? (int)X86.Lzcnt.LeadingZeroCount(ulTmp) : LeadingZeroCount(ulTmp); + + // Shift both dividend and divisor left by iCurScale. + // + Buf16 bufRem; + _ = &bufRem; // workaround for CS0165 + bufRem.Low64 = d1.Low64 << iCurScale; + bufRem.High64 = (d1.Mid + ((ulong)d1.High << 32)) >> (31 - iCurScale) >> 1; + + ulong divisor = bufDivisor.Low64 << iCurScale; + + if (bufDivisor.U2 == 0) + { + // Have a 64-bit divisor in sdlDivisor. The remainder + // (currently 96 bits spread over 4 ULONGs) will be < divisor. + // + + bufQuo.U1 = Div96By64(ref *(Buf12*)&bufRem.U1, divisor); + bufQuo.U0 = Div96By64(ref *(Buf12*)&bufRem, divisor); + + for (;;) + { + if (bufRem.Low64 == 0) + { + if (iScale < 0) + { + iCurScale = Math.Min(9, -iScale); + goto HaveScale64; + } + break; + } + + // We need to unscale if and only if we have a non-zero remainder + fUnscale = true; + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + ulong tmp = bufRem.Low64; + if ((long)tmp < 0 || (tmp <<= 1) > divisor || + (tmp == divisor && (bufQuo.U0 & 1) != 0)) + goto RoundUp; + break; + } + + HaveScale64: + ulPwr = s_powers10[iCurScale]; + iScale += iCurScale; + + if (IncreaseScale(ref bufQuo, ulPwr) != 0) + goto ThrowOverflow; + + IncreaseScale64(ref *(Buf12*)&bufRem, ulPwr); + ulTmp = Div96By64(ref *(Buf12*)&bufRem, divisor); + if (!Add32To96(ref bufQuo, ulTmp)) + { + iScale = OverflowUnscale(ref bufQuo, iScale, bufRem.Low64 != 0); + break; + } + } // for (;;) + } + else + { + // Have a 96-bit divisor in rgulDivisor[]. + // + // Start by finishing the shift left by iCurScale. + // + uint tmp = (uint)(bufDivisor.High64 >> (31 - iCurScale) >> 1); + bufDivisor.Low64 = divisor; + bufDivisor.U2 = tmp; + + // The remainder (currently 96 bits spread over 4 ULONGs) + // will be < divisor. + // + bufQuo.Low64 = Div128By96(ref bufRem, ref bufDivisor); + + for (;;) + { + if ((bufRem.Low64 | bufRem.U2) == 0) + { + if (iScale < 0) + { + iCurScale = Math.Min(9, -iScale); + goto HaveScale96; + } + break; + } + + // We need to unscale if and only if we have a non-zero remainder + fUnscale = true; + + // Remainder is non-zero. Scale up quotient and remainder by + // powers of 10 so we can compute more significant bits. + // + if (iScale == DEC_SCALE_MAX || (iCurScale = SearchScale(ref bufQuo, iScale)) == 0) + { + // No more scaling to be done, but remainder is non-zero. + // Round quotient. + // + if ((int)bufRem.U2 < 0) + { + goto RoundUp; + } + + ulTmp = bufRem.U1 >> 31; + bufRem.Low64 <<= 1; + bufRem.U2 = (bufRem.U2 << 1) + ulTmp; + + 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: + ulPwr = s_powers10[iCurScale]; + iScale += iCurScale; + + if (IncreaseScale(ref bufQuo, ulPwr) != 0) + goto ThrowOverflow; + + bufRem.U3 = IncreaseScale(ref *(Buf12*)&bufRem, ulPwr); + ulTmp = Div128By96(ref bufRem, ref bufDivisor); + if (!Add32To96(ref bufQuo, ulTmp)) + { + iScale = OverflowUnscale(ref bufQuo, iScale, (bufRem.Low64 | bufRem.High64) != 0); + break; + } + } // for (;;) + } + } + +Unscale: + if (fUnscale) + { + uint low = bufQuo.U0; + ulong high64 = bufQuo.High64; + Unscale(ref low, ref high64, ref iScale); + 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)iScale << ScaleShift); + return; + +RoundUp: + { + if (++bufQuo.Low64 == 0 && ++bufQuo.U2 == 0) + { + iScale = OverflowUnscale(ref bufQuo, iScale, true); + } + goto Unscale; + } + +ThrowOverflow: + throw new OverflowException(SR.Overflow_Decimal); + } + + //********************************************************************** + // VarDecMod - Computes the remainder between two decimals + //********************************************************************** + 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; + + // This piece of code is to work around the fact that Dividing a decimal with 28 digits number by decimal which causes + // causes the result to be 28 digits, can cause to be incorrectly rounded up. + // eg. Decimal.MaxValue / 2 * Decimal.MaxValue will overflow since the division by 2 was rounded instead of being truncked. + DecCalc tmp = d2; + DecAddSub(ref d1, ref tmp, true); + + // Formula: d1 - (RoundTowardsZero(d1 / d2) * d2) + tmp = d1; + VarDecDiv(ref tmp, ref d2); + Truncate(ref Unsafe.As(ref tmp)); + VarDecMul(ref tmp, ref d2); + uint flags = d1.uflags; + DecAddSub(ref d1, ref tmp, true); + // See if the result has crossed 0 + if (((flags ^ d1.uflags) & SignMask) != 0) + { + if ((d1.Low | d1.Mid | d1.High) == 0 || d1.Scale == DEC_SCALE_MAX && d1.Low64 == 1 && d1.High == 0) + { + // Certain Remainder operations on decimals with 28 significant digits round + // to [+-]0.0000000000000000000000000001m instead of [+-]0m during the intermediate calculations. + // 'zero' results just need their sign corrected. + d1.uflags ^= SignMask; + } + else + { + // If the division rounds up because it runs out of digits, the multiplied result can end up with a larger + // absolute value and the result of the formula crosses 0. To correct it can add the divisor back. + DecAddSub(ref d1, ref d2, false); + } + } + } + + 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; + } + +#region Number Formatting helpers + + 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; + } + + internal static void DecAddInt32(ref DecCalc value, uint i) + { + if (D32AddCarry(ref value.ulo, i)) + { + if (D32AddCarry(ref value.umid, 1)) + D32AddCarry(ref value.uhi, 1); + } + } + + private static bool D32AddCarry(ref uint value, uint i) + { + uint v = value; + uint sum = v + i; + value = sum; + return (sum < v) || (sum < i); + } + + internal static void DecMul10(ref DecCalc value) + { + DecCalc d = value; + DecShiftLeft(ref value); + DecShiftLeft(ref value); + DecAdd(ref value, d); + DecShiftLeft(ref value); + } + + private static void DecShiftLeft(ref DecCalc 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; + } + + private static void DecAdd(ref DecCalc value, DecCalc d) + { + if (D32AddCarry(ref value.ulo, d.Low)) + { + if (D32AddCarry(ref value.umid, 1)) + D32AddCarry(ref value.uhi, 1); + } + + if (D32AddCarry(ref value.umid, d.Mid)) + D32AddCarry(ref value.uhi, 1); + + D32AddCarry(ref value.uhi, d.High); + } + +#endregion + + 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 + // ULONGs 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 + { +#if BIGENDIAN + get => ((ulong)U1 << 32) | U0; + set { U1 = (uint)(value >> 32); U0 = (uint)value; } +#else + get => ulo64LE; + set => ulo64LE = value; +#endif + } + + /// + /// U1-U2 combined (overlaps with Low64) + /// + public ulong High64 + { +#if BIGENDIAN + get => ((ulong)U2 << 32) | U1; + set { U2 = (uint)(value >> 32); U1 = (uint)value; } +#else + get => uhigh64LE; + set => uhigh64LE = value; +#endif + } + } + + [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 + { +#if BIGENDIAN + get => ((ulong)U1 << 32) | U0; + set { U1 = (uint)(value >> 32); U0 = (uint)value; } +#else + get => ulo64LE; + set => ulo64LE = value; +#endif + } + + public ulong High64 + { +#if BIGENDIAN + get => ((ulong)U3 << 32) | U2; + set { U3 = (uint)(value >> 32); U2 = (uint)value; } +#else + get => uhigh64LE; + set => uhigh64LE = value; +#endif + } + } + + [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 + { +#if BIGENDIAN + get => ((ulong)U1 << 32) | U0; + set { U1 = (uint)(value >> 32); U0 = (uint)value; } +#else + get => ulo64LE; + set => ulo64LE = value; +#endif + } + + public ulong Mid64 + { +#if BIGENDIAN + get => ((ulong)U3 << 32) | U2; + set { U3 = (uint)(value >> 32); U2 = (uint)value; } +#else + get => umid64LE; + set => umid64LE = value; +#endif + } + + public ulong High64 + { +#if BIGENDIAN + get => ((ulong)U5 << 32) | U4; + set { U5 = (uint)(value >> 32); U4 = (uint)value; } +#else + get => uhigh64LE; + set => uhigh64LE = value; +#endif + } + + public int Length => 6; + } + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Decimal.cs b/src/System.Private.CoreLib/shared/System/Decimal.cs new file mode 100644 index 0000000000..a79a7c3e7f --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/Decimal.cs @@ -0,0 +1,1119 @@ +// 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. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System +{ + // Implements the Decimal data type. The Decimal data type can + // represent values ranging from -79,228,162,514,264,337,593,543,950,335 to + // 79,228,162,514,264,337,593,543,950,335 with 28 significant digits. The + // Decimal data type is ideally suited to financial calculations that + // require a large number of significant digits and no round-off errors. + // + // The finite set of values of type Decimal are of the form m + // / 10e, where m is an integer such that + // -296 <; m <; 296, and e is an integer + // between 0 and 28 inclusive. + // + // Contrary to the float and double data types, decimal + // fractional numbers such as 0.1 can be represented exactly in the + // Decimal representation. In the float and double + // representations, such numbers are often infinite fractions, making those + // representations more prone to round-off errors. + // + // The Decimal class implements widening conversions from the + // ubyte, char, short, int, and long types + // to Decimal. These widening conversions never loose any information + // and never throw exceptions. The Decimal class also implements + // narrowing conversions from Decimal to ubyte, char, + // short, int, and long. These narrowing conversions round + // the Decimal value towards zero to the nearest integer, and then + // converts that integer to the destination type. An OverflowException + // is thrown if the result is not within the range of the destination type. + // + // The Decimal class provides a widening conversion from + // Currency to Decimal. This widening conversion never loses any + // information and never throws exceptions. The Currency class provides + // a narrowing conversion from Decimal to Currency. This + // narrowing conversion rounds the Decimal to four decimals and then + // converts that number to a Currency. An OverflowException + // is thrown if the result is not within the range of the Currency type. + // + // The Decimal class provides narrowing conversions to and from the + // float and double types. A conversion from Decimal to + // float or double may loose precision, but will not loose + // information about the overall magnitude of the numeric value, and will never + // throw an exception. A conversion from float or double to + // Decimal throws an OverflowException if the value is not within + // the range of the Decimal type. + [StructLayout(LayoutKind.Sequential)] + [Serializable] + [System.Runtime.Versioning.NonVersionable] // This only applies to field layout + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public readonly partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable, IEquatable, IDeserializationCallback, ISpanFormattable + { + // Sign mask for the flags field. A value of zero in this bit indicates a + // positive Decimal value, and a value of one in this bit indicates a + // negative Decimal value. + // + // Look at OleAut's DECIMAL_NEG constant to check for negative values + // in native code. + private const int SignMask = unchecked((int)0x80000000); + + // Scale mask for the flags field. This byte in the flags field contains + // the power of 10 to divide the Decimal value by. The scale byte must + // contain a value between 0 and 28 inclusive. + private const int ScaleMask = 0x00FF0000; + + // Number of bits scale is shifted by. + private const int ScaleShift = 16; + + // Constant representing the Decimal value 0. + public const decimal Zero = 0m; + + // Constant representing the Decimal value 1. + public const decimal One = 1m; + + // Constant representing the Decimal value -1. + public const decimal MinusOne = -1m; + + // Constant representing the largest possible Decimal value. The value of + // this constant is 79,228,162,514,264,337,593,543,950,335. + public const decimal MaxValue = 79228162514264337593543950335m; + + // Constant representing the smallest possible Decimal value. The value of + // this constant is -79,228,162,514,264,337,593,543,950,335. + public const decimal MinValue = -79228162514264337593543950335m; + + // The lo, mid, hi, and flags fields contain the representation of the + // Decimal value. The lo, mid, and hi fields contain the 96-bit integer + // part of the Decimal. Bits 0-15 (the lower word) of the flags field are + // unused and must be zero; bits 16-23 contain must contain a value between + // 0 and 28, indicating the power of 10 to divide the 96-bit integer part + // by to produce the Decimal value; bits 24-30 are unused and must be zero; + // and finally bit 31 indicates the sign of the Decimal value, 0 meaning + // positive and 1 meaning negative. + // + // NOTE: Do not change the order in which these fields are declared. The + // native methods in this class rely on this particular order. + // Do not rename (binary serialization). + private readonly int flags; + private readonly int hi; + private readonly int lo; + private readonly int mid; + + // Constructs a Decimal from an integer value. + // + public Decimal(int value) + { + if (value >= 0) + { + flags = 0; + } + else + { + flags = SignMask; + value = -value; + } + lo = value; + mid = 0; + hi = 0; + } + + // Constructs a Decimal from an unsigned integer value. + // + [CLSCompliant(false)] + public Decimal(uint value) + { + flags = 0; + lo = (int)value; + mid = 0; + hi = 0; + } + + // Constructs a Decimal from a long value. + // + public Decimal(long value) + { + if (value >= 0) + { + flags = 0; + } + else + { + flags = SignMask; + value = -value; + } + lo = (int)value; + mid = (int)(value >> 32); + hi = 0; + } + + // Constructs a Decimal from an unsigned long value. + // + [CLSCompliant(false)] + public Decimal(ulong value) + { + flags = 0; + lo = (int)value; + mid = (int)(value >> 32); + hi = 0; + } + + // Constructs a Decimal from a float value. + // + public Decimal(float value) + { + DecCalc.VarDecFromR4(value, out AsMutable(ref this)); + } + + // Constructs a Decimal from a double value. + // + public Decimal(double value) + { + DecCalc.VarDecFromR8(value, out AsMutable(ref this)); + } + + // + // Decimal <==> Currency conversion. + // + // A Currency represents a positive or negative decimal value with 4 digits past the decimal point. The actual Int64 representation used by these methods + // is the currency value multiplied by 10,000. For example, a currency value of $12.99 would be represented by the Int64 value 129,900. + // + public static decimal FromOACurrency(long cy) + { + ulong absoluteCy; // has to be ulong to accommodate the case where cy == long.MinValue. + bool isNegative = false; + if (cy < 0) + { + isNegative = true; + absoluteCy = (ulong)(-cy); + } + else + { + absoluteCy = (ulong)cy; + } + + // In most cases, FromOACurrency() produces a Decimal with Scale set to 4. Unless, that is, some of the trailing digits past the decimal point are zero, + // in which case, for compatibility with .Net, we reduce the Scale by the number of zeros. While the result is still numerically equivalent, the scale does + // affect the ToString() value. In particular, it prevents a converted currency value of $12.95 from printing uglily as "12.9500". + int scale = 4; + if (absoluteCy != 0) // For compatibility, a currency of 0 emits the Decimal "0.0000" (scale set to 4). + { + while (scale != 0 && ((absoluteCy % 10) == 0)) + { + scale--; + absoluteCy /= 10; + } + } + + return new decimal((int)absoluteCy, (int)(absoluteCy >> 32), 0, isNegative, (byte)scale); + } + + public static long ToOACurrency(decimal value) + { + return DecCalc.VarCyFromDec(ref AsMutable(ref value)); + } + + private static bool IsValid(int flags) => (flags & ~(SignMask | ScaleMask)) == 0 && ((uint)(flags & ScaleMask) <= (28 << ScaleShift)); + + // Constructs a Decimal from an integer array containing a binary + // representation. The bits argument must be a non-null integer + // array with four elements. bits[0], bits[1], and + // bits[2] contain the low, middle, and high 32 bits of the 96-bit + // integer part of the Decimal. bits[3] contains the scale factor + // and sign of the Decimal: bits 0-15 (the lower word) are unused and must + // be zero; bits 16-23 must contain a value between 0 and 28, indicating + // the power of 10 to divide the 96-bit integer part by to produce the + // Decimal value; bits 24-30 are unused and must be zero; and finally bit + // 31 indicates the sign of the Decimal value, 0 meaning positive and 1 + // meaning negative. + // + // Note that there are several possible binary representations for the + // same numeric value. For example, the value 1 can be represented as {1, + // 0, 0, 0} (integer value 1 with a scale factor of 0) and equally well as + // {1000, 0, 0, 0x30000} (integer value 1000 with a scale factor of 3). + // The possible binary representations of a particular value are all + // equally valid, and all are numerically equivalent. + // + public Decimal(int[] bits) + { + if (bits == null) + throw new ArgumentNullException(nameof(bits)); + if (bits.Length == 4) + { + int f = bits[3]; + if (IsValid(f)) + { + lo = bits[0]; + mid = bits[1]; + hi = bits[2]; + flags = f; + return; + } + } + throw new ArgumentException(SR.Arg_DecBitCtor); + } + + // Constructs a Decimal from its constituent parts. + // + public Decimal(int lo, int mid, int hi, bool isNegative, byte scale) + { + if (scale > 28) + throw new ArgumentOutOfRangeException(nameof(scale), SR.ArgumentOutOfRange_DecimalScale); + this.lo = lo; + this.mid = mid; + this.hi = hi; + flags = ((int)scale) << 16; + if (isNegative) + flags |= SignMask; + } + + void IDeserializationCallback.OnDeserialization(object sender) + { + // OnDeserialization is called after each instance of this class is deserialized. + // This callback method performs decimal validation after being deserialized. + if (!IsValid(flags)) + throw new SerializationException(SR.Overflow_Decimal); + } + + // Constructs a Decimal from its constituent parts. + private Decimal(int lo, int mid, int hi, int flags) + { + if (IsValid(flags)) + { + this.lo = lo; + this.mid = mid; + this.hi = hi; + this.flags = flags; + return; + } + throw new ArgumentException(SR.Arg_DecBitCtor); + } + + private Decimal(in decimal d, int flags) + { + this = d; + this.flags = flags; + } + + // Returns the absolute value of the given Decimal. If d is + // positive, the result is d. If d is negative, the result + // is -d. + // + internal static decimal Abs(ref decimal d) + { + return new decimal(in d, d.flags & ~SignMask); + } + + // Adds two Decimal values. + // + public static decimal Add(decimal d1, decimal d2) + { + DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), false); + return d1; + } + + + // Rounds a Decimal to an integer value. The Decimal argument is rounded + // towards positive infinity. + public static decimal Ceiling(decimal d) + { + int flags = d.flags; + if ((flags & ScaleMask) != 0) + DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Ceiling); + return d; + } + + // Compares two Decimal values, returning an integer that indicates their + // relationship. + // + public static int Compare(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2); + } + + // Compares this object to another object, returning an integer that + // indicates the relationship. + // Returns a value less than zero if this object + // null is considered to be less than any instance. + // If object is not of type Decimal, this method throws an ArgumentException. + // + public int CompareTo(object value) + { + if (value == null) + return 1; + if (!(value is decimal)) + throw new ArgumentException(SR.Arg_MustBeDecimal); + + decimal other = (decimal)value; + return DecCalc.VarDecCmp(in this, in other); + } + + public int CompareTo(decimal value) + { + return DecCalc.VarDecCmp(in this, in value); + } + + // Divides two Decimal values. + // + public static decimal Divide(decimal d1, decimal d2) + { + DecCalc.VarDecDiv(ref AsMutable(ref d1), ref AsMutable(ref d2)); + return d1; + } + + // Checks if this Decimal is equal to a given object. Returns true + // if the given object is a boxed Decimal and its value is equal to the + // value of this Decimal. Returns false otherwise. + // + public override bool Equals(object value) + { + if (value is decimal) + { + decimal other = (decimal)value; + return DecCalc.VarDecCmp(in this, in other) == 0; + } + return false; + } + + public bool Equals(decimal value) + { + return DecCalc.VarDecCmp(in this, in value) == 0; + } + + // Returns the hash code for this Decimal. + // + public override int GetHashCode() => DecCalc.GetHashCode(in this); + + // Compares two Decimal values for equality. Returns true if the two + // Decimal values are equal, or false if they are not equal. + // + public static bool Equals(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) == 0; + } + + // Rounds a Decimal to an integer value. The Decimal argument is rounded + // towards negative infinity. + // + public static decimal Floor(decimal d) + { + int flags = d.flags; + if ((flags & ScaleMask) != 0) + DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Floor); + return d; + } + + // Converts this Decimal to a string. The resulting string consists of an + // optional minus sign ("-") followed to a sequence of digits ("0" - "9"), + // optionally followed by a decimal point (".") and another sequence of + // digits. + // + public override string ToString() + { + return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo); + } + + public string ToString(string format) + { + return Number.FormatDecimal(this, format, NumberFormatInfo.CurrentInfo); + } + + public string ToString(IFormatProvider provider) + { + return Number.FormatDecimal(this, null, NumberFormatInfo.GetInstance(provider)); + } + + public string ToString(string format, IFormatProvider provider) + { + return Number.FormatDecimal(this, format, NumberFormatInfo.GetInstance(provider)); + } + + public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) + { + return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); + } + + // Converts a string to a Decimal. The string must consist of an optional + // minus sign ("-") followed by a sequence of digits ("0" - "9"). The + // sequence of digits may optionally contain a single decimal point (".") + // character. Leading and trailing whitespace characters are allowed. + // Parse also allows a currency symbol, a trailing negative sign, and + // parentheses in the number. + // + public static decimal Parse(string s) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo); + } + + public static decimal Parse(string s, NumberStyles style) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, style, NumberFormatInfo.CurrentInfo); + } + + public static decimal Parse(string s, IFormatProvider provider) + { + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.GetInstance(provider)); + } + + public static decimal Parse(string s, NumberStyles style, IFormatProvider provider) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); + return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static decimal Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider provider = null) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); + } + + public static bool TryParse(string s, out decimal result) + { + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(ReadOnlySpan s, out decimal result) + { + return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); + } + + public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out decimal result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s == null) + { + result = 0; + return false; + } + + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out decimal result) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); + } + + // Returns a binary representation of a Decimal. The return value is an + // integer array with four elements. Elements 0, 1, and 2 contain the low, + // middle, and high 32 bits of the 96-bit integer part of the Decimal. + // Element 3 contains the scale factor and sign of the Decimal: bits 0-15 + // (the lower word) are unused; bits 16-23 contain a value between 0 and + // 28, indicating the power of 10 to divide the 96-bit integer part by to + // produce the Decimal value; bits 24-30 are unused; and finally bit 31 + // indicates the sign of the Decimal value, 0 meaning positive and 1 + // meaning negative. + // + public static int[] GetBits(decimal d) + { + return new int[] { d.lo, d.mid, d.hi, d.flags }; + } + + internal static void GetBytes(decimal d, byte[] buffer) + { + Debug.Assert((buffer != null && buffer.Length >= 16), "[GetBytes]buffer != null && buffer.Length >= 16"); + buffer[0] = (byte)d.lo; + buffer[1] = (byte)(d.lo >> 8); + buffer[2] = (byte)(d.lo >> 16); + buffer[3] = (byte)(d.lo >> 24); + + buffer[4] = (byte)d.mid; + buffer[5] = (byte)(d.mid >> 8); + buffer[6] = (byte)(d.mid >> 16); + buffer[7] = (byte)(d.mid >> 24); + + buffer[8] = (byte)d.hi; + buffer[9] = (byte)(d.hi >> 8); + buffer[10] = (byte)(d.hi >> 16); + buffer[11] = (byte)(d.hi >> 24); + + buffer[12] = (byte)d.flags; + buffer[13] = (byte)(d.flags >> 8); + buffer[14] = (byte)(d.flags >> 16); + buffer[15] = (byte)(d.flags >> 24); + } + + internal static decimal ToDecimal(byte[] buffer) + { + Debug.Assert((buffer != null && buffer.Length >= 16), "[ToDecimal]buffer != null && buffer.Length >= 16"); + int lo = ((int)buffer[0]) | ((int)buffer[1] << 8) | ((int)buffer[2] << 16) | ((int)buffer[3] << 24); + int mid = ((int)buffer[4]) | ((int)buffer[5] << 8) | ((int)buffer[6] << 16) | ((int)buffer[7] << 24); + int hi = ((int)buffer[8]) | ((int)buffer[9] << 8) | ((int)buffer[10] << 16) | ((int)buffer[11] << 24); + int flags = ((int)buffer[12]) | ((int)buffer[13] << 8) | ((int)buffer[14] << 16) | ((int)buffer[15] << 24); + return new decimal(lo, mid, hi, flags); + } + + // Returns the larger of two Decimal values. + // + internal static ref decimal Max(ref decimal d1, ref decimal d2) + { + return ref DecCalc.VarDecCmp(in d1, in d2) >= 0 ? ref d1 : ref d2; + } + + // Returns the smaller of two Decimal values. + // + internal static ref decimal Min(ref decimal d1, ref decimal d2) + { + return ref DecCalc.VarDecCmp(in d1, in d2) < 0 ? ref d1 : ref d2; + } + + public static decimal Remainder(decimal d1, decimal d2) + { + DecCalc.VarDecMod(ref AsMutable(ref d1), ref AsMutable(ref d2)); + return d1; + } + + // Multiplies two Decimal values. + // + public static decimal Multiply(decimal d1, decimal d2) + { + DecCalc.VarDecMul(ref AsMutable(ref d1), ref AsMutable(ref d2)); + return d1; + } + + // Returns the negated value of the given Decimal. If d is non-zero, + // the result is -d. If d is zero, the result is zero. + // + public static decimal Negate(decimal d) + { + return new decimal(in d, d.flags ^ SignMask); + } + + // Rounds a Decimal value to a given number of decimal places. The value + // given by d is rounded to the number of decimal places given by + // decimals. The decimals argument must be an integer between + // 0 and 28 inclusive. + // + // By default a mid-point value is rounded to the nearest even number. If the mode is + // passed in, it can also round away from zero. + + public static decimal Round(decimal d) => Round(ref d, 0, MidpointRounding.ToEven); + public static decimal Round(decimal d, int decimals) => Round(ref d, decimals, MidpointRounding.ToEven); + public static decimal Round(decimal d, MidpointRounding mode) => Round(ref d, 0, mode); + public static decimal Round(decimal d, int decimals, MidpointRounding mode) => Round(ref d, decimals, mode); + + private static decimal Round(ref decimal d, int decimals, MidpointRounding mode) + { + if ((uint)decimals > 28) + throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound); + if ((uint)mode > (uint)MidpointRounding.AwayFromZero) + throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); + + int scale = d.Scale - decimals; + if (scale > 0) + DecCalc.InternalRound(ref AsMutable(ref d), (uint)scale, (DecCalc.RoundingMode)mode); + return d; + } + + internal static int Sign(ref decimal d) => (d.lo | d.mid | d.hi) == 0 ? 0 : (d.flags >> 31) | 1; + + // Subtracts two Decimal values. + // + public static decimal Subtract(decimal d1, decimal d2) + { + DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), true); + return d1; + } + + // Converts a Decimal to an unsigned byte. The Decimal value is rounded + // towards zero to the nearest integer value, and the result of this + // operation is returned as a byte. + // + public static byte ToByte(decimal value) + { + uint temp; + try + { + temp = ToUInt32(value); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Byte, e); + } + if (temp != (byte)temp) throw new OverflowException(SR.Overflow_Byte); + return (byte)temp; + } + + // Converts a Decimal to a signed byte. The Decimal value is rounded + // towards zero to the nearest integer value, and the result of this + // operation is returned as a byte. + // + [CLSCompliant(false)] + public static sbyte ToSByte(decimal value) + { + int temp; + try + { + temp = ToInt32(value); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_SByte, e); + } + if (temp != (sbyte)temp) throw new OverflowException(SR.Overflow_SByte); + return (sbyte)temp; + } + + // Converts a Decimal to a short. The Decimal value is + // rounded towards zero to the nearest integer value, and the result of + // this operation is returned as a short. + // + public static short ToInt16(decimal value) + { + int temp; + try + { + temp = ToInt32(value); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Int16, e); + } + if (temp != (short)temp) throw new OverflowException(SR.Overflow_Int16); + return (short)temp; + } + + // Converts a Decimal to a double. Since a double has fewer significant + // digits than a Decimal, this operation may produce round-off errors. + // + public static double ToDouble(decimal d) + { + return DecCalc.VarR8FromDec(ref d); + } + + // Converts a Decimal to an integer. The Decimal value is rounded towards + // zero to the nearest integer value, and the result of this operation is + // returned as an integer. + // + public static int ToInt32(decimal d) + { + Truncate(ref d); + if ((d.hi | d.mid) == 0) + { + int i = d.lo; + if (!d.IsNegative) + { + if (i >= 0) return i; + } + else + { + i = -i; + if (i <= 0) return i; + } + } + throw new OverflowException(SR.Overflow_Int32); + } + + // Converts a Decimal to a long. The Decimal value is rounded towards zero + // to the nearest integer value, and the result of this operation is + // returned as a long. + // + public static long ToInt64(decimal d) + { + Truncate(ref d); + if (d.hi == 0) + { + long l = (long)d.Low64; + if (!d.IsNegative) + { + if (l >= 0) return l; + } + else + { + l = -l; + if (l <= 0) return l; + } + } + throw new OverflowException(SR.Overflow_Int64); + } + + // Converts a Decimal to an ushort. The Decimal + // value is rounded towards zero to the nearest integer value, and the + // result of this operation is returned as an ushort. + // + [CLSCompliant(false)] + public static ushort ToUInt16(decimal value) + { + uint temp; + try + { + temp = ToUInt32(value); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_UInt16, e); + } + if (temp != (ushort)temp) throw new OverflowException(SR.Overflow_UInt16); + return (ushort)temp; + } + + // Converts a Decimal to an unsigned integer. The Decimal + // value is rounded towards zero to the nearest integer value, and the + // result of this operation is returned as an unsigned integer. + // + [CLSCompliant(false)] + public static uint ToUInt32(decimal d) + { + Truncate(ref d); + if ((d.hi | d.mid) == 0) + { + uint i = d.Low; + if (!d.IsNegative || i == 0) + return i; + } + throw new OverflowException(SR.Overflow_UInt32); + } + + // Converts a Decimal to an unsigned long. The Decimal + // value is rounded towards zero to the nearest integer value, and the + // result of this operation is returned as a long. + // + [CLSCompliant(false)] + public static ulong ToUInt64(decimal d) + { + Truncate(ref d); + if (d.hi == 0) + { + ulong l = d.Low64; + if (!d.IsNegative || l == 0) + return l; + } + throw new OverflowException(SR.Overflow_UInt64); + } + + // Converts a Decimal to a float. Since a float has fewer significant + // digits than a Decimal, this operation may produce round-off errors. + // + public static float ToSingle(decimal d) + { + return DecCalc.VarR4FromDec(ref d); + } + + // Truncates a Decimal to an integer value. The Decimal argument is rounded + // towards zero to the nearest integer value, corresponding to removing all + // digits after the decimal point. + // + public static decimal Truncate(decimal d) + { + Truncate(ref d); + return d; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Truncate(ref decimal d) + { + int flags = d.flags; + if ((flags & ScaleMask) != 0) + DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Truncate); + } + + public static implicit operator decimal(byte value) + { + return new decimal(value); + } + + [CLSCompliant(false)] + public static implicit operator decimal(sbyte value) + { + return new decimal(value); + } + + public static implicit operator decimal(short value) + { + return new decimal(value); + } + + [CLSCompliant(false)] + public static implicit operator decimal(ushort value) + { + return new decimal(value); + } + + public static implicit operator decimal(char value) + { + return new decimal(value); + } + + public static implicit operator decimal(int value) + { + return new decimal(value); + } + + [CLSCompliant(false)] + public static implicit operator decimal(uint value) + { + return new decimal(value); + } + + public static implicit operator decimal(long value) + { + return new decimal(value); + } + + [CLSCompliant(false)] + public static implicit operator decimal(ulong value) + { + return new decimal(value); + } + + + public static explicit operator decimal(float value) + { + return new decimal(value); + } + + public static explicit operator decimal(double value) + { + return new decimal(value); + } + + public static explicit operator byte(decimal value) + { + return ToByte(value); + } + + [CLSCompliant(false)] + public static explicit operator sbyte(decimal value) + { + return ToSByte(value); + } + + public static explicit operator char(decimal value) + { + ushort temp; + try + { + temp = ToUInt16(value); + } + catch (OverflowException e) + { + throw new OverflowException(SR.Overflow_Char, e); + } + return (char)temp; + } + + public static explicit operator short(decimal value) + { + return ToInt16(value); + } + + [CLSCompliant(false)] + public static explicit operator ushort(decimal value) + { + return ToUInt16(value); + } + + public static explicit operator int(decimal value) + { + return ToInt32(value); + } + + [CLSCompliant(false)] + public static explicit operator uint(decimal value) + { + return ToUInt32(value); + } + + public static explicit operator long(decimal value) + { + return ToInt64(value); + } + + [CLSCompliant(false)] + public static explicit operator ulong(decimal value) + { + return ToUInt64(value); + } + + public static explicit operator float(decimal value) + { + return ToSingle(value); + } + + public static explicit operator double(decimal value) + { + return ToDouble(value); + } + + public static decimal operator +(decimal d) + { + return d; + } + + public static decimal operator -(decimal d) + { + return Negate(d); + } + + public static decimal operator ++(decimal d) + { + return Add(d, One); + } + + public static decimal operator --(decimal d) + { + return Subtract(d, One); + } + + public static decimal operator +(decimal d1, decimal d2) + { + return Add(d1, d2); + } + + public static decimal operator -(decimal d1, decimal d2) + { + return Subtract(d1, d2); + } + + public static decimal operator *(decimal d1, decimal d2) + { + return Multiply(d1, d2); + } + + public static decimal operator /(decimal d1, decimal d2) + { + return Divide(d1, d2); + } + + public static decimal operator %(decimal d1, decimal d2) + { + return Remainder(d1, d2); + } + + public static bool operator ==(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) == 0; + } + + public static bool operator !=(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) != 0; + } + + public static bool operator <(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) < 0; + } + + public static bool operator <=(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) <= 0; + } + + public static bool operator >(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) > 0; + } + + public static bool operator >=(decimal d1, decimal d2) + { + return DecCalc.VarDecCmp(in d1, in d2) >= 0; + } + + // + // IConvertible implementation + // + + public TypeCode GetTypeCode() + { + return TypeCode.Decimal; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(this); + } + + + char IConvertible.ToChar(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "Char")); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(this); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(this); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(this); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(this); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(this); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(this); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(this); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(this); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(this); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(this); + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return this; + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "DateTime")); + } + + object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.DefaultToType((IConvertible)this, type, provider); + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/Number.Parsing.cs b/src/System.Private.CoreLib/shared/System/Number.Parsing.cs index c764f08420..9b9bf66456 100644 --- a/src/System.Private.CoreLib/shared/System/Number.Parsing.cs +++ b/src/System.Private.CoreLib/shared/System/Number.Parsing.cs @@ -1466,6 +1466,83 @@ namespace System return result; } + private static unsafe bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value) + { + decimal d = new decimal(); + + char* p = number.digits; + 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 > DecimalPrecision) + 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')))))))) + { + decimal.DecMul10(ref d); + if (*p != 0) + decimal.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 roundinp 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) + { + decimal.DecAddInt32(ref d, 1); + if ((d.High | d.Mid | d.Low) == 0) + { + d = new decimal(unchecked((int)0x9999999A), unchecked((int)0x99999999), 0x19999999, false, 0); + e++; + } + } + } + } + + if (e > 0) + return false; + + if (e <= -DecimalPrecision) + { + // 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. + value = new decimal(0, 0, 0, number.sign, DecimalPrecision - 1); + } + else + { + value = new decimal((int)d.Low, (int)d.Mid, (int)d.High, number.sign, (byte)-e); + } + return true; + } + internal static double ParseDouble(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { NumberBuffer number = default; diff --git a/src/System.Private.CoreLib/src/System/Currency.cs b/src/System.Private.CoreLib/src/System/Currency.cs index 81d0bbf8a5..c9e8dc030e 100644 --- a/src/System.Private.CoreLib/src/System/Currency.cs +++ b/src/System.Private.CoreLib/src/System/Currency.cs @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -using System; -using System.Globalization; -using System.Runtime.CompilerServices; -using System.Runtime.Versioning; - namespace System { internal struct Currency @@ -18,46 +12,17 @@ namespace System // public Currency(decimal value) { - m_value = decimal.ToCurrency(value).m_value; - } - - // Constructs a Currency from a long value without scaling. The - // ignored parameter exists only to distinguish this constructor - // from the constructor that takes a long. Used only in the System - // package, especially in Variant. - internal Currency(long value, int ignored) - { - m_value = value; - } - - // Creates a Currency from an OLE Automation Currency. This method - // applies no scaling to the Currency value, essentially doing a bitwise - // copy. - // - public static Currency FromOACurrency(long cy) - { - return new Currency(cy, 0); - } - - //Creates an OLE Automation Currency from a Currency instance. This - // method applies no scaling to the Currency value, essentially doing - // a bitwise copy. - // - public long ToOACurrency() - { - return m_value; + m_value = decimal.ToOACurrency(value); } + } - // Converts a Currency to a decimal. + partial struct Decimal + { + // Constructs a Decimal from a Currency value. // - public static decimal ToDecimal(Currency c) + internal Decimal(Currency value) { - decimal result = new decimal(); - FCallToDecimal(ref result, c); - return result; + this = FromOACurrency(value.m_value); } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallToDecimal(ref decimal result, Currency c); } } diff --git a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs deleted file mode 100644 index d18a52e348..0000000000 --- a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs +++ /dev/null @@ -1,96 +0,0 @@ -// 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. - -using System.Runtime.CompilerServices; -using Internal.Runtime.CompilerServices; - -namespace System -{ - public partial struct Decimal - { - internal static uint DecDivMod1E9(ref decimal value) - { - return D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0, - ref Unsafe.As(ref value.hi)), - ref Unsafe.As(ref value.mid)), - ref Unsafe.As(ref value.lo)); - - uint D32DivMod1E9(uint hi32, ref uint lo32) - { - ulong n = (ulong)hi32 << 32 | lo32; - lo32 = (uint)(n / 1000000000); - return (uint)(n % 1000000000); - } - } - - private static int GetHashCode(ref 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) & ~(uint)ScaleMask) | (uint)scale << ScaleShift; - return (int)(flags ^ (uint)(high64 >> 32) ^ (uint)high64 ^ low); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Div96ByConst(ref ulong high64, ref uint low, uint pow) - { - ulong div64; -#if !BIT64 - if (high64 <= uint.MaxValue) - { - div64 = ((high64 << 32) | low) / pow; - if (low == (uint)div64 * pow) - { - low = (uint)div64; - high64 = div64 >> 32; - return true; - } - return false; - } -#endif - div64 = high64 / pow; - uint div = (uint)((((high64 - (uint)div64 * pow) << 32) | low) / pow); - if (low == div * pow) - { - high64 = div64; - low = div; - return true; - } - 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. - // - // 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. - // - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Unscale(ref uint low, ref ulong high64, ref int scale) - { - 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; - - 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--; - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Decimal.cs b/src/System.Private.CoreLib/src/System/Decimal.cs deleted file mode 100644 index 0a37664aed..0000000000 --- a/src/System.Private.CoreLib/src/System/Decimal.cs +++ /dev/null @@ -1,1337 +0,0 @@ -// 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. - - -using System; -using System.Globalization; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.Versioning; -using System.Runtime.Serialization; -using System.Diagnostics; - -namespace System -{ - // Implements the Decimal data type. The Decimal data type can - // represent values ranging from -79,228,162,514,264,337,593,543,950,335 to - // 79,228,162,514,264,337,593,543,950,335 with 28 significant digits. The - // Decimal data type is ideally suited to financial calculations that - // require a large number of significant digits and no round-off errors. - // - // The finite set of values of type Decimal are of the form m - // / 10e, where m is an integer such that - // -296 <; m <; 296, and e is an integer - // between 0 and 28 inclusive. - // - // Contrary to the float and double data types, decimal - // fractional numbers such as 0.1 can be represented exactly in the - // Decimal representation. In the float and double - // representations, such numbers are often infinite fractions, making those - // representations more prone to round-off errors. - // - // The Decimal class implements widening conversions from the - // ubyte, char, short, int, and long types - // to Decimal. These widening conversions never loose any information - // and never throw exceptions. The Decimal class also implements - // narrowing conversions from Decimal to ubyte, char, - // short, int, and long. These narrowing conversions round - // the Decimal value towards zero to the nearest integer, and then - // converts that integer to the destination type. An OverflowException - // is thrown if the result is not within the range of the destination type. - // - // The Decimal class provides a widening conversion from - // Currency to Decimal. This widening conversion never loses any - // information and never throws exceptions. The Currency class provides - // a narrowing conversion from Decimal to Currency. This - // narrowing conversion rounds the Decimal to four decimals and then - // converts that number to a Currency. An OverflowException - // is thrown if the result is not within the range of the Currency type. - // - // The Decimal class provides narrowing conversions to and from the - // float and double types. A conversion from Decimal to - // float or double may loose precision, but will not loose - // information about the overall magnitude of the numeric value, and will never - // throw an exception. A conversion from float or double to - // Decimal throws an OverflowException if the value is not within - // the range of the Decimal type. - [StructLayout(LayoutKind.Sequential)] - [Serializable] - [System.Runtime.Versioning.NonVersionable] // This only applies to field layout - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable, IEquatable, IDeserializationCallback, ISpanFormattable - { - // Sign mask for the flags field. A value of zero in this bit indicates a - // positive Decimal value, and a value of one in this bit indicates a - // negative Decimal value. - // - // Look at OleAut's DECIMAL_NEG constant to check for negative values - // in native code. - private const int SignMask = unchecked((int)0x80000000); - private const byte DECIMAL_NEG = 0x80; - private const byte DECIMAL_ADD = 0x00; - - // Scale mask for the flags field. This byte in the flags field contains - // the power of 10 to divide the Decimal value by. The scale byte must - // contain a value between 0 and 28 inclusive. - private const int ScaleMask = 0x00FF0000; - - // Number of bits scale is shifted by. - private const int ScaleShift = 16; - - // The maximum power of 10 that a 32 bit integer can store - private const int MaxInt32Scale = 9; - - // Fast access for 10^n where n is 0-9 - private static uint[] Powers10 = new uint[] { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000 - }; - - // Constant representing the Decimal value 0. - public const decimal Zero = 0m; - - // Constant representing the Decimal value 1. - public const decimal One = 1m; - - // Constant representing the Decimal value -1. - public const decimal MinusOne = -1m; - - // Constant representing the largest possible Decimal value. The value of - // this constant is 79,228,162,514,264,337,593,543,950,335. - public const decimal MaxValue = 79228162514264337593543950335m; - - // Constant representing the smallest possible Decimal value. The value of - // this constant is -79,228,162,514,264,337,593,543,950,335. - public const decimal MinValue = -79228162514264337593543950335m; - - - // Constant representing the negative number that is the closest possible - // Decimal value to -0m. - private const decimal NearNegativeZero = -0.0000000000000000000000000001m; - - // Constant representing the positive number that is the closest possible - // Decimal value to +0m. - private const decimal NearPositiveZero = +0.0000000000000000000000000001m; - - // The lo, mid, hi, and flags fields contain the representation of the - // Decimal value. The lo, mid, and hi fields contain the 96-bit integer - // part of the Decimal. Bits 0-15 (the lower word) of the flags field are - // unused and must be zero; bits 16-23 contain must contain a value between - // 0 and 28, indicating the power of 10 to divide the 96-bit integer part - // by to produce the Decimal value; bits 24-30 are unused and must be zero; - // and finally bit 31 indicates the sign of the Decimal value, 0 meaning - // positive and 1 meaning negative. - // - // NOTE: Do not change the order in which these fields are declared. The - // native methods in this class rely on this particular order. - // Do not rename (binary serialization). - private int flags; - private int hi; - private int lo; - private int mid; - - internal uint High => (uint)hi; - internal uint Low => (uint)lo; - internal uint Mid => (uint)mid; - - // Constructs a zero Decimal. - //public Decimal() { - // lo = 0; - // mid = 0; - // hi = 0; - // flags = 0; - //} - - // Constructs a Decimal from an integer value. - // - public Decimal(int value) - { - if (value >= 0) - { - flags = 0; - } - else - { - flags = SignMask; - value = -value; - } - lo = value; - mid = 0; - hi = 0; - } - - // Constructs a Decimal from an unsigned integer value. - // - [CLSCompliant(false)] - public Decimal(uint value) - { - flags = 0; - lo = (int)value; - mid = 0; - hi = 0; - } - - // Constructs a Decimal from a long value. - // - public Decimal(long value) - { - if (value >= 0) - { - flags = 0; - } - else - { - flags = SignMask; - value = -value; - } - lo = (int)value; - mid = (int)(value >> 32); - hi = 0; - } - - // Constructs a Decimal from an unsigned long value. - // - [CLSCompliant(false)] - public Decimal(ulong value) - { - flags = 0; - lo = (int)value; - mid = (int)(value >> 32); - hi = 0; - } - - // Constructs a Decimal from a float value. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public extern Decimal(float value); - - // Constructs a Decimal from a double value. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public extern Decimal(double value); - - // Constructs a Decimal from a Currency value. - // - internal Decimal(Currency value) - { - this = Currency.ToDecimal(value); - } - - // Don't remove these 2 methods below. They are required by the fx when the are dealing with Currency in their - // databases - public static long ToOACurrency(decimal value) - { - return new Currency(value).ToOACurrency(); - } - - public static decimal FromOACurrency(long cy) - { - return Currency.ToDecimal(Currency.FromOACurrency(cy)); - } - - - // Constructs a Decimal from an integer array containing a binary - // representation. The bits argument must be a non-null integer - // array with four elements. bits[0], bits[1], and - // bits[2] contain the low, middle, and high 32 bits of the 96-bit - // integer part of the Decimal. bits[3] contains the scale factor - // and sign of the Decimal: bits 0-15 (the lower word) are unused and must - // be zero; bits 16-23 must contain a value between 0 and 28, indicating - // the power of 10 to divide the 96-bit integer part by to produce the - // Decimal value; bits 24-30 are unused and must be zero; and finally bit - // 31 indicates the sign of the Decimal value, 0 meaning positive and 1 - // meaning negative. - // - // Note that there are several possible binary representations for the - // same numeric value. For example, the value 1 can be represented as {1, - // 0, 0, 0} (integer value 1 with a scale factor of 0) and equally well as - // {1000, 0, 0, 0x30000} (integer value 1000 with a scale factor of 3). - // The possible binary representations of a particular value are all - // equally valid, and all are numerically equivalent. - // - public Decimal(int[] bits) - { - if (bits == null) - throw new ArgumentNullException(nameof(bits)); - if (bits.Length == 4) - { - int f = bits[3]; - if ((f & ~(SignMask | ScaleMask)) == 0 && (f & ScaleMask) <= (28 << 16)) - { - lo = bits[0]; - mid = bits[1]; - hi = bits[2]; - flags = f; - return; - } - } - throw new ArgumentException(SR.Arg_DecBitCtor); - } - - // Constructs a Decimal from its constituent parts. - // - public Decimal(int lo, int mid, int hi, bool isNegative, byte scale) - { - if (scale > 28) - throw new ArgumentOutOfRangeException(nameof(scale), SR.ArgumentOutOfRange_DecimalScale); - this.lo = lo; - this.mid = mid; - this.hi = hi; - flags = ((int)scale) << 16; - if (isNegative) - flags |= SignMask; - } - - [OnSerializing] - private void OnSerializing(StreamingContext ctx) - { - // OnSerializing is called before serialization of an object - try - { - // Run the constructor to validate the decimal - new decimal(lo, mid, hi, flags); - } - catch (ArgumentException e) - { - throw new SerializationException(SR.Overflow_Decimal, e); - } - } - - void IDeserializationCallback.OnDeserialization(object sender) - { - // OnDeserialization is called after each instance of this class is deserialized. - // This callback method performs decimal validation after being deserialized. - try - { - // Run the constructor to validate the decimal - new decimal(lo, mid, hi, flags); - } - catch (ArgumentException e) - { - throw new SerializationException(SR.Overflow_Decimal, e); - } - } - - // Constructs a Decimal from its constituent parts. - private Decimal(int lo, int mid, int hi, int flags) - { - if ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)) - { - this.lo = lo; - this.mid = mid; - this.hi = hi; - this.flags = flags; - return; - } - throw new ArgumentException(SR.Arg_DecBitCtor); - } - - // Returns the absolute value of the given Decimal. If d is - // positive, the result is d. If d is negative, the result - // is -d. - // - internal static decimal Abs(ref decimal d) - { - return new decimal(d.lo, d.mid, d.hi, d.flags & ~SignMask); - } - - // Adds two Decimal values. - // - public static decimal Add(decimal d1, decimal d2) - { - FCallAddSub(ref d1, ref d2, DECIMAL_ADD); - return d1; - } - - internal bool IsNegative => (flags & SignMask) != 0; - - internal int Scale => (byte)((uint)flags >> ScaleShift); - - // FCallAddSub adds or subtracts two decimal values. On return, d1 contains the result - // of the operation. Passing in DECIMAL_ADD or DECIMAL_NEG for bSign indicates - // addition or subtraction, respectively. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallAddSub(ref decimal d1, ref decimal d2, byte bSign); - - // Rounds a Decimal to an integer value. The Decimal argument is rounded - // towards positive infinity. - public static decimal Ceiling(decimal d) - { - return (-(decimal.Floor(-d))); - } - - // Compares two Decimal values, returning an integer that indicates their - // relationship. - // - public static int Compare(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int FCallCompare(ref decimal d1, ref decimal d2); - - // Compares this object to another object, returning an integer that - // indicates the relationship. - // Returns a value less than zero if this object - // null is considered to be less than any instance. - // If object is not of type Decimal, this method throws an ArgumentException. - // - public int CompareTo(object value) - { - if (value == null) - return 1; - if (!(value is decimal)) - throw new ArgumentException(SR.Arg_MustBeDecimal); - - decimal other = (decimal)value; - return FCallCompare(ref this, ref other); - } - - public int CompareTo(decimal value) - { - return FCallCompare(ref this, ref value); - } - - // Divides two Decimal values. - // - public static decimal Divide(decimal d1, decimal d2) - { - FCallDivide(ref d1, ref d2); - return d1; - } - - // FCallDivide divides two decimal values. On return, d1 contains the result - // of the operation. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallDivide(ref decimal d1, ref decimal d2); - - - // Checks if this Decimal is equal to a given object. Returns true - // if the given object is a boxed Decimal and its value is equal to the - // value of this Decimal. Returns false otherwise. - // - public override bool Equals(object value) - { - if (value is decimal) - { - decimal other = (decimal)value; - return FCallCompare(ref this, ref other) == 0; - } - return false; - } - - public bool Equals(decimal value) - { - return FCallCompare(ref this, ref value) == 0; - } - - // Returns the hash code for this Decimal. - // - public override int GetHashCode() => GetHashCode(ref this); - - // Compares two Decimal values for equality. Returns true if the two - // Decimal values are equal, or false if they are not equal. - // - public static bool Equals(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) == 0; - } - - // Rounds a Decimal to an integer value. The Decimal argument is rounded - // towards negative infinity. - // - public static decimal Floor(decimal d) - { - FCallFloor(ref d); - return d; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallFloor(ref decimal d); - - // Converts this Decimal to a string. The resulting string consists of an - // optional minus sign ("-") followed to a sequence of digits ("0" - "9"), - // optionally followed by a decimal point (".") and another sequence of - // digits. - // - public override string ToString() - { - return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo); - } - - public string ToString(string format) - { - return Number.FormatDecimal(this, format, NumberFormatInfo.CurrentInfo); - } - - public string ToString(IFormatProvider provider) - { - return Number.FormatDecimal(this, null, NumberFormatInfo.GetInstance(provider)); - } - - public string ToString(string format, IFormatProvider provider) - { - return Number.FormatDecimal(this, format, NumberFormatInfo.GetInstance(provider)); - } - - public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider provider = null) - { - return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); - } - - // Converts a string to a Decimal. The string must consist of an optional - // minus sign ("-") followed by a sequence of digits ("0" - "9"). The - // sequence of digits may optionally contain a single decimal point (".") - // character. Leading and trailing whitespace characters are allowed. - // Parse also allows a currency symbol, a trailing negative sign, and - // parentheses in the number. - // - public static decimal Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo); - } - - public static decimal Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseDecimal(s, style, NumberFormatInfo.CurrentInfo); - } - - public static decimal Parse(string s, IFormatProvider provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseDecimal(s, NumberStyles.Number, NumberFormatInfo.GetInstance(provider)); - } - - public static decimal Parse(string s, NumberStyles style, IFormatProvider provider) - { - NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); - } - - public static decimal Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider provider = null) - { - NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); - } - - public static bool TryParse(string s, out decimal result) - { - if (s == null) - { - result = 0; - return false; - } - - return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); - } - - public static bool TryParse(ReadOnlySpan s, out decimal result) - { - return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result); - } - - public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out decimal result) - { - NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - - if (s == null) - { - result = 0; - return false; - } - - return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out decimal result) - { - NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - // Returns a binary representation of a Decimal. The return value is an - // integer array with four elements. Elements 0, 1, and 2 contain the low, - // middle, and high 32 bits of the 96-bit integer part of the Decimal. - // Element 3 contains the scale factor and sign of the Decimal: bits 0-15 - // (the lower word) are unused; bits 16-23 contain a value between 0 and - // 28, indicating the power of 10 to divide the 96-bit integer part by to - // produce the Decimal value; bits 24-30 are unused; and finally bit 31 - // indicates the sign of the Decimal value, 0 meaning positive and 1 - // meaning negative. - // - public static int[] GetBits(decimal d) - { - return new int[] { d.lo, d.mid, d.hi, d.flags }; - } - - internal static void GetBytes(decimal d, byte[] buffer) - { - Debug.Assert((buffer != null && buffer.Length >= 16), "[GetBytes]buffer != null && buffer.Length >= 16"); - buffer[0] = (byte)d.lo; - buffer[1] = (byte)(d.lo >> 8); - buffer[2] = (byte)(d.lo >> 16); - buffer[3] = (byte)(d.lo >> 24); - - buffer[4] = (byte)d.mid; - buffer[5] = (byte)(d.mid >> 8); - buffer[6] = (byte)(d.mid >> 16); - buffer[7] = (byte)(d.mid >> 24); - - buffer[8] = (byte)d.hi; - buffer[9] = (byte)(d.hi >> 8); - buffer[10] = (byte)(d.hi >> 16); - buffer[11] = (byte)(d.hi >> 24); - - buffer[12] = (byte)d.flags; - buffer[13] = (byte)(d.flags >> 8); - buffer[14] = (byte)(d.flags >> 16); - buffer[15] = (byte)(d.flags >> 24); - } - - internal static decimal ToDecimal(byte[] buffer) - { - Debug.Assert((buffer != null && buffer.Length >= 16), "[ToDecimal]buffer != null && buffer.Length >= 16"); - int lo = ((int)buffer[0]) | ((int)buffer[1] << 8) | ((int)buffer[2] << 16) | ((int)buffer[3] << 24); - int mid = ((int)buffer[4]) | ((int)buffer[5] << 8) | ((int)buffer[6] << 16) | ((int)buffer[7] << 24); - int hi = ((int)buffer[8]) | ((int)buffer[9] << 8) | ((int)buffer[10] << 16) | ((int)buffer[11] << 24); - int flags = ((int)buffer[12]) | ((int)buffer[13] << 8) | ((int)buffer[14] << 16) | ((int)buffer[15] << 24); - return new decimal(lo, mid, hi, flags); - } - - // This method does a 'raw' and 'unchecked' addition of a UInt32 to a Decimal in place. - // 'raw' means that it operates on the internal 96-bit unsigned integer value and - // ingores the sign and scale. This means that it is not equivalent to just adding - // that number, as the sign and scale are effectively applied to the UInt32 value also. - // 'unchecked' means that it does not fail if you overflow the 96 bit value. - private static void InternalAddUInt32RawUnchecked(ref decimal value, uint i) - { - uint v; - uint sum; - v = (uint)value.lo; - sum = v + i; - value.lo = (int)sum; - if (sum < v || sum < i) - { - v = (uint)value.mid; - sum = v + 1; - value.mid = (int)sum; - if (sum < v || sum < 1) - { - value.hi = (int)((uint)value.hi + 1); - } - } - } - - // This method does an in-place division of a decimal by a UInt32, returning the remainder. - // Although it does not operate on the sign or scale, this does not result in any - // caveat for the result. It is equivalent to dividing by that number. - private static uint InternalDivRemUInt32(ref decimal value, uint divisor) - { - uint remainder = 0; - ulong n; - if (value.hi != 0) - { - n = ((uint)value.hi); - value.hi = (int)((uint)(n / divisor)); - remainder = (uint)(n % divisor); - } - if (value.mid != 0 || remainder != 0) - { - n = ((ulong)remainder << 32) | (uint)value.mid; - value.mid = (int)((uint)(n / divisor)); - remainder = (uint)(n % divisor); - } - if (value.lo != 0 || remainder != 0) - { - n = ((ulong)remainder << 32) | (uint)value.lo; - value.lo = (int)((uint)(n / divisor)); - remainder = (uint)(n % divisor); - } - return remainder; - } - - // Does an in-place round the specified number of digits, rounding mid-point values - // away from zero - private static void InternalRoundFromZero(ref decimal d, int decimalCount) - { - int scale = (d.flags & ScaleMask) >> ScaleShift; - int scaleDifference = scale - decimalCount; - if (scaleDifference <= 0) - { - return; - } - // Divide the value by 10^scaleDifference - uint lastRemainder; - uint lastDivisor; - do - { - int diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference; - lastDivisor = Powers10[diffChunk]; - lastRemainder = InternalDivRemUInt32(ref d, lastDivisor); - scaleDifference -= diffChunk; - } while (scaleDifference > 0); - - // Round away from zero at the mid point - if (lastRemainder >= (lastDivisor >> 1)) - { - InternalAddUInt32RawUnchecked(ref d, 1); - } - - // the scale becomes the desired decimal count - d.flags = ((decimalCount << ScaleShift) & ScaleMask) | (d.flags & SignMask); - } - - // Returns the larger of two Decimal values. - // - internal static decimal Max(ref decimal d1, ref decimal d2) - { - return FCallCompare(ref d1, ref d2) >= 0 ? d1 : d2; - } - - // Returns the smaller of two Decimal values. - // - internal static decimal Min(ref decimal d1, ref decimal d2) - { - return FCallCompare(ref d1, ref d2) < 0 ? d1 : d2; - } - - public static decimal Remainder(decimal d1, decimal d2) - { - // OleAut doesn't provide a VarDecMod. - - // In the operation x % y the sign of y does not matter. Result will have the sign of x. - d2.flags = (d2.flags & ~SignMask) | (d1.flags & SignMask); - - - // This piece of code is to work around the fact that Dividing a decimal with 28 digits number by decimal which causes - // causes the result to be 28 digits, can cause to be incorrectly rounded up. - // eg. Decimal.MaxValue / 2 * Decimal.MaxValue will overflow since the division by 2 was rounded instead of being truncked. - if (Math.Abs(d1) < Math.Abs(d2)) - { - return d1; - } - d1 -= d2; - - if (d1 == 0) - { - // The sign of D1 will be wrong here. Fall through so that we still get a DivideByZeroException - d1.flags = (d1.flags & ~SignMask) | (d2.flags & SignMask); - } - - // Formula: d1 - (RoundTowardsZero(d1 / d2) * d2) - decimal dividedResult = Truncate(d1 / d2); - decimal multipliedResult = dividedResult * d2; - decimal result = d1 - multipliedResult; - // See if the result has crossed 0 - if ((d1.flags & SignMask) != (result.flags & SignMask)) - { - if (NearNegativeZero <= result && result <= NearPositiveZero) - { - // Certain Remainder operations on decimals with 28 significant digits round - // to [+-]0.0000000000000000000000000001m instead of [+-]0m during the intermediate calculations. - // 'zero' results just need their sign corrected. - result.flags = (result.flags & ~SignMask) | (d1.flags & SignMask); - } - else - { - // If the division rounds up because it runs out of digits, the multiplied result can end up with a larger - // absolute value and the result of the formula crosses 0. To correct it can add the divisor back. - result += d2; - } - } - - return result; - } - - // Multiplies two Decimal values. - // - public static decimal Multiply(decimal d1, decimal d2) - { - FCallMultiply(ref d1, ref d2); - return d1; - } - - // FCallMultiply multiples two decimal values. On return, d1 contains the result - // of the operation. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallMultiply(ref decimal d1, ref decimal d2); - - // Returns the negated value of the given Decimal. If d is non-zero, - // the result is -d. If d is zero, the result is zero. - // - public static decimal Negate(decimal d) - { - return new decimal(d.lo, d.mid, d.hi, d.flags ^ SignMask); - } - - // Rounds a Decimal value to a given number of decimal places. The value - // given by d is rounded to the number of decimal places given by - // decimals. The decimals argument must be an integer between - // 0 and 28 inclusive. - // - // By default a mid-point value is rounded to the nearest even number. If the mode is - // passed in, it can also round away from zero. - - public static decimal Round(decimal d) - { - return Round(d, 0); - } - - public static decimal Round(decimal d, int decimals) - { - FCallRound(ref d, decimals); - return d; - } - - public static decimal Round(decimal d, MidpointRounding mode) - { - return Round(d, 0, mode); - } - - public static decimal Round(decimal d, int decimals, MidpointRounding mode) - { - if ((decimals < 0) || (decimals > 28)) - throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound); - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { - throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); - } - - if (mode == MidpointRounding.ToEven) - { - FCallRound(ref d, decimals); - } - else - { - InternalRoundFromZero(ref d, decimals); - } - return d; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallRound(ref decimal d, int decimals); - - internal static int Sign(ref decimal d) => (d.lo | d.mid | d.hi) == 0 ? 0 : (d.flags >> 31) | 1; - - // Subtracts two Decimal values. - // - public static decimal Subtract(decimal d1, decimal d2) - { - FCallAddSub(ref d1, ref d2, DECIMAL_NEG); - return d1; - } - - // Converts a Decimal to an unsigned byte. The Decimal value is rounded - // towards zero to the nearest integer value, and the result of this - // operation is returned as a byte. - // - public static byte ToByte(decimal value) - { - uint temp; - try - { - temp = ToUInt32(value); - } - catch (OverflowException e) - { - throw new OverflowException(SR.Overflow_Byte, e); - } - if (temp < byte.MinValue || temp > byte.MaxValue) throw new OverflowException(SR.Overflow_Byte); - return (byte)temp; - } - - // Converts a Decimal to a signed byte. The Decimal value is rounded - // towards zero to the nearest integer value, and the result of this - // operation is returned as a byte. - // - [CLSCompliant(false)] - public static sbyte ToSByte(decimal value) - { - int temp; - try - { - temp = ToInt32(value); - } - catch (OverflowException e) - { - throw new OverflowException(SR.Overflow_SByte, e); - } - if (temp < sbyte.MinValue || temp > sbyte.MaxValue) throw new OverflowException(SR.Overflow_SByte); - return (sbyte)temp; - } - - // Converts a Decimal to a short. The Decimal value is - // rounded towards zero to the nearest integer value, and the result of - // this operation is returned as a short. - // - public static short ToInt16(decimal value) - { - int temp; - try - { - temp = ToInt32(value); - } - catch (OverflowException e) - { - throw new OverflowException(SR.Overflow_Int16, e); - } - if (temp < short.MinValue || temp > short.MaxValue) throw new OverflowException(SR.Overflow_Int16); - return (short)temp; - } - - - // Converts a Decimal to a Currency. Since a Currency - // has fewer significant digits than a Decimal, this operation may - // produce round-off errors. - // - internal static Currency ToCurrency(decimal d) - { - Currency result = new Currency(); - FCallToCurrency(ref result, d); - return result; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallToCurrency(ref Currency result, decimal d); - - // Converts a Decimal to a double. Since a double has fewer significant - // digits than a Decimal, this operation may produce round-off errors. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern double ToDouble(decimal d); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern int FCallToInt32(decimal d); - - // Converts a Decimal to an integer. The Decimal value is rounded towards - // zero to the nearest integer value, and the result of this operation is - // returned as an integer. - // - public static int ToInt32(decimal d) - { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0 && d.mid == 0) - { - int i = d.lo; - if (d.flags >= 0) - { - if (i >= 0) return i; - } - else - { - i = -i; - if (i <= 0) return i; - } - } - throw new OverflowException(SR.Overflow_Int32); - } - - // Converts a Decimal to a long. The Decimal value is rounded towards zero - // to the nearest integer value, and the result of this operation is - // returned as a long. - // - public static long ToInt64(decimal d) - { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0) - { - long l = d.lo & 0xFFFFFFFFL | (long)d.mid << 32; - if (d.flags >= 0) - { - if (l >= 0) return l; - } - else - { - l = -l; - if (l <= 0) return l; - } - } - throw new OverflowException(SR.Overflow_Int64); - } - - // Converts a Decimal to an ushort. The Decimal - // value is rounded towards zero to the nearest integer value, and the - // result of this operation is returned as an ushort. - // - [CLSCompliant(false)] - public static ushort ToUInt16(decimal value) - { - uint temp; - try - { - temp = ToUInt32(value); - } - catch (OverflowException e) - { - throw new OverflowException(SR.Overflow_UInt16, e); - } - if (temp < ushort.MinValue || temp > ushort.MaxValue) throw new OverflowException(SR.Overflow_UInt16); - return (ushort)temp; - } - - // Converts a Decimal to an unsigned integer. The Decimal - // value is rounded towards zero to the nearest integer value, and the - // result of this operation is returned as an unsigned integer. - // - [CLSCompliant(false)] - public static uint ToUInt32(decimal d) - { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0 && d.mid == 0) - { - uint i = (uint)d.lo; - if (d.flags >= 0 || i == 0) - return i; - } - throw new OverflowException(SR.Overflow_UInt32); - } - - // Converts a Decimal to an unsigned long. The Decimal - // value is rounded towards zero to the nearest integer value, and the - // result of this operation is returned as a long. - // - [CLSCompliant(false)] - public static ulong ToUInt64(decimal d) - { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0) - { - ulong l = ((ulong)(uint)d.lo) | ((ulong)(uint)d.mid << 32); - if (d.flags >= 0 || l == 0) - return l; - } - throw new OverflowException(SR.Overflow_UInt64); - } - - // Converts a Decimal to a float. Since a float has fewer significant - // digits than a Decimal, this operation may produce round-off errors. - // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public static extern float ToSingle(decimal d); - - // Truncates a Decimal to an integer value. The Decimal argument is rounded - // towards zero to the nearest integer value, corresponding to removing all - // digits after the decimal point. - // - public static decimal Truncate(decimal d) - { - FCallTruncate(ref d); - return d; - } - - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallTruncate(ref decimal d); - - - public static implicit operator decimal(byte value) - { - return new decimal(value); - } - - [CLSCompliant(false)] - public static implicit operator decimal(sbyte value) - { - return new decimal(value); - } - - public static implicit operator decimal(short value) - { - return new decimal(value); - } - - [CLSCompliant(false)] - public static implicit operator decimal(ushort value) - { - return new decimal(value); - } - - public static implicit operator decimal(char value) - { - return new decimal(value); - } - - public static implicit operator decimal(int value) - { - return new decimal(value); - } - - [CLSCompliant(false)] - public static implicit operator decimal(uint value) - { - return new decimal(value); - } - - public static implicit operator decimal(long value) - { - return new decimal(value); - } - - [CLSCompliant(false)] - public static implicit operator decimal(ulong value) - { - return new decimal(value); - } - - - public static explicit operator decimal(float value) - { - return new decimal(value); - } - - public static explicit operator decimal(double value) - { - return new decimal(value); - } - - public static explicit operator byte(decimal value) - { - return ToByte(value); - } - - [CLSCompliant(false)] - public static explicit operator sbyte(decimal value) - { - return ToSByte(value); - } - - public static explicit operator char(decimal value) - { - ushort temp; - try - { - temp = ToUInt16(value); - } - catch (OverflowException e) - { - throw new OverflowException(SR.Overflow_Char, e); - } - return (char)temp; - } - - public static explicit operator short(decimal value) - { - return ToInt16(value); - } - - [CLSCompliant(false)] - public static explicit operator ushort(decimal value) - { - return ToUInt16(value); - } - - public static explicit operator int(decimal value) - { - return ToInt32(value); - } - - [CLSCompliant(false)] - public static explicit operator uint(decimal value) - { - return ToUInt32(value); - } - - public static explicit operator long(decimal value) - { - return ToInt64(value); - } - - [CLSCompliant(false)] - public static explicit operator ulong(decimal value) - { - return ToUInt64(value); - } - - public static explicit operator float(decimal value) - { - return ToSingle(value); - } - - public static explicit operator double(decimal value) - { - return ToDouble(value); - } - - public static decimal operator +(decimal d) - { - return d; - } - - public static decimal operator -(decimal d) - { - return Negate(d); - } - - public static decimal operator ++(decimal d) - { - return Add(d, One); - } - - public static decimal operator --(decimal d) - { - return Subtract(d, One); - } - - public static decimal operator +(decimal d1, decimal d2) - { - FCallAddSub(ref d1, ref d2, DECIMAL_ADD); - return d1; - } - - public static decimal operator -(decimal d1, decimal d2) - { - FCallAddSub(ref d1, ref d2, DECIMAL_NEG); - return d1; - } - - public static decimal operator *(decimal d1, decimal d2) - { - FCallMultiply(ref d1, ref d2); - return d1; - } - - public static decimal operator /(decimal d1, decimal d2) - { - FCallDivide(ref d1, ref d2); - return d1; - } - - public static decimal operator %(decimal d1, decimal d2) - { - return Remainder(d1, d2); - } - - public static bool operator ==(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) == 0; - } - - public static bool operator !=(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) != 0; - } - - public static bool operator <(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) < 0; - } - - public static bool operator <=(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) <= 0; - } - - public static bool operator >(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) > 0; - } - - public static bool operator >=(decimal d1, decimal d2) - { - return FCallCompare(ref d1, ref d2) >= 0; - } - - // - // IConvertible implementation - // - - public TypeCode GetTypeCode() - { - return TypeCode.Decimal; - } - - bool IConvertible.ToBoolean(IFormatProvider provider) - { - return Convert.ToBoolean(this); - } - - - char IConvertible.ToChar(IFormatProvider provider) - { - throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "Char")); - } - - sbyte IConvertible.ToSByte(IFormatProvider provider) - { - return Convert.ToSByte(this); - } - - byte IConvertible.ToByte(IFormatProvider provider) - { - return Convert.ToByte(this); - } - - short IConvertible.ToInt16(IFormatProvider provider) - { - return Convert.ToInt16(this); - } - - ushort IConvertible.ToUInt16(IFormatProvider provider) - { - return Convert.ToUInt16(this); - } - - int IConvertible.ToInt32(IFormatProvider provider) - { - return Convert.ToInt32(this); - } - - uint IConvertible.ToUInt32(IFormatProvider provider) - { - return Convert.ToUInt32(this); - } - - long IConvertible.ToInt64(IFormatProvider provider) - { - return Convert.ToInt64(this); - } - - ulong IConvertible.ToUInt64(IFormatProvider provider) - { - return Convert.ToUInt64(this); - } - - float IConvertible.ToSingle(IFormatProvider provider) - { - return Convert.ToSingle(this); - } - - double IConvertible.ToDouble(IFormatProvider provider) - { - return Convert.ToDouble(this); - } - - decimal IConvertible.ToDecimal(IFormatProvider provider) - { - return this; - } - - DateTime IConvertible.ToDateTime(IFormatProvider provider) - { - throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "DateTime")); - } - - object IConvertible.ToType(Type type, IFormatProvider provider) - { - return Convert.DefaultToType((IConvertible)this, type, provider); - } - } -} diff --git a/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs index e14db02762..30e70d0bd7 100644 --- a/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs +++ b/src/System.Private.CoreLib/src/System/Number.CoreCLR.cs @@ -13,8 +13,5 @@ namespace System [MethodImpl(MethodImplOptions.InternalCall)] public static extern double NumberToDouble(ref NumberBuffer number); - - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern bool NumberBufferToDecimal(ref NumberBuffer number, ref decimal value); } } diff --git a/src/System.Private.CoreLib/src/System/StubHelpers.cs b/src/System.Private.CoreLib/src/System/StubHelpers.cs index 069f752d4a..6d9e7a8cea 100644 --- a/src/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/System.Private.CoreLib/src/System/StubHelpers.cs @@ -1694,9 +1694,6 @@ namespace System.StubHelpers [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern unsafe int strlen(sbyte* ptr); - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern void DecimalCanonicalizeInternal(ref decimal dec); - [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern unsafe void FmtClassUpdateNativeInternal(object obj, byte* pNative, ref CleanupWorkList pCleanupWorkList); [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/classlibnative/bcltype/CMakeLists.txt b/src/classlibnative/bcltype/CMakeLists.txt index 62cf9682c3..41cf3cffb5 100644 --- a/src/classlibnative/bcltype/CMakeLists.txt +++ b/src/classlibnative/bcltype/CMakeLists.txt @@ -8,8 +8,6 @@ set(BCLTYPE_SOURCES arraynative.cpp arrayhelpers.cpp bignum.cpp - currency.cpp - decimal.cpp diyfp.cpp grisu3.cpp number.cpp diff --git a/src/classlibnative/bcltype/currency.cpp b/src/classlibnative/bcltype/currency.cpp deleted file mode 100644 index 4506105e25..0000000000 --- a/src/classlibnative/bcltype/currency.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// 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. -// -// File: Currency.cpp -// - -// - -#include "common.h" -#include "object.h" -#include "excep.h" -#include "frames.h" -#include "vars.hpp" -#include "currency.h" -#include "string.h" - - -FCIMPL2_IV(void, COMCurrency::DoToDecimal, DECIMAL * result, CY c) -{ - FCALL_CONTRACT; - - // GC could only happen when exception is thrown, no need to protect result - HELPER_METHOD_FRAME_BEGIN_0(); - - _ASSERTE(result); - HRESULT hr = VarDecFromCy(c, result); - if (FAILED(hr)) - { - // Didn't expect to get here. Update code for this HR. - _ASSERTE(S_OK == hr); - COMPlusThrowHR(hr); - } - - if (FAILED(DecimalCanonicalize(result))) - COMPlusThrow(kOverflowException, W("Overflow_Currency")); - - result->wReserved = 0; - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND diff --git a/src/classlibnative/bcltype/currency.h b/src/classlibnative/bcltype/currency.h deleted file mode 100644 index a1ba64e463..0000000000 --- a/src/classlibnative/bcltype/currency.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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. -// -// File: Currency.h -// - -// - -#ifndef _CURRENCY_H_ -#define _CURRENCY_H_ - -#include -#include - -class COMCurrency -{ -public: - static FCDECL2_IV(void, DoToDecimal, DECIMAL * result, CY c); -}; - -#include - -#endif // _CURRENCY_H_ diff --git a/src/classlibnative/bcltype/decimal.cpp b/src/classlibnative/bcltype/decimal.cpp deleted file mode 100644 index 729b19f334..0000000000 --- a/src/classlibnative/bcltype/decimal.cpp +++ /dev/null @@ -1,2534 +0,0 @@ -// 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. -// -// File: decimal.cpp -// - -// - -#include "common.h" -#include "object.h" -#include "excep.h" -#include "frames.h" -#include "vars.hpp" -#include "decimal.h" -#include "string.h" - -LONG g_OLEAUT32_Loaded = 0; - -unsigned int DecDivMod1E9(DECIMAL* value); -void DecMul10(DECIMAL* value); -void DecAddInt32(DECIMAL* value, unsigned int i); - -#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); DECIMAL_LO64_SET(dest, DECIMAL_LO64_GET(src));} - -FCIMPL2_IV(void, COMDecimal::InitSingle, DECIMAL *_this, float value) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - _ASSERTE(_this != NULL); - HRESULT hr = VarDecFromR4(value, _this); - if (FAILED(hr)) - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - _this->wReserved = 0; -} -FCIMPLEND - -FCIMPL2_IV(void, COMDecimal::InitDouble, DECIMAL *_this, double value) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - _ASSERTE(_this != NULL); - HRESULT hr = VarDecFromR8(value, _this); - if (FAILED(hr)) - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - _this->wReserved = 0; -} -FCIMPLEND - - -#ifdef _MSC_VER -// C4702: unreachable code on IA64 retail -#pragma warning(push) -#pragma warning(disable:4702) -#endif -FCIMPL2(INT32, COMDecimal::DoCompare, DECIMAL * d1, DECIMAL * d2) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - HRESULT hr = VarDecCmp(d1, d2); - if (FAILED(hr) || (int)hr == VARCMP_NULL) { - _ASSERTE(!"VarDecCmp failed in Decimal::Compare"); - FCThrowRes(kOverflowException, W("Overflow_Decimal")); - } - - INT32 retVal = ((int)hr) - 1; - FC_GC_POLL_RET (); - return retVal; -} -FCIMPLEND -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -FCIMPL1(void, COMDecimal::DoFloor, DECIMAL * d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL decRes; - HRESULT hr; - hr = VarDecInt(d, &decRes); - - // VarDecInt can't overflow, as of source for OleAut32 build 4265. - // It only returns NOERROR - _ASSERTE(hr==NOERROR); - - // copy decRes into d - COPYDEC(*d, decRes) - d->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - -FCIMPL3(void, COMDecimal::DoMultiply, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL decRes; - - // GC is only triggered for throwing, no need to protect result - HRESULT hr = VarDecMul(d1, d2, &decRes); - if (FAILED(hr)) { - *overflowed = true; - FC_GC_POLL(); - return; - } - - // copy decRes into d1 - COPYDEC(*d1, decRes) - d1->wReserved = 0; - *overflowed = false; - FC_GC_POLL(); -} -FCIMPLEND - - -FCIMPL2(void, COMDecimal::DoMultiplyThrow, DECIMAL * d1, DECIMAL * d2) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL decRes; - - // GC is only triggered for throwing, no need to protect result - HRESULT hr = VarDecMul(d1, d2, &decRes); - if (FAILED(hr)) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - - // copy decRes into d1 - COPYDEC(*d1, decRes) - d1->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - -FCIMPL2(void, COMDecimal::DoRound, DECIMAL * d, INT32 decimals) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL decRes; - - // GC is only triggered for throwing, no need to protect result - if (decimals < 0 || decimals > 28) - FCThrowArgumentOutOfRangeVoid(W("decimals"), W("ArgumentOutOfRange_DecimalRound")); - HRESULT hr = VarDecRound(d, decimals, &decRes); - if (FAILED(hr)) - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - - // copy decRes into d - COPYDEC(*d, decRes) - d->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - -FCIMPL2_IV(void, COMDecimal::DoToCurrency, CY * result, DECIMAL d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - // GC is only triggered for throwing, no need to protect result - HRESULT hr = VarCyFromDec(&d, result); - if (FAILED(hr)) { - _ASSERTE(hr != E_INVALIDARG); - FCThrowResVoid(kOverflowException, W("Overflow_Currency")); - } -} -FCIMPLEND - -FCIMPL1(double, COMDecimal::ToDouble, FC_DECIMAL d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - double result = 0.0; - // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0 - VarR8FromDec(&d, &result); - return result; -} -FCIMPLEND - -FCIMPL1(INT32, COMDecimal::ToInt32, FC_DECIMAL d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL result; - HRESULT hr = VarDecRound(&d, 0, &result); - if (FAILED(hr)) - FCThrowRes(kOverflowException, W("Overflow_Decimal")); - - result.wReserved = 0; - - if( DECIMAL_SCALE(result) != 0) { - d = result; - VarDecFix(&d, &result); - } - - if (DECIMAL_HI32(result) == 0 && DECIMAL_MID32(result) == 0) { - INT32 i = DECIMAL_LO32(result); - if ((INT16)DECIMAL_SIGNSCALE(result) >= 0) { - if (i >= 0) return i; - } - else { - // Int32.MinValue is represented as sign being negative - // and Lo32 being 0x80000000 (-ve number). Return that as is without - // reversing the sign of the number. - if(i == 0x80000000) return i; - i = -i; - if (i <= 0) return i; - } - } - FCThrowRes(kOverflowException, W("Overflow_Int32")); -} -FCIMPLEND - -FCIMPL1(float, COMDecimal::ToSingle, FC_DECIMAL d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - float result = 0.0f; - // Note: this can fail if the input is an invalid decimal, but for compatibility we should return 0 - VarR4FromDec(&d, &result); - return result; -} -FCIMPLEND - -FCIMPL1(void, COMDecimal::DoTruncate, DECIMAL * d) -{ - FCALL_CONTRACT; - - ENSURE_OLEAUT32_LOADED(); - - DECIMAL decRes; - - VarDecFix(d, &decRes); - - // copy decRes into d - COPYDEC(*d, decRes) - d->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - -int COMDecimal::NumberToDecimal(NUMBER* number, DECIMAL* value) -{ - WRAPPER_NO_CONTRACT - _ASSERTE(number != NULL); - _ASSERTE(value != NULL); - - DECIMAL d; - d.wReserved = 0; - DECIMAL_SIGNSCALE(d) = 0; - DECIMAL_HI32(d) = 0; - DECIMAL_LO32(d) = 0; - DECIMAL_MID32(d) = 0; - wchar_t* p = number->digits; - _ASSERT(p != NULL); - int e = number->scale; - if (!*p) { - // 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 - if (e > 0) { - e = 0; - } - } else { - if (e > DECIMAL_PRECISION) return 0; - while ((e > 0 || (*p && e > -28)) && - (DECIMAL_HI32(d) < 0x19999999 || (DECIMAL_HI32(d) == 0x19999999 && - (DECIMAL_MID32(d) < 0x99999999 || (DECIMAL_MID32(d) == 0x99999999 && - (DECIMAL_LO32(d) < 0x99999999 || (DECIMAL_LO32(d) == 0x99999999 && *p <= '5'))))))) { - DecMul10(&d); - if (*p) DecAddInt32(&d, *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 roundinp 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) { - DecAddInt32(&d, 1); - if ((DECIMAL_HI32(d) | DECIMAL_MID32(d) | DECIMAL_LO32(d)) == 0) { - DECIMAL_HI32(d) = 0x19999999; - DECIMAL_MID32(d) = 0x99999999; - DECIMAL_LO32(d) = 0x9999999A; - e++; - } - } - } - } - if (e > 0) return 0; - 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. - DECIMAL_SIGNSCALE(d) = 0; - DECIMAL_HI32(d) = 0; - DECIMAL_LO32(d) = 0; - DECIMAL_MID32(d) = 0; - DECIMAL_SCALE(d) = (DECIMAL_PRECISION - 1); - } - else - { - DECIMAL_SCALE(d) = static_cast(-e); - } - DECIMAL_SIGN(d) = number->sign? DECIMAL_NEG: 0; - *value = d; - return 1; -} - -#if defined(_TARGET_X86_) - -#pragma warning(disable:4035) - -unsigned int DecDivMod1E9(DECIMAL* value) -{ - LIMITED_METHOD_CONTRACT - - _asm { - mov ebx,value - mov ecx,1000000000 - xor edx,edx - mov eax,[ebx+4] - div ecx - mov [ebx+4],eax - mov eax,[ebx+12] - div ecx - mov [ebx+12],eax - mov eax,[ebx+8] - div ecx - mov [ebx+8],eax - mov eax,edx - } -} - -void DecMul10(DECIMAL* value) -{ - LIMITED_METHOD_CONTRACT - - _asm { - mov ebx,value - mov eax,[ebx+8] - mov edx,[ebx+12] - mov ecx,[ebx+4] - shl eax,1 - rcl edx,1 - rcl ecx,1 - shl eax,1 - rcl edx,1 - rcl ecx,1 - add eax,[ebx+8] - adc edx,[ebx+12] - adc ecx,[ebx+4] - shl eax,1 - rcl edx,1 - rcl ecx,1 - mov [ebx+8],eax - mov [ebx+12],edx - mov [ebx+4],ecx - } -} - -void DecAddInt32(DECIMAL* value, unsigned int i) -{ - LIMITED_METHOD_CONTRACT - - _asm { - mov edx,value - mov eax,i - add dword ptr [edx+8],eax - adc dword ptr [edx+12],0 - adc dword ptr [edx+4],0 - } -} - -#pragma warning(default:4035) - -#else // !(defined(_TARGET_X86_) - -unsigned int D32DivMod1E9(unsigned int hi32, ULONG* lo32) -{ - LIMITED_METHOD_CONTRACT - _ASSERTE(lo32 != NULL); - - unsigned __int64 n = (unsigned __int64)hi32 << 32 | *lo32; - *lo32 = (unsigned int)(n / 1000000000); - return (unsigned int)(n % 1000000000); -} - -unsigned int DecDivMod1E9(DECIMAL* value) -{ - WRAPPER_NO_CONTRACT - _ASSERTE(value != NULL); - - return D32DivMod1E9(D32DivMod1E9(D32DivMod1E9(0, - &DECIMAL_HI32(*value)), &DECIMAL_MID32(*value)), &DECIMAL_LO32(*value)); -} - -void DecShiftLeft(DECIMAL* value) -{ - LIMITED_METHOD_CONTRACT - _ASSERTE(value != NULL); - - unsigned int c0 = DECIMAL_LO32(*value) & 0x80000000? 1: 0; - unsigned int c1 = DECIMAL_MID32(*value) & 0x80000000? 1: 0; - DECIMAL_LO32(*value) <<= 1; - DECIMAL_MID32(*value) = DECIMAL_MID32(*value) << 1 | c0; - DECIMAL_HI32(*value) = DECIMAL_HI32(*value) << 1 | c1; -} - -int D32AddCarry(ULONG* value, unsigned int i) -{ - LIMITED_METHOD_CONTRACT - _ASSERTE(value != NULL); - - unsigned int v = *value; - unsigned int sum = v + i; - *value = sum; - return sum < v || sum < i? 1: 0; -} - -void DecAdd(DECIMAL* value, DECIMAL* d) -{ - WRAPPER_NO_CONTRACT - _ASSERTE(value != NULL && d != NULL); - - if (D32AddCarry(&DECIMAL_LO32(*value), DECIMAL_LO32(*d))) { - if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { - D32AddCarry(&DECIMAL_HI32(*value), 1); - } - } - if (D32AddCarry(&DECIMAL_MID32(*value), DECIMAL_MID32(*d))) { - D32AddCarry(&DECIMAL_HI32(*value), 1); - } - D32AddCarry(&DECIMAL_HI32(*value), DECIMAL_HI32(*d)); -} - -void DecMul10(DECIMAL* value) -{ - WRAPPER_NO_CONTRACT - _ASSERTE(value != NULL); - - DECIMAL d = *value; - DecShiftLeft(value); - DecShiftLeft(value); - DecAdd(value, &d); - DecShiftLeft(value); -} - -void DecAddInt32(DECIMAL* value, unsigned int i) -{ - WRAPPER_NO_CONTRACT - _ASSERTE(value != NULL); - - if (D32AddCarry(&DECIMAL_LO32(*value), i)) { - if (D32AddCarry(&DECIMAL_MID32(*value), 1)) { - D32AddCarry(&DECIMAL_HI32(*value), 1); - } - } -} - -#endif - -/*** -* -* Decimal Code ported from OleAut32 -* -***********************************************************************/ - -// This OleAut code is only used on 64-bit and rotor platforms. It is desiriable to continue -// to call the OleAut routines in X86 because of the performance of the hand-tuned assembly -// code and because there are currently no inconsistencies in behavior accross platforms. - -#ifndef UInt32x32To64 -#define UInt32x32To64(a, b) ((DWORDLONG)((DWORD)(a)) * (DWORDLONG)((DWORD)(b))) -#endif - -typedef union { - DWORDLONG int64; - struct { -#if BIGENDIAN - ULONG Hi; - ULONG Lo; -#else - ULONG Lo; - ULONG Hi; -#endif - } u; -} SPLIT64; - -#define OVFL_MAX_1_HI 429496729 -#define DEC_SCALE_MAX 28 -#define POWER10_MAX 9 - -#define OVFL_MAX_9_HI 4u -#define OVFL_MAX_9_MID 1266874889u -#define OVFL_MAX_9_LO 3047500985u - -#define OVFL_MAX_5_HI 42949 - - -const ULONG rgulPower10[POWER10_MAX+1] = {1, 10, 100, 1000, 10000, 100000, 1000000, - 10000000, 100000000, 1000000000}; - -struct DECOVFL -{ - ULONG Hi; - ULONG Mid; - ULONG Lo; -}; - -const DECOVFL PowerOvfl[] = { -// This is a table of the largest values that can be in the upper two -// ULONGs 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 <= 9. For the lower word, this is the -// remaining fraction part * 2^32. 2^32 = 4294967296. -// - { 429496729u, 2576980377u, 2576980377u }, // 10^1 remainder 0.6 - { 42949672u, 4123168604u, 687194767u }, // 10^2 remainder 0.16 - { 4294967u, 1271310319u, 2645699854u }, // 10^3 remainder 0.616 - { 429496u, 3133608139u, 694066715u }, // 10^4 remainder 0.1616 - { 42949u, 2890341191u, 2216890319u }, // 10^5 remainder 0.51616 - { 4294u, 4154504685u, 2369172679u }, // 10^6 remainder 0.551616 - { 429u, 2133437386u, 4102387834u }, // 10^7 remainder 0.9551616 - { 42u, 4078814305u, 410238783u }, // 10^8 remainder 0.09991616 - { 4u, 1266874889u, 3047500985u }, // 10^9 remainder 0.709551616 -}; - - -/*** -* IncreaseScale -* -* Entry: -* rgulNum - Pointer to 96-bit number as array of ULONGs, least-sig first -* ulPwr - Scale factor to multiply by -* -* Purpose: -* 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. -* -* Exit: -* Returns highest 32 bits of product. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG IncreaseScale(ULONG *rgulNum, ULONG ulPwr) -{ - LIMITED_METHOD_CONTRACT; - - SPLIT64 sdlTmp; - - sdlTmp.int64 = UInt32x32To64(rgulNum[0], ulPwr); - rgulNum[0] = sdlTmp.u.Lo; - sdlTmp.int64 = UInt32x32To64(rgulNum[1], ulPwr) + sdlTmp.u.Hi; - rgulNum[1] = sdlTmp.u.Lo; - sdlTmp.int64 = UInt32x32To64(rgulNum[2], ulPwr) + sdlTmp.u.Hi; - rgulNum[2] = sdlTmp.u.Lo; - return sdlTmp.u.Hi; -} - - -/*** -* SearchScale -* -* Entry: -* ulResHi - Top ULONG of quotient -* ulResMid - Middle ULONG of quotient -* ulResLo - Bottom ULONG of quotient -* iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX -* -* Purpose: -* Determine the max power of 10, <= 9, that the quotient can be scaled -* up by and still fit in 96 bits. -* -* Exit: -* Returns power of 10 to scale by, -1 if overflow error. -* -***********************************************************************/ - -int SearchScale(ULONG ulResHi, ULONG ulResMid, ULONG ulResLo, int iScale) -{ - WRAPPER_NO_CONTRACT; - - int iCurScale; - - // Quick check to stop us from trying to scale any more. - // - if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX) { - iCurScale = 0; - goto HaveScale; - } - - if (iScale > 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. - // - iCurScale = DEC_SCALE_MAX - iScale; - if (ulResHi < PowerOvfl[iCurScale - 1].Hi) - goto HaveScale; - - if (ulResHi == PowerOvfl[iCurScale - 1].Hi) { - UpperEq: - if (ulResMid > PowerOvfl[iCurScale - 1].Mid || - (ulResMid == PowerOvfl[iCurScale - 1].Mid && ulResLo > PowerOvfl[iCurScale - 1].Lo)) { - iCurScale--; - } - goto HaveScale; - } - } - else if (ulResHi < OVFL_MAX_9_HI || (ulResHi == OVFL_MAX_9_HI && - ulResMid < OVFL_MAX_9_MID) || (ulResHi == OVFL_MAX_9_HI && ulResMid == OVFL_MAX_9_MID && ulResLo <= OVFL_MAX_9_LO)) - return 9; - - // Search for a power to scale by < 9. Do a binary search - // on PowerOvfl[]. - // - iCurScale = 5; - if (ulResHi < OVFL_MAX_5_HI) - iCurScale = 7; - else if (ulResHi > OVFL_MAX_5_HI) - iCurScale = 3; - else - goto UpperEq; - - // iCurScale is 3 or 7. - // - if (ulResHi < PowerOvfl[iCurScale - 1].Hi) - iCurScale++; - else if (ulResHi > PowerOvfl[iCurScale - 1].Hi) - iCurScale--; - else - goto UpperEq; - - // iCurScale is 2, 4, 6, or 8. - // - // 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 (ulResHi > PowerOvfl[iCurScale - 1].Hi) - iCurScale--; - - if (ulResHi == PowerOvfl[iCurScale - 1].Hi) - goto UpperEq; - -HaveScale: - // iCurScale = largest power of 10 we can scale by without overflow, - // iCurScale < 9. See if this is enough to make scale factor - // positive if it isn't already. - // - if (iCurScale + iScale < 0) - iCurScale = -1; - - return iCurScale; -} - -//*********************************************************************** -// -// Arithmetic Inlines -// - -#define Div64by32(num, den) ((ULONG)((DWORDLONG)(num) / (ULONG)(den))) -#define Mod64by32(num, den) ((ULONG)((DWORDLONG)(num) % (ULONG)(den))) - -inline DWORDLONG DivMod64by32(DWORDLONG num, ULONG den) -{ - WRAPPER_NO_CONTRACT; - - SPLIT64 sdl; - - sdl.u.Lo = Div64by32(num, den); - sdl.u.Hi = Mod64by32(num, den); - return sdl.int64; -} - -/*** -* Div128By96 -* -* Entry: -* rgulNum - Pointer to 128-bit dividend as array of ULONGs, least-sig first -* rgulDen - Pointer to 96-bit divisor. -* -* Purpose: -* Do partial divide, yielding 32-bit result and 96-bit remainder. -* Top divisor ULONG must be larger than top dividend ULONG. 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). -* -* Exit: -* Remainder overwrites lower 96-bits of dividend. -* Returns quotient. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG Div128By96(ULONG *rgulNum, ULONG *rgulDen) -{ - LIMITED_METHOD_CONTRACT; - - SPLIT64 sdlQuo; - SPLIT64 sdlNum; - SPLIT64 sdlProd1; - SPLIT64 sdlProd2; - - sdlNum.u.Lo = rgulNum[0]; - sdlNum.u.Hi = rgulNum[1]; - - if (rgulNum[3] == 0 && rgulNum[2] < rgulDen[2]) - // Result is zero. Entire dividend is remainder. - // - return 0; - - // DivMod64by32 returns quotient in Lo, remainder in Hi. - // - sdlQuo.u.Lo = rgulNum[2]; - sdlQuo.u.Hi = rgulNum[3]; - sdlQuo.int64 = DivMod64by32(sdlQuo.int64, rgulDen[2]); - - // Compute full remainder, rem = dividend - (quo * divisor). - // - sdlProd1.int64 = UInt32x32To64(sdlQuo.u.Lo, rgulDen[0]); // quo * lo divisor - sdlProd2.int64 = UInt32x32To64(sdlQuo.u.Lo, rgulDen[1]); // quo * mid divisor - sdlProd2.int64 += sdlProd1.u.Hi; - sdlProd1.u.Hi = sdlProd2.u.Lo; - - sdlNum.int64 -= sdlProd1.int64; - rgulNum[2] = sdlQuo.u.Hi - sdlProd2.u.Hi; // sdlQuo.Hi is remainder - - // Propagate carries - // - if (sdlNum.int64 > ~sdlProd1.int64) { - rgulNum[2]--; - if (rgulNum[2] >= ~sdlProd2.u.Hi) - goto NegRem; - } - else if (rgulNum[2] > ~sdlProd2.u.Hi) { -NegRem: - // Remainder went negative. Add divisor back in until it's positive, - // a max of 2 times. - // - sdlProd1.u.Lo = rgulDen[0]; - sdlProd1.u.Hi = rgulDen[1]; - - for (;;) { - sdlQuo.u.Lo--; - sdlNum.int64 += sdlProd1.int64; - rgulNum[2] += rgulDen[2]; - - if (sdlNum.int64 < sdlProd1.int64) { - // Detected carry. Check for carry out of top - // before adding it in. - // - if (rgulNum[2]++ < rgulDen[2]) - break; - } - if (rgulNum[2] < rgulDen[2]) - break; // detected carry - } - } - - rgulNum[0] = sdlNum.u.Lo; - rgulNum[1] = sdlNum.u.Hi; - return sdlQuo.u.Lo; -} - - - -/*** -* Div96By32 -* -* Entry: -* rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first -* ulDen - 32-bit divisor. -* -* Purpose: -* Do full divide, yielding 96-bit result and 32-bit remainder. -* -* Exit: -* Quotient overwrites dividend. -* Returns remainder. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG Div96By32(ULONG *rgulNum, ULONG ulDen) -{ - LIMITED_METHOD_CONTRACT; - - SPLIT64 sdlTmp; - - sdlTmp.u.Hi = 0; - - if (rgulNum[2] != 0) - goto Div3Word; - - if (rgulNum[1] >= ulDen) - goto Div2Word; - - sdlTmp.u.Hi = rgulNum[1]; - rgulNum[1] = 0; - goto Div1Word; - -Div3Word: - sdlTmp.u.Lo = rgulNum[2]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); - rgulNum[2] = sdlTmp.u.Lo; -Div2Word: - sdlTmp.u.Lo = rgulNum[1]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); - rgulNum[1] = sdlTmp.u.Lo; -Div1Word: - sdlTmp.u.Lo = rgulNum[0]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); - rgulNum[0] = sdlTmp.u.Lo; - return sdlTmp.u.Hi; -} - - -/*** -* Div96By64 -* -* Entry: -* rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first -* sdlDen - 64-bit divisor. -* -* Purpose: -* Do partial divide, yielding 32-bit result and 64-bit remainder. -* Divisor must be larger than upper 64 bits of dividend. -* -* Exit: -* Remainder overwrites lower 64-bits of dividend. -* Returns quotient. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG Div96By64(ULONG *rgulNum, SPLIT64 sdlDen) -{ - LIMITED_METHOD_CONTRACT; - - SPLIT64 sdlQuo; - SPLIT64 sdlNum; - SPLIT64 sdlProd; - - sdlNum.u.Lo = rgulNum[0]; - - if (rgulNum[2] >= sdlDen.u.Hi) { - // Divide would overflow. Assume a quotient of 2^32, and set - // up remainder accordingly. Then jump to loop which reduces - // the quotient. - // - sdlNum.u.Hi = rgulNum[1] - sdlDen.u.Lo; - sdlQuo.u.Lo = 0; - goto NegRem; - } - - // Hardware divide won't overflow - // - if (rgulNum[2] == 0 && rgulNum[1] < sdlDen.u.Hi) - // Result is zero. Entire dividend is remainder. - // - return 0; - - // DivMod64by32 returns quotient in Lo, remainder in Hi. - // - sdlQuo.u.Lo = rgulNum[1]; - sdlQuo.u.Hi = rgulNum[2]; - sdlQuo.int64 = DivMod64by32(sdlQuo.int64, sdlDen.u.Hi); - sdlNum.u.Hi = sdlQuo.u.Hi; // remainder - - // Compute full remainder, rem = dividend - (quo * divisor). - // - sdlProd.int64 = UInt32x32To64(sdlQuo.u.Lo, sdlDen.u.Lo); // quo * lo divisor - sdlNum.int64 -= sdlProd.int64; - - if (sdlNum.int64 > ~sdlProd.int64) { -NegRem: - // Remainder went negative. Add divisor back in until it's positive, - // a max of 2 times. - // - do { - sdlQuo.u.Lo--; - sdlNum.int64 += sdlDen.int64; - }while (sdlNum.int64 >= sdlDen.int64); - } - - rgulNum[0] = sdlNum.u.Lo; - rgulNum[1] = sdlNum.u.Hi; - return sdlQuo.u.Lo; -} - -// Add a 32 bit unsigned long to an array of 3 unsigned longs representing a 96 integer -// Returns FALSE if there is an overflow -BOOL Add32To96(ULONG *rgulNum, ULONG ulValue) { - rgulNum[0] += ulValue; - if (rgulNum[0] < ulValue) { - if (++rgulNum[1] == 0) { - if (++rgulNum[2] == 0) { - return FALSE; - } - } - } - return TRUE; -} - -// 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, -void OverflowUnscale(ULONG *rgulQuo, BOOL fRemainder) { - LIMITED_METHOD_CONTRACT; - - SPLIT64 sdlTmp; - - // We have overflown, so load the high bit with a one. - sdlTmp.u.Hi = 1u; - sdlTmp.u.Lo = rgulQuo[2]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); - rgulQuo[2] = sdlTmp.u.Lo; - sdlTmp.u.Lo = rgulQuo[1]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); - rgulQuo[1] = sdlTmp.u.Lo; - sdlTmp.u.Lo = rgulQuo[0]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10u); - rgulQuo[0] = sdlTmp.u.Lo; - // 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 ((sdlTmp.u.Hi > 5) || ((sdlTmp.u.Hi == 5) && ( fRemainder || (rgulQuo[0] & 1)))) { - Add32To96(rgulQuo, 1u); - } -} - - - -//********************************************************************** -// -// VarDecDiv - Decimal Divide -// -//********************************************************************** - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable:21000) // Suppress PREFast warning about overly large function -#endif - -FCIMPL2(void, COMDecimal::DoDivideThrow, DECIMAL * pdecL, DECIMAL * pdecR) -{ - FCALL_CONTRACT; - - ULONG rgulQuo[3]; - ULONG rgulQuoSave[3]; - ULONG rgulRem[4]; - ULONG rgulDivisor[3]; - ULONG ulPwr; - ULONG ulTmp; - ULONG ulTmp1; - SPLIT64 sdlTmp; - SPLIT64 sdlDivisor; - int iScale; - int iCurScale; - BOOL fUnscale; - - iScale = DECIMAL_SCALE(*pdecL) - DECIMAL_SCALE(*pdecR); - fUnscale = FALSE; - rgulDivisor[0] = DECIMAL_LO32(*pdecR); - rgulDivisor[1] = DECIMAL_MID32(*pdecR); - rgulDivisor[2] = DECIMAL_HI32(*pdecR); - - if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) { - // Divisor is only 32 bits. Easy divide. - // - if (rgulDivisor[0] == 0) - FCThrowVoid(kDivideByZeroException); - - rgulQuo[0] = DECIMAL_LO32(*pdecL); - rgulQuo[1] = DECIMAL_MID32(*pdecL); - rgulQuo[2] = DECIMAL_HI32(*pdecL); - rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]); - - for (;;) { - if (rgulRem[0] == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale; - } - break; - } - // We need to unscale if and only if we have a non-zero remainder - fUnscale = 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 rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9 - // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and - // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since - // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888 - // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is - // assured not to overflow. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - ulTmp = rgulRem[0] << 1; - if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] && - (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) { -RoundUp: - if (!Add32To96(rgulQuo, 1)) { - if (iScale == 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - iScale--; - OverflowUnscale(rgulQuo, TRUE); - break; - } - } - break; - } - - if (iCurScale < 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - -HaveScale: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - - - sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]); - rgulRem[0] = sdlTmp.u.Hi; - - if (!Add32To96(rgulQuo, sdlTmp.u.Lo)) { - if (iScale == 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 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 ULONG 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. - // - if (rgulDivisor[2] == 0) - ulTmp = rgulDivisor[1]; - else - ulTmp = rgulDivisor[2]; - - iCurScale = 0; - if (!(ulTmp & 0xFFFF0000)) { - iCurScale += 16; - ulTmp <<= 16; - } - if (!(ulTmp & 0xFF000000)) { - iCurScale += 8; - ulTmp <<= 8; - } - if (!(ulTmp & 0xF0000000)) { - iCurScale += 4; - ulTmp <<= 4; - } - if (!(ulTmp & 0xC0000000)) { - iCurScale += 2; - ulTmp <<= 2; - } - if (!(ulTmp & 0x80000000)) { - iCurScale++; - ulTmp <<= 1; - } - - // Shift both dividend and divisor left by iCurScale. - // - sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale; - rgulRem[0] = sdlTmp.u.Lo; - rgulRem[1] = sdlTmp.u.Hi; - sdlTmp.u.Lo = DECIMAL_MID32(*pdecL); - sdlTmp.u.Hi = DECIMAL_HI32(*pdecL); - sdlTmp.int64 <<= iCurScale; - rgulRem[2] = sdlTmp.u.Hi; - rgulRem[3] = (DECIMAL_HI32(*pdecL) >> (31 - iCurScale)) >> 1; - - sdlDivisor.u.Lo = rgulDivisor[0]; - sdlDivisor.u.Hi = rgulDivisor[1]; - sdlDivisor.int64 <<= iCurScale; - - if (rgulDivisor[2] == 0) { - // Have a 64-bit divisor in sdlDivisor. The remainder - // (currently 96 bits spread over 4 ULONGs) will be < divisor. - // - sdlTmp.u.Lo = rgulRem[2]; - sdlTmp.u.Hi = rgulRem[3]; - - rgulQuo[2] = 0; - rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor); - rgulQuo[0] = Div96By64(rgulRem, sdlDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale64; - } - break; - } - - // We need to unscale if and only if we have a non-zero remainder - fUnscale = TRUE; - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - sdlTmp.u.Lo = rgulRem[0]; - sdlTmp.u.Hi = rgulRem[1]; - if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || - (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1))) - goto RoundUp; - break; - } - - if (iCurScale < 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - -HaveScale64: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - - - rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 - IncreaseScale(rgulRem, ulPwr); - ulTmp = Div96By64(rgulRem, sdlDivisor); - if (!Add32To96(rgulQuo, ulTmp)) { - if (iScale == 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0)); - break; - } - - } // for (;;) - } - else { - // Have a 96-bit divisor in rgulDivisor[]. - // - // Start by finishing the shift left by iCurScale. - // - sdlTmp.u.Lo = rgulDivisor[1]; - sdlTmp.u.Hi = rgulDivisor[2]; - sdlTmp.int64 <<= iCurScale; - rgulDivisor[0] = sdlDivisor.u.Lo; - rgulDivisor[1] = sdlDivisor.u.Hi; - rgulDivisor[2] = sdlTmp.u.Hi; - - // The remainder (currently 96 bits spread over 4 ULONGs) - // will be < divisor. - // - rgulQuo[2] = 0; - rgulQuo[1] = 0; - rgulQuo[0] = Div128By96(rgulRem, rgulDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale96; - } - break; - } - - // We need to unscale if and only if we have a non-zero remainder - fUnscale = TRUE; - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - if (rgulRem[2] >= 0x80000000) - goto RoundUp; - - ulTmp = rgulRem[0] > 0x80000000; - ulTmp1 = rgulRem[1] > 0x80000000; - rgulRem[0] <<= 1; - rgulRem[1] = (rgulRem[1] << 1) + ulTmp; - rgulRem[2] = (rgulRem[2] << 1) + ulTmp1; - - if (rgulRem[2] > rgulDivisor[2] || (rgulRem[2] == rgulDivisor[2] && - (rgulRem[1] > rgulDivisor[1] || (rgulRem[1] == rgulDivisor[1] && - (rgulRem[0] > rgulDivisor[0] || (rgulRem[0] == rgulDivisor[0] && - (rgulQuo[0] & 1))))))) - goto RoundUp; - break; - } - - if (iCurScale < 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - -HaveScale96: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - - rgulRem[3] = IncreaseScale(rgulRem, ulPwr); - ulTmp = Div128By96(rgulRem, rgulDivisor); - if (!Add32To96(rgulQuo, ulTmp)) { - if (iScale == 0) { - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0)); - break; - } - - } // for (;;) - } - } - - // We need to unscale if and only if we have a non-zero remainder - if (fUnscale) { - // Try extracting any extra powers of 10 we may have - // added. We do this 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. If the remainder is not zero, then we restore - // the previous value. - // - // 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. - // - while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100000000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 8; - } - else - break; - } - - if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 4; - } - } - - if ((rgulQuo[0] & 3) == 0 && iScale >= 2) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 2; - } - } - - if ((rgulQuo[0] & 1) == 0 && iScale >= 1) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 1; - } - } - } - - DECIMAL_SIGN(*pdecL) = DECIMAL_SIGN(*pdecL) ^ DECIMAL_SIGN(*pdecR); - DECIMAL_HI32(*pdecL) = rgulQuo[2]; - DECIMAL_MID32(*pdecL) = rgulQuo[1]; - DECIMAL_LO32(*pdecL) = rgulQuo[0]; - DECIMAL_SCALE(*pdecL) = (BYTE)iScale; - - pdecL->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - - -FCIMPL3(void, COMDecimal::DoDivide, DECIMAL * pdecL, DECIMAL * pdecR, CLR_BOOL * overflowed) -{ - FCALL_CONTRACT; - - ULONG rgulQuo[3]; - ULONG rgulQuoSave[3]; - ULONG rgulRem[4]; - ULONG rgulDivisor[3]; - ULONG ulPwr; - ULONG ulTmp; - ULONG ulTmp1; - SPLIT64 sdlTmp; - SPLIT64 sdlDivisor; - int iScale; - int iCurScale; - BOOL fUnscale; - - iScale = DECIMAL_SCALE(*pdecL) - DECIMAL_SCALE(*pdecR); - fUnscale = FALSE; - rgulDivisor[0] = DECIMAL_LO32(*pdecR); - rgulDivisor[1] = DECIMAL_MID32(*pdecR); - rgulDivisor[2] = DECIMAL_HI32(*pdecR); - - if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) { - // Divisor is only 32 bits. Easy divide. - // - if (rgulDivisor[0] == 0) - FCThrowVoid(kDivideByZeroException); - - rgulQuo[0] = DECIMAL_LO32(*pdecL); - rgulQuo[1] = DECIMAL_MID32(*pdecL); - rgulQuo[2] = DECIMAL_HI32(*pdecL); - rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]); - - for (;;) { - if (rgulRem[0] == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale; - } - break; - } - // We need to unscale if and only if we have a non-zero remainder - fUnscale = 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 rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9 - // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and - // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since - // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888 - // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is - // assured not to overflow. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - ulTmp = rgulRem[0] << 1; - if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] && - (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) { -RoundUp: - if (!Add32To96(rgulQuo, 1)) { - if (iScale == 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - iScale--; - OverflowUnscale(rgulQuo, TRUE); - break; - } - } - break; - } - - if (iCurScale < 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - -HaveScale: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - - - sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]); - rgulRem[0] = sdlTmp.u.Hi; - - if (!Add32To96(rgulQuo, sdlTmp.u.Lo)) { - if (iScale == 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 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 ULONG 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. - // - if (rgulDivisor[2] == 0) - ulTmp = rgulDivisor[1]; - else - ulTmp = rgulDivisor[2]; - - iCurScale = 0; - if (!(ulTmp & 0xFFFF0000)) { - iCurScale += 16; - ulTmp <<= 16; - } - if (!(ulTmp & 0xFF000000)) { - iCurScale += 8; - ulTmp <<= 8; - } - if (!(ulTmp & 0xF0000000)) { - iCurScale += 4; - ulTmp <<= 4; - } - if (!(ulTmp & 0xC0000000)) { - iCurScale += 2; - ulTmp <<= 2; - } - if (!(ulTmp & 0x80000000)) { - iCurScale++; - ulTmp <<= 1; - } - - // Shift both dividend and divisor left by iCurScale. - // - sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale; - rgulRem[0] = sdlTmp.u.Lo; - rgulRem[1] = sdlTmp.u.Hi; - sdlTmp.u.Lo = DECIMAL_MID32(*pdecL); - sdlTmp.u.Hi = DECIMAL_HI32(*pdecL); - sdlTmp.int64 <<= iCurScale; - rgulRem[2] = sdlTmp.u.Hi; - rgulRem[3] = (DECIMAL_HI32(*pdecL) >> (31 - iCurScale)) >> 1; - - sdlDivisor.u.Lo = rgulDivisor[0]; - sdlDivisor.u.Hi = rgulDivisor[1]; - sdlDivisor.int64 <<= iCurScale; - - if (rgulDivisor[2] == 0) { - // Have a 64-bit divisor in sdlDivisor. The remainder - // (currently 96 bits spread over 4 ULONGs) will be < divisor. - // - sdlTmp.u.Lo = rgulRem[2]; - sdlTmp.u.Hi = rgulRem[3]; - - rgulQuo[2] = 0; - rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor); - rgulQuo[0] = Div96By64(rgulRem, sdlDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale64; - } - break; - } - - // We need to unscale if and only if we have a non-zero remainder - fUnscale = TRUE; - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - sdlTmp.u.Lo = rgulRem[0]; - sdlTmp.u.Hi = rgulRem[1]; - if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || - (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1))) - goto RoundUp; - break; - } - - if (iCurScale < 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - -HaveScale64: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - - - rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 - IncreaseScale(rgulRem, ulPwr); - ulTmp = Div96By64(rgulRem, sdlDivisor); - if (!Add32To96(rgulQuo, ulTmp)) { - if (iScale == 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0)); - break; - } - - } // for (;;) - } - else { - // Have a 96-bit divisor in rgulDivisor[]. - // - // Start by finishing the shift left by iCurScale. - // - sdlTmp.u.Lo = rgulDivisor[1]; - sdlTmp.u.Hi = rgulDivisor[2]; - sdlTmp.int64 <<= iCurScale; - rgulDivisor[0] = sdlDivisor.u.Lo; - rgulDivisor[1] = sdlDivisor.u.Hi; - rgulDivisor[2] = sdlTmp.u.Hi; - - // The remainder (currently 96 bits spread over 4 ULONGs) - // will be < divisor. - // - rgulQuo[2] = 0; - rgulQuo[1] = 0; - rgulQuo[0] = Div128By96(rgulRem, rgulDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale96; - } - break; - } - - // We need to unscale if and only if we have a non-zero remainder - fUnscale = TRUE; - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - if (rgulRem[2] >= 0x80000000) - goto RoundUp; - - ulTmp = rgulRem[0] > 0x80000000; - ulTmp1 = rgulRem[1] > 0x80000000; - rgulRem[0] <<= 1; - rgulRem[1] = (rgulRem[1] << 1) + ulTmp; - rgulRem[2] = (rgulRem[2] << 1) + ulTmp1; - - if (rgulRem[2] > rgulDivisor[2] || (rgulRem[2] == rgulDivisor[2] && - (rgulRem[1] > rgulDivisor[1] || (rgulRem[1] == rgulDivisor[1] && - (rgulRem[0] > rgulDivisor[0] || (rgulRem[0] == rgulDivisor[0] && - (rgulQuo[0] & 1))))))) - goto RoundUp; - break; - } - - if (iCurScale < 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - -HaveScale96: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - - rgulRem[3] = IncreaseScale(rgulRem, ulPwr); - ulTmp = Div128By96(rgulRem, rgulDivisor); - if (!Add32To96(rgulQuo, ulTmp)) { - if (iScale == 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - iScale--; - OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0)); - break; - } - - } // for (;;) - } - } - - // We need to unscale if and only if we have a non-zero remainder - if (fUnscale) { - // Try extracting any extra powers of 10 we may have - // added. We do this 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. If the remainder is not zero, then we restore - // the previous value. - // - // 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. - // - while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100000000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 8; - } - else - break; - } - - if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 4; - } - } - - if ((rgulQuo[0] & 3) == 0 && iScale >= 2) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 2; - } - } - - if ((rgulQuo[0] & 1) == 0 && iScale >= 1) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 1; - } - } - } - - DECIMAL_SIGN(*pdecL) = DECIMAL_SIGN(*pdecL) ^ DECIMAL_SIGN(*pdecR); - DECIMAL_HI32(*pdecL) = rgulQuo[2]; - DECIMAL_MID32(*pdecL) = rgulQuo[1]; - DECIMAL_LO32(*pdecL) = rgulQuo[0]; - DECIMAL_SCALE(*pdecL) = (BYTE)iScale; - - pdecL->wReserved = 0; - *overflowed = false; - FC_GC_POLL(); -} -FCIMPLEND - -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - - -//********************************************************************** -// -// VarDecAdd - Decimal Addition -// VarDecSub - Decimal Subtraction -// -//********************************************************************** - -static const ULONG ulTenToNine = 1000000000; - -/*** -* ScaleResult -* -* Entry: -* rgulRes - Array of ULONGs with value, least-significant first. -* iHiRes - Index of last non-zero value in rgulRes. -* iScale - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX -* -* Purpose: -* See if we need to scale the result to fit it in 96 bits. -* Perform needed scaling. Adjust scale factor accordingly. -* -* Exit: -* rgulRes updated in place, always 3 ULONGs. -* New scale factor returned, -1 if overflow error. -* -***********************************************************************/ - -int ScaleResult(ULONG *rgulRes, int iHiRes, int iScale) -{ - LIMITED_METHOD_CONTRACT; - - int iNewScale; - int iCur; - ULONG ulPwr; - ULONG ulTmp; - ULONG ulSticky; - SPLIT64 sdlTmp; - - // 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. iHiRes is the index into rgulRes[] - // of the highest non-zero ULONG. - // - iNewScale = iHiRes * 32 - 64 - 1; - if (iNewScale > 0) { - - // Find the MSB. - // - ulTmp = rgulRes[iHiRes]; - if (!(ulTmp & 0xFFFF0000)) { - iNewScale -= 16; - ulTmp <<= 16; - } - if (!(ulTmp & 0xFF000000)) { - iNewScale -= 8; - ulTmp <<= 8; - } - if (!(ulTmp & 0xF0000000)) { - iNewScale -= 4; - ulTmp <<= 4; - } - if (!(ulTmp & 0xC0000000)) { - iNewScale -= 2; - ulTmp <<= 2; - } - if (!(ulTmp & 0x80000000)) { - iNewScale--; - ulTmp <<= 1; - } - - // 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...) - // - iNewScale = ((iNewScale * 77) >> 8) + 1; - - // iNewScale = 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 (iNewScale > iScale) - return -1; - } - else - iNewScale = 0; - - // Make sure we scale by enough to bring the current scale factor - // into valid range. - // - if (iNewScale < iScale - DEC_SCALE_MAX) - iNewScale = iScale - DEC_SCALE_MAX; - - if (iNewScale != 0) { - // Scale by the power of 10 given by iNewScale. Note that this is - // NOT guaranteed to bring the number within 96 bits -- it could - // be 1 power of 10 short. - // - iScale -= iNewScale; - ulSticky = 0; - sdlTmp.u.Hi = 0; // initialize remainder - - for (;;) { - - ulSticky |= sdlTmp.u.Hi; // record remainder as sticky bit - - if (iNewScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iNewScale]; - - // Compute first quotient. - // DivMod64by32 returns quotient in Lo, remainder in Hi. - // - sdlTmp.int64 = DivMod64by32(rgulRes[iHiRes], ulPwr); - rgulRes[iHiRes] = sdlTmp.u.Lo; - iCur = iHiRes - 1; - - if (iCur >= 0) { - // If first quotient was 0, update iHiRes. - // - if (sdlTmp.u.Lo == 0) - iHiRes--; - - // Compute subsequent quotients. - // - do { - sdlTmp.u.Lo = rgulRes[iCur]; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr); - rgulRes[iCur] = sdlTmp.u.Lo; - iCur--; - } while (iCur >= 0); - - } - - iNewScale -= POWER10_MAX; - if (iNewScale > 0) - continue; // scale some more - - // If we scaled enough, iHiRes would be 2 or less. If not, - // divide by 10 more. - // - if (iHiRes > 2) { - iNewScale = 1; - iScale--; - 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. - // - ulPwr >>= 1; // power of 10 always even - if ( ulPwr <= sdlTmp.u.Hi && (ulPwr < sdlTmp.u.Hi || - ((rgulRes[0] & 1) | ulSticky)) ) { - iCur = -1; - while (++rgulRes[++iCur] == 0); - - if (iCur > 2) { - // The rounding caused us to carry beyond 96 bits. - // Scale by 10 more. - // - iHiRes = iCur; - ulSticky = 0; // no sticky bit - sdlTmp.u.Hi = 0; // or remainder - iNewScale = 1; - iScale--; - continue; // scale by 10 - } - } - - // We may have scaled it more than we planned. Make sure the scale - // factor hasn't gone negative, indicating overflow. - // - if (iScale < 0) - return -1; - - return iScale; - } // for(;;) - } - return iScale; -} - -FCIMPL3(void, COMDecimal::DoAddSubThrow, DECIMAL * pdecL, DECIMAL * pdecR, UINT8 bSign) -{ - FCALL_CONTRACT; - - ULONG rgulNum[6]; - ULONG ulPwr; - int iScale; - int iHiProd; - int iCur; - SPLIT64 sdlTmp; - DECIMAL decRes; - DECIMAL decTmp; - LPDECIMAL pdecTmp; - LPDECIMAL pdecLOriginal; - - _ASSERTE(bSign == 0 || bSign == DECIMAL_NEG); - - pdecLOriginal = pdecL; - - bSign ^= (DECIMAL_SIGN(*pdecR) ^ DECIMAL_SIGN(*pdecL)) & DECIMAL_NEG; - - if (DECIMAL_SCALE(*pdecR) == DECIMAL_SCALE(*pdecL)) { - // Scale factors are equal, no alignment necessary. - // - DECIMAL_SIGNSCALE(decRes) = DECIMAL_SIGNSCALE(*pdecL); - -AlignedAdd: - if (bSign) { - // Signs differ - subtract - // - DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) { - DECIMAL_HI32(decRes)--; - if (DECIMAL_HI32(decRes) >= DECIMAL_HI32(*pdecL)) - goto SignFlip; - } - else if (DECIMAL_HI32(decRes) > DECIMAL_HI32(*pdecL)) { - // Got negative result. Flip its sign. - // -SignFlip: - DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes)); - DECIMAL_HI32(decRes) = ~DECIMAL_HI32(decRes); - if (DECIMAL_LO64_GET(decRes) == 0) - DECIMAL_HI32(decRes)++; - DECIMAL_SIGN(decRes) ^= DECIMAL_NEG; - } - - } - else { - // Signs are the same - add - // - DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) + DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) { - DECIMAL_HI32(decRes)++; - if (DECIMAL_HI32(decRes) <= DECIMAL_HI32(*pdecL)) - goto AlignedScale; - } - else if (DECIMAL_HI32(decRes) < DECIMAL_HI32(*pdecL)) { -AlignedScale: - // The addition carried above 96 bits. Divide the result by 10, - // dropping the scale factor. - // - if (DECIMAL_SCALE(decRes) == 0) - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); // DISP_E_OVERFLOW - DECIMAL_SCALE(decRes)--; - - sdlTmp.u.Lo = DECIMAL_HI32(decRes); - sdlTmp.u.Hi = 1; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_HI32(decRes) = sdlTmp.u.Lo; - - sdlTmp.u.Lo = DECIMAL_MID32(decRes); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_MID32(decRes) = sdlTmp.u.Lo; - - sdlTmp.u.Lo = DECIMAL_LO32(decRes); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_LO32(decRes) = sdlTmp.u.Lo; - - // See if we need to round up. - // - if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(decRes) & 1))) { - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1); - if (DECIMAL_LO64_GET(decRes) == 0) - DECIMAL_HI32(decRes)++; - } - } - } - } - 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. - // - DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecR); // scale factor of "smaller" - DECIMAL_SIGN(decRes) = DECIMAL_SIGN(*pdecL); // but sign of "larger" - iScale = DECIMAL_SCALE(decRes)- DECIMAL_SCALE(*pdecL); - - if (iScale < 0) { - // Guessed scale factor wrong. Swap operands. - // - iScale = -iScale; - DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecL); - DECIMAL_SIGN(decRes) ^= bSign; - pdecTmp = pdecR; - pdecR = pdecL; - pdecL = pdecTmp; - } - - // *pdecL will need to be multiplied by 10^iScale so - // it will have the same scale as *pdecR. We could be - // extending it to up to 192 bits of precision. - // - if (iScale <= POWER10_MAX) { - // Scaling won't make it larger than 4 ULONGs - // - ulPwr = rgulPower10[iScale]; - DECIMAL_LO64_SET(decTmp, UInt32x32To64(DECIMAL_LO32(*pdecL), ulPwr)); - sdlTmp.int64 = UInt32x32To64(DECIMAL_MID32(*pdecL), ulPwr); - sdlTmp.int64 += DECIMAL_MID32(decTmp); - DECIMAL_MID32(decTmp) = sdlTmp.u.Lo; - DECIMAL_HI32(decTmp) = sdlTmp.u.Hi; - sdlTmp.int64 = UInt32x32To64(DECIMAL_HI32(*pdecL), ulPwr); - sdlTmp.int64 += DECIMAL_HI32(decTmp); - if (sdlTmp.u.Hi == 0) { - // Result fits in 96 bits. Use standard aligned add. - // - DECIMAL_HI32(decTmp) = sdlTmp.u.Lo; - pdecL = &decTmp; - goto AlignedAdd; - } - rgulNum[0] = DECIMAL_LO32(decTmp); - rgulNum[1] = DECIMAL_MID32(decTmp); - rgulNum[2] = sdlTmp.u.Lo; - rgulNum[3] = sdlTmp.u.Hi; - iHiProd = 3; - } - else { - // Have to scale by a bunch. Move the number to a buffer - // where it has room to grow as it's scaled. - // - rgulNum[0] = DECIMAL_LO32(*pdecL); - rgulNum[1] = DECIMAL_MID32(*pdecL); - rgulNum[2] = DECIMAL_HI32(*pdecL); - iHiProd = 2; - - // Scan for zeros in the upper words. - // - if (rgulNum[2] == 0) { - iHiProd = 1; - if (rgulNum[1] == 0) { - iHiProd = 0; - if (rgulNum[0] == 0) { - // Left arg is zero, return right. - // - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR)); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecR); - DECIMAL_SIGN(decRes) ^= bSign; - goto RetDec; - } - } - } - - // Scaling loop, up to 10^9 at a time. iHiProd stays updated - // with index of highest non-zero ULONG. - // - for (; iScale > 0; iScale -= POWER10_MAX) { - if (iScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iScale]; - - sdlTmp.u.Hi = 0; - for (iCur = 0; iCur <= iHiProd; iCur++) { - sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi; - rgulNum[iCur] = sdlTmp.u.Lo; - } - - if (sdlTmp.u.Hi != 0) - // We're extending the result by another ULONG. - rgulNum[++iHiProd] = sdlTmp.u.Hi; - } - } - - // Scaling complete, do the add. Could be subtract if signs differ. - // - sdlTmp.u.Lo = rgulNum[0]; - sdlTmp.u.Hi = rgulNum[1]; - - if (bSign) { - // Signs differ, subtract. - // - DECIMAL_LO64_SET(decRes, (sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = rgulNum[2] - DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) { - DECIMAL_HI32(decRes)--; - if (DECIMAL_HI32(decRes) >= rgulNum[2]) - goto LongSub; - } - else if (DECIMAL_HI32(decRes) > rgulNum[2]) { -LongSub: - // If rgulNum has more than 96 bits of precision, then we need to - // carry the subtraction into the higher bits. If it doesn't, - // then we subtracted in the wrong order and have to flip the - // sign of the result. - // - if (iHiProd <= 2) - goto SignFlip; - - iCur = 3; - while(rgulNum[iCur++]-- == 0); - if (rgulNum[iHiProd] == 0) - iHiProd--; - } - } - else { - // Signs the same, add. - // - DECIMAL_LO64_SET(decRes, (sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = rgulNum[2] + DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) { - DECIMAL_HI32(decRes)++; - if (DECIMAL_HI32(decRes) <= rgulNum[2]) - goto LongAdd; - } - else if (DECIMAL_HI32(decRes) < rgulNum[2]) { -LongAdd: - // Had a carry above 96 bits. - // - iCur = 3; - do { - if (iHiProd < iCur) { - rgulNum[iCur] = 1; - iHiProd = iCur; - break; - } - }while (++rgulNum[iCur++] == 0); - } - } - - if (iHiProd > 2) { - rgulNum[0] = DECIMAL_LO32(decRes); - rgulNum[1] = DECIMAL_MID32(decRes); - rgulNum[2] = DECIMAL_HI32(decRes); - DECIMAL_SCALE(decRes) = (BYTE)ScaleResult(rgulNum, iHiProd, DECIMAL_SCALE(decRes)); - if (DECIMAL_SCALE(decRes) == (BYTE)-1) - FCThrowResVoid(kOverflowException, W("Overflow_Decimal")); // DISP_E_OVERFLOW - - DECIMAL_LO32(decRes) = rgulNum[0]; - DECIMAL_MID32(decRes) = rgulNum[1]; - DECIMAL_HI32(decRes) = rgulNum[2]; - } - } - -RetDec: - pdecL = pdecLOriginal; - COPYDEC(*pdecL, decRes) - pdecL->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - -FCIMPL4(void, COMDecimal::DoAddSub, DECIMAL * pdecL, DECIMAL * pdecR, UINT8 bSign, CLR_BOOL * overflowed) -{ - FCALL_CONTRACT; - - ULONG rgulNum[6]; - ULONG ulPwr; - int iScale; - int iHiProd; - int iCur; - SPLIT64 sdlTmp; - DECIMAL decRes; - DECIMAL decTmp; - LPDECIMAL pdecTmp; - LPDECIMAL pdecLOriginal; - - _ASSERTE(bSign == 0 || bSign == DECIMAL_NEG); - - pdecLOriginal = pdecL; - - bSign ^= (DECIMAL_SIGN(*pdecR) ^ DECIMAL_SIGN(*pdecL)) & DECIMAL_NEG; - - if (DECIMAL_SCALE(*pdecR) == DECIMAL_SCALE(*pdecL)) { - // Scale factors are equal, no alignment necessary. - // - DECIMAL_SIGNSCALE(decRes) = DECIMAL_SIGNSCALE(*pdecL); - -AlignedAdd: - if (bSign) { - // Signs differ - subtract - // - DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) { - DECIMAL_HI32(decRes)--; - if (DECIMAL_HI32(decRes) >= DECIMAL_HI32(*pdecL)) - goto SignFlip; - } - else if (DECIMAL_HI32(decRes) > DECIMAL_HI32(*pdecL)) { - // Got negative result. Flip its sign. - // -SignFlip: - DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes)); - DECIMAL_HI32(decRes) = ~DECIMAL_HI32(decRes); - if (DECIMAL_LO64_GET(decRes) == 0) - DECIMAL_HI32(decRes)++; - DECIMAL_SIGN(decRes) ^= DECIMAL_NEG; - } - - } - else { - // Signs are the same - add - // - DECIMAL_LO64_SET(decRes, (DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) + DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) { - DECIMAL_HI32(decRes)++; - if (DECIMAL_HI32(decRes) <= DECIMAL_HI32(*pdecL)) - goto AlignedScale; - } - else if (DECIMAL_HI32(decRes) < DECIMAL_HI32(*pdecL)) { -AlignedScale: - // The addition carried above 96 bits. Divide the result by 10, - // dropping the scale factor. - // - if (DECIMAL_SCALE(decRes) == 0) { - *overflowed = true; - FC_GC_POLL(); - return; - } - DECIMAL_SCALE(decRes)--; - - sdlTmp.u.Lo = DECIMAL_HI32(decRes); - sdlTmp.u.Hi = 1; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_HI32(decRes) = sdlTmp.u.Lo; - - sdlTmp.u.Lo = DECIMAL_MID32(decRes); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_MID32(decRes) = sdlTmp.u.Lo; - - sdlTmp.u.Lo = DECIMAL_LO32(decRes); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - DECIMAL_LO32(decRes) = sdlTmp.u.Lo; - - // See if we need to round up. - // - if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (DECIMAL_LO32(decRes) & 1))) { - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1); - if (DECIMAL_LO64_GET(decRes) == 0) - DECIMAL_HI32(decRes)++; - } - } - } - } - 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. - // - DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecR); // scale factor of "smaller" - DECIMAL_SIGN(decRes) = DECIMAL_SIGN(*pdecL); // but sign of "larger" - iScale = DECIMAL_SCALE(decRes)- DECIMAL_SCALE(*pdecL); - - if (iScale < 0) { - // Guessed scale factor wrong. Swap operands. - // - iScale = -iScale; - DECIMAL_SCALE(decRes) = DECIMAL_SCALE(*pdecL); - DECIMAL_SIGN(decRes) ^= bSign; - pdecTmp = pdecR; - pdecR = pdecL; - pdecL = pdecTmp; - } - - // *pdecL will need to be multiplied by 10^iScale so - // it will have the same scale as *pdecR. We could be - // extending it to up to 192 bits of precision. - // - if (iScale <= POWER10_MAX) { - // Scaling won't make it larger than 4 ULONGs - // - ulPwr = rgulPower10[iScale]; - DECIMAL_LO64_SET(decTmp, UInt32x32To64(DECIMAL_LO32(*pdecL), ulPwr)); - sdlTmp.int64 = UInt32x32To64(DECIMAL_MID32(*pdecL), ulPwr); - sdlTmp.int64 += DECIMAL_MID32(decTmp); - DECIMAL_MID32(decTmp) = sdlTmp.u.Lo; - DECIMAL_HI32(decTmp) = sdlTmp.u.Hi; - sdlTmp.int64 = UInt32x32To64(DECIMAL_HI32(*pdecL), ulPwr); - sdlTmp.int64 += DECIMAL_HI32(decTmp); - if (sdlTmp.u.Hi == 0) { - // Result fits in 96 bits. Use standard aligned add. - // - DECIMAL_HI32(decTmp) = sdlTmp.u.Lo; - pdecL = &decTmp; - goto AlignedAdd; - } - rgulNum[0] = DECIMAL_LO32(decTmp); - rgulNum[1] = DECIMAL_MID32(decTmp); - rgulNum[2] = sdlTmp.u.Lo; - rgulNum[3] = sdlTmp.u.Hi; - iHiProd = 3; - } - else { - // Have to scale by a bunch. Move the number to a buffer - // where it has room to grow as it's scaled. - // - rgulNum[0] = DECIMAL_LO32(*pdecL); - rgulNum[1] = DECIMAL_MID32(*pdecL); - rgulNum[2] = DECIMAL_HI32(*pdecL); - iHiProd = 2; - - // Scan for zeros in the upper words. - // - if (rgulNum[2] == 0) { - iHiProd = 1; - if (rgulNum[1] == 0) { - iHiProd = 0; - if (rgulNum[0] == 0) { - // Left arg is zero, return right. - // - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR)); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecR); - DECIMAL_SIGN(decRes) ^= bSign; - goto RetDec; - } - } - } - - // Scaling loop, up to 10^9 at a time. iHiProd stays updated - // with index of highest non-zero ULONG. - // - for (; iScale > 0; iScale -= POWER10_MAX) { - if (iScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iScale]; - - sdlTmp.u.Hi = 0; - for (iCur = 0; iCur <= iHiProd; iCur++) { - sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi; - rgulNum[iCur] = sdlTmp.u.Lo; - } - - if (sdlTmp.u.Hi != 0) - // We're extending the result by another ULONG. - rgulNum[++iHiProd] = sdlTmp.u.Hi; - } - } - - // Scaling complete, do the add. Could be subtract if signs differ. - // - sdlTmp.u.Lo = rgulNum[0]; - sdlTmp.u.Hi = rgulNum[1]; - - if (bSign) { - // Signs differ, subtract. - // - DECIMAL_LO64_SET(decRes, (sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = rgulNum[2] - DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) { - DECIMAL_HI32(decRes)--; - if (DECIMAL_HI32(decRes) >= rgulNum[2]) - goto LongSub; - } - else if (DECIMAL_HI32(decRes) > rgulNum[2]) { -LongSub: - // If rgulNum has more than 96 bits of precision, then we need to - // carry the subtraction into the higher bits. If it doesn't, - // then we subtracted in the wrong order and have to flip the - // sign of the result. - // - if (iHiProd <= 2) - goto SignFlip; - - iCur = 3; - while(rgulNum[iCur++]-- == 0); - if (rgulNum[iHiProd] == 0) - iHiProd--; - } - } - else { - // Signs the same, add. - // - DECIMAL_LO64_SET(decRes, (sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR))); - DECIMAL_HI32(decRes) = rgulNum[2] + DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) { - DECIMAL_HI32(decRes)++; - if (DECIMAL_HI32(decRes) <= rgulNum[2]) - goto LongAdd; - } - else if (DECIMAL_HI32(decRes) < rgulNum[2]) { -LongAdd: - // Had a carry above 96 bits. - // - iCur = 3; - do { - if (iHiProd < iCur) { - rgulNum[iCur] = 1; - iHiProd = iCur; - break; - } - }while (++rgulNum[iCur++] == 0); - } - } - - if (iHiProd > 2) { - rgulNum[0] = DECIMAL_LO32(decRes); - rgulNum[1] = DECIMAL_MID32(decRes); - rgulNum[2] = DECIMAL_HI32(decRes); - DECIMAL_SCALE(decRes) = (BYTE)ScaleResult(rgulNum, iHiProd, DECIMAL_SCALE(decRes)); - if (DECIMAL_SCALE(decRes) == (BYTE)-1) { - *overflowed = true; - FC_GC_POLL(); - return; - } - - DECIMAL_LO32(decRes) = rgulNum[0]; - DECIMAL_MID32(decRes) = rgulNum[1]; - DECIMAL_HI32(decRes) = rgulNum[2]; - } - } - -RetDec: - pdecL = pdecLOriginal; - COPYDEC(*pdecL, decRes) - pdecL->wReserved = 0; - FC_GC_POLL(); -} -FCIMPLEND - diff --git a/src/classlibnative/bcltype/decimal.h b/src/classlibnative/bcltype/decimal.h deleted file mode 100644 index f037fd3acb..0000000000 --- a/src/classlibnative/bcltype/decimal.h +++ /dev/null @@ -1,52 +0,0 @@ -// 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. -// -// File: Decimal.h -// - -// - -#ifndef _DECIMAL_H_ -#define _DECIMAL_H_ - -#include - -#include - -#include "number.h" - -#define DECIMAL_PRECISION 29 - -class COMDecimal { -public: - static FCDECL2_IV(void, InitSingle, DECIMAL *_this, float value); - static FCDECL2_IV(void, InitDouble, DECIMAL *_this, double value); - static FCDECL2(INT32, DoCompare, DECIMAL * d1, DECIMAL * d2); - - static FCDECL3(void, DoAddSubThrow, DECIMAL * d1, DECIMAL * d2, UINT8 bSign); - static FCDECL2(void, DoDivideThrow, DECIMAL * d1, DECIMAL * d2); - static FCDECL2(void, DoMultiplyThrow, DECIMAL * d1, DECIMAL * d2); - - static FCDECL4(void, DoAddSub, DECIMAL * d1, DECIMAL * d2, UINT8 bSign, CLR_BOOL * overflowed); - static FCDECL3(void, DoDivide, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed); - static FCDECL3(void, DoMultiply, DECIMAL * d1, DECIMAL * d2, CLR_BOOL * overflowed); - - static FCDECL2(void, DoRound, DECIMAL * d1, INT32 decimals); - static FCDECL2_IV(void, DoToCurrency, CY * result, DECIMAL d); - static FCDECL1(void, DoTruncate, DECIMAL * d); - static FCDECL1(void, DoFloor, DECIMAL * d); - - static FCDECL1(double, ToDouble, FC_DECIMAL d); - static FCDECL1(float, ToSingle, FC_DECIMAL d); - static FCDECL1(INT32, ToInt32, FC_DECIMAL d); - static FCDECL1(Object*, ToString, FC_DECIMAL d); - - static int NumberToDecimal(NUMBER* number, DECIMAL* value); - - -}; - -#include - -#endif // _DECIMAL_H_ diff --git a/src/classlibnative/bcltype/number.cpp b/src/classlibnative/bcltype/number.cpp index ac068d6a54..b4bf7a5bfc 100644 --- a/src/classlibnative/bcltype/number.cpp +++ b/src/classlibnative/bcltype/number.cpp @@ -11,7 +11,6 @@ #include "excep.h" #include "number.h" #include "string.h" -#include "decimal.h" #include "bignum.h" #include "grisu3.h" #include "fp.h" @@ -1278,6 +1277,7 @@ STRINGREF NumberToString(NUMBER* number, wchar format, int nMaxDigits, NUMFMTREF */ _ASSERTE(numfmt != NULL); + _ASSERTE(!bDecimal); UINT64 newBufferLen = MIN_BUFFER_SIZE; CQuickBytesSpecifySize buf; @@ -1441,15 +1441,8 @@ STRINGREF NumberToString(NUMBER* number, wchar format, int nMaxDigits, NUMFMTREF { bool enableRounding = true; if (nMaxDigits < 1) { - if (bDecimal && (nMaxDigits == -1)) { // Default to 29 digits precision only for G formatting without a precision specifier - // This ensures that the PAL code pads out to the correct place even when we use the default precision - nMaxDigits = nMinDigits = DECIMAL_PRECISION; - enableRounding = false; // Turn off rounding for ECMA compliance to output trailing 0's after decimal as significant - } - else { - // This ensures that the PAL code pads out to the correct place even when we use the default precision - nMaxDigits = nMinDigits = number->precision; - } + // This ensures that the PAL code pads out to the correct place even when we use the default precision + nMaxDigits = nMinDigits = number->precision; } else nMinDigits=nMaxDigits; @@ -1471,11 +1464,6 @@ STRINGREF NumberToString(NUMBER* number, wchar format, int nMaxDigits, NUMFMTREF if (enableRounding) // Don't round for G formatting without precision RoundNumber(number, nMaxDigits); // This also fixes up the minus zero case - else { - if (bDecimal && ((GetDigitsBuffer(number))[0] == 0)) { // Minus zero should be formatted as 0 - number->sign = 0; - } - } if (number->sign) { AddStringRef(&dst, sNegative); } @@ -2000,11 +1988,3 @@ FCIMPL1(double, COMNumber::NumberToDoubleFC, NUMBER* number) return d; } FCIMPLEND - -FCIMPL2(FC_BOOL_RET, COMNumber::NumberBufferToDecimal, NUMBER* number, DECIMAL* value) -{ - FCALL_CONTRACT; - - FC_RETURN_BOOL(COMDecimal::NumberToDecimal(number, value) != 0); -} -FCIMPLEND diff --git a/src/classlibnative/bcltype/number.h b/src/classlibnative/bcltype/number.h index bf1f328b02..0fd12b11bd 100644 --- a/src/classlibnative/bcltype/number.h +++ b/src/classlibnative/bcltype/number.h @@ -33,7 +33,6 @@ class COMNumber public: static FCDECL3_VII(void, DoubleToNumberFC, double value, int precision, NUMBER* number); static FCDECL1(double, NumberToDoubleFC, NUMBER* number); - static FCDECL2(FC_BOOL_RET, NumberBufferToDecimal, NUMBER* number, DECIMAL* value); static wchar_t* Int32ToDecChars(__in wchar_t* p, unsigned int value, int digits); }; diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h index 2d7f1c1c85..15d18f52ee 100644 --- a/src/inc/utilcode.h +++ b/src/inc/utilcode.h @@ -1057,55 +1057,36 @@ inline int CountBits(int iNum) #include "bitposition.h" -// Used to remove trailing zeros from Decimal types. -// NOTE: Assumes hi32 bits are empty (used for conversions from Cy->Dec) -inline HRESULT DecimalCanonicalize(DECIMAL* dec) +// Convert the currency to a decimal and canonicalize. +inline void VarDecFromCyCanonicalize(CY cyIn, DECIMAL* dec) { WRAPPER_NO_CONTRACT; - // Clear the VARENUM field - (*(USHORT*)dec) = 0; - - // Remove trailing zeros: - DECIMAL temp; - DECIMAL templast; - temp = templast = *dec; - - // Ensure the hi 32 bits are empty (should be if we came from a currency) - if ((DECIMAL_HI32(temp) != 0) || (DECIMAL_SCALE(temp) > 4)) - return DISP_E_OVERFLOW; - - // Return immediately if dec represents a zero. - if (DECIMAL_LO32(temp) == 0 && DECIMAL_MID32(temp) == 0) - return S_OK; - - // Compare to the original to see if we've - // lost non-zero digits (and make sure we don't overflow the scale BYTE) - -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable:6219) // "Suppress PREFast warning about Implicit cast between semantically different integer types" -#endif - while ((DECIMAL_SCALE(temp) <= 4) && (VARCMP_EQ == VarDecCmp(dec, &temp))) + (*(ULONG*)dec) = 0; + DECIMAL_HI32(*dec) = 0; + if (cyIn.int64 == 0) // For compatibility, a currency of 0 emits the Decimal "0.0000" (scale set to 4). { + DECIMAL_SCALE(*dec) = 4; + DECIMAL_LO32(*dec) = 0; + DECIMAL_MID32(*dec) = 0; + return; + } -#ifdef _PREFAST_ -#pragma warning(pop) -#endif - templast = temp; - - // Remove the last digit and normalize. Ignore temp.Hi32 - // as Currency values will have a max of 64 bits of data. - DECIMAL_SCALE(temp)--; - UINT64 temp64 = (((UINT64) DECIMAL_MID32(temp)) << 32) + DECIMAL_LO32(temp); - temp64 /= 10; - - DECIMAL_MID32(temp) = (ULONG)(temp64 >> 32); - DECIMAL_LO32(temp) = (ULONG)temp64; + if (cyIn.int64 < 0) { + DECIMAL_SIGN(*dec) = DECIMAL_NEG; + cyIn.int64 = -cyIn.int64; } - *dec = templast; - return S_OK; + BYTE scale = 4; + ULONGLONG absoluteCy = (ULONGLONG)cyIn.int64; + while (scale != 0 && ((absoluteCy % 10) == 0)) + { + scale--; + absoluteCy /= 10; + } + DECIMAL_SCALE(*dec) = scale; + DECIMAL_LO32(*dec) = (ULONG)absoluteCy; + DECIMAL_MID32(*dec) = (ULONG)(absoluteCy >> 32); } //***************************************************************************** diff --git a/src/palrt/CMakeLists.txt b/src/palrt/CMakeLists.txt index e19b55d9dc..e5ca200a5e 100644 --- a/src/palrt/CMakeLists.txt +++ b/src/palrt/CMakeLists.txt @@ -5,8 +5,6 @@ set(PALRT_SOURCES bstr.cpp coguid.cpp comem.cpp - decarith.cpp - decconv.cpp guid.cpp memorystream.cpp path.cpp diff --git a/src/palrt/decarith.cpp b/src/palrt/decarith.cpp deleted file mode 100644 index f190707ab6..0000000000 --- a/src/palrt/decarith.cpp +++ /dev/null @@ -1,1267 +0,0 @@ -// 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. -// - -// -// =========================================================================== -// File: decarith.cpp -// -// =========================================================================== -/*** -* -*Purpose: -* Implement arithmetic for Decimal data type. -* -*Implementation Notes: -* -*****************************************************************************/ - -#include "common.h" - -#include -#include "convert.h" - -//*********************************************************************** -// -// Additional Decimal and Int64 definitions -// -#define COPYDEC(dest, src) {DECIMAL_SIGNSCALE(dest) = DECIMAL_SIGNSCALE(src); DECIMAL_HI32(dest) = DECIMAL_HI32(src); \ - DECIMAL_MID32(dest) = DECIMAL_MID32(src); DECIMAL_LO32(dest) = DECIMAL_LO32(src); } - -#define DEC_SCALE_MAX 28 -#define POWER10_MAX 9 - -// The following functions are defined in the classlibnative\bcltype\decimal.cpp -ULONG Div96By32(ULONG *rgulNum, ULONG ulDen); -ULONG Div96By64(ULONG *rgulNum, SPLIT64 sdlDen); -ULONG Div128By96(ULONG *rgulNum, ULONG *rgulDen); -int ScaleResult(ULONG *rgulRes, int iHiRes, int iScale); -ULONG IncreaseScale(ULONG *rgulNum, ULONG ulPwr); - -//*********************************************************************** -// -// Data tables -// - -static ULONG rgulPower10[POWER10_MAX+1] = {1, 10, 100, 1000, 10000, 100000, 1000000, - 10000000, 100000000, 1000000000}; - -struct DECOVFL -{ - ULONG Hi; - ULONG Mid; -}; - -static DECOVFL PowerOvfl[] = { -// This is a table of the largest values that can be in the upper two -// ULONGs 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 <= 9. For the lower word, this is the -// remaining fraction part * 2^32. 2^32 = 4294967296. -// - { 429496729UL, 2576980377UL }, // 10^1 remainder 0.6 - { 42949672UL, 4123168604UL }, // 10^2 remainder 0.16 - { 4294967UL, 1271310319UL }, // 10^3 remainder 0.616 - { 429496UL, 3133608139UL }, // 10^4 remainder 0.1616 - { 42949UL, 2890341191UL }, // 10^5 remainder 0.51616 - { 4294UL, 4154504685UL }, // 10^6 remainder 0.551616 - { 429UL, 2133437386UL }, // 10^7 remainder 0.9551616 - { 42UL, 4078814305UL }, // 10^8 remainder 0.09991616 -// { 4UL, 1266874889UL }, // 10^9 remainder 0.709551616 -}; - -#define OVFL_MAX_9_HI 4 -#define OVFL_MAX_9_MID 1266874889 - -#define OVFL_MAX_5_HI 42949 -#define OVFL_MAX_5_MID 2890341191 - -#define OVFL_MAX_1_HI 429496729 - - - -//*********************************************************************** -// -// static helper functions -// - -/*** -* FullDiv64By32 -* -* Entry: -* pdlNum - Pointer to 64-bit dividend -* ulDen - 32-bit divisor -* -* Purpose: -* Do full divide, yielding 64-bit result and 32-bit remainder. -* -* Exit: -* Quotient overwrites dividend. -* Returns remainder. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG FullDiv64By32(DWORDLONG *pdlNum, ULONG ulDen) -{ - SPLIT64 sdlTmp; - SPLIT64 sdlRes; - - sdlTmp.int64 = *pdlNum; - sdlRes.u.Hi = 0; - - if (sdlTmp.u.Hi >= ulDen) { - // DivMod64by32 returns quotient in Lo, remainder in Hi. - // - sdlRes.u.Lo = sdlTmp.u.Hi; - sdlRes.int64 = DivMod64by32(sdlRes.int64, ulDen); - sdlTmp.u.Hi = sdlRes.u.Hi; - sdlRes.u.Hi = sdlRes.u.Lo; - } - - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen); - sdlRes.u.Lo = sdlTmp.u.Lo; - *pdlNum = sdlRes.int64; - return sdlTmp.u.Hi; -} - - - - -/*** -* SearchScale -* -* Entry: -* ulResHi - Top ULONG of quotient -* ulResLo - Middle ULONG of quotient -* iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX -* -* Purpose: -* Determine the max power of 10, <= 9, that the quotient can be scaled -* up by and still fit in 96 bits. -* -* Exit: -* Returns power of 10 to scale by, -1 if overflow error. -* -***********************************************************************/ - -int SearchScale(ULONG ulResHi, ULONG ulResLo, int iScale) -{ - int iCurScale; - - // Quick check to stop us from trying to scale any more. - // - if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX) { - iCurScale = 0; - goto HaveScale; - } - - if (iScale > 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. - // - iCurScale = DEC_SCALE_MAX - iScale; - if (ulResHi < PowerOvfl[iCurScale - 1].Hi) - goto HaveScale; - - if (ulResHi == PowerOvfl[iCurScale - 1].Hi) { - UpperEq: - if (ulResLo >= PowerOvfl[iCurScale - 1].Mid) - iCurScale--; - goto HaveScale; - } - } - else if (ulResHi < OVFL_MAX_9_HI || (ulResHi == OVFL_MAX_9_HI && - ulResLo < OVFL_MAX_9_MID)) - return 9; - - // Search for a power to scale by < 9. Do a binary search - // on PowerOvfl[]. - // - iCurScale = 5; - if (ulResHi < OVFL_MAX_5_HI) - iCurScale = 7; - else if (ulResHi > OVFL_MAX_5_HI) - iCurScale = 3; - else - goto UpperEq; - - // iCurScale is 3 or 7. - // - if (ulResHi < PowerOvfl[iCurScale - 1].Hi) - iCurScale++; - else if (ulResHi > PowerOvfl[iCurScale - 1].Hi) - iCurScale--; - else - goto UpperEq; - - // iCurScale is 2, 4, 6, or 8. - // - // 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 (ulResHi > PowerOvfl[iCurScale - 1].Hi) - iCurScale--; - - if (ulResHi == PowerOvfl[iCurScale - 1].Hi) - goto UpperEq; - -HaveScale: - // iCurScale = largest power of 10 we can scale by without overflow, - // iCurScale < 9. See if this is enough to make scale factor - // positive if it isn't already. - // - if (iCurScale + iScale < 0) - iCurScale = -1; - - return iCurScale; -} - -/*** -* DecFixInt -* -* Entry: -* pdecRes - Pointer to Decimal result location -* pdecIn - Pointer to Decimal operand -* -* Purpose: -* Chop the value to integer. Return remainder so Int() function -* can round down if non-zero. -* -* Exit: -* Returns remainder. -* -* Exceptions: -* None. -* -***********************************************************************/ - -ULONG DecFixInt(LPDECIMAL pdecRes, LPDECIMAL pdecIn) -{ - ULONG rgulNum[3]; - ULONG ulRem; - ULONG ulPwr; - int iScale; - - if (pdecIn->u.u.scale > 0) { - rgulNum[0] = pdecIn->v.v.Lo32; - rgulNum[1] = pdecIn->v.v.Mid32; - rgulNum[2] = pdecIn->Hi32; - iScale = pdecIn->u.u.scale; - pdecRes->u.u.sign = pdecIn->u.u.sign; - ulRem = 0; - - do { - if (iScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iScale]; - - ulRem |= Div96By32(rgulNum, ulPwr); - iScale -= 9; - }while (iScale > 0); - - pdecRes->v.v.Lo32 = rgulNum[0]; - pdecRes->v.v.Mid32 = rgulNum[1]; - pdecRes->Hi32 = rgulNum[2]; - pdecRes->u.u.scale = 0; - - return ulRem; - } - - COPYDEC(*pdecRes, *pdecIn) - return 0; -} - - -//*********************************************************************** -// -// -// - -//********************************************************************** -// -// VarDecMul - Decimal Multiply -// -//********************************************************************** - -STDAPI VarDecMul(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes) -{ - SPLIT64 sdlTmp; - SPLIT64 sdlTmp2; - SPLIT64 sdlTmp3; - int iScale; - int iHiProd; - ULONG ulPwr; - ULONG ulRemLo; - ULONG ulRemHi; - ULONG rgulProd[6]; - - iScale = pdecL->u.u.scale + pdecR->u.u.scale; - - if ((pdecL->Hi32 | pdecL->v.v.Mid32 | pdecR->Hi32 | pdecR->v.v.Mid32) == 0) - { - // Upper 64 bits are zero. - // - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Lo32); - if (iScale > DEC_SCALE_MAX) - { - // Result iScale 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] - // - iScale -= DEC_SCALE_MAX; - if (iScale > 19) - { -ReturnZero: - DECIMAL_SETZERO(*pdecRes); - return NOERROR; - } - if (iScale > POWER10_MAX) - { - // Divide by 1E10 first, to get the power down to a 32-bit quantity. - // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now - // then multiply the next divisor by 4 (which will be a max of 4E9). - // - ulRemLo = FullDiv64By32(&sdlTmp.int64, ulTenToTenDiv4); - ulPwr = rgulPower10[iScale - 10] << 2; - } - else - { - ulPwr = rgulPower10[iScale]; - ulRemLo = 0; - } - - // Power to divide by fits in 32 bits. - // - ulRemHi = FullDiv64By32(&sdlTmp.int64, ulPwr); - - // Round result. See if remainder >= 1/2 of divisor. - // Divisor is a power of 10, so it is always even. - // - ulPwr >>= 1; - if (ulRemHi >= ulPwr && (ulRemHi > ulPwr || (ulRemLo | (sdlTmp.u.Lo & 1)))) - sdlTmp.int64++; - - iScale = DEC_SCALE_MAX; - } - DECIMAL_LO32(*pdecRes) = sdlTmp.u.Lo; - DECIMAL_MID32(*pdecRes) = sdlTmp.u.Hi; - DECIMAL_HI32(*pdecRes) = 0; - } - else - { - - // At least one operand has 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 - // - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Lo32); - rgulProd[0] = sdlTmp.u.Lo; - - sdlTmp2.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->v.v.Mid32) + sdlTmp.u.Hi; - - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->v.v.Lo32); - sdlTmp.int64 += sdlTmp2.int64; // this could generate carry - rgulProd[1] = sdlTmp.u.Lo; - if (sdlTmp.int64 < sdlTmp2.int64) // detect carry - sdlTmp2.u.Hi = 1; - else - sdlTmp2.u.Hi = 0; - sdlTmp2.u.Lo = sdlTmp.u.Hi; - - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->v.v.Mid32) + sdlTmp2.int64; - - if (pdecL->Hi32 | pdecR->Hi32) { - // Highest 32 bits is non-zero. Calculate 5 more partial products. - // - sdlTmp2.int64 = UInt32x32To64(pdecL->v.v.Lo32, pdecR->Hi32); - sdlTmp.int64 += sdlTmp2.int64; // this could generate carry - if (sdlTmp.int64 < sdlTmp2.int64) // detect carry - sdlTmp3.u.Hi = 1; - else - sdlTmp3.u.Hi = 0; - - sdlTmp2.int64 = UInt32x32To64(pdecL->Hi32, pdecR->v.v.Lo32); - sdlTmp.int64 += sdlTmp2.int64; // this could generate carry - rgulProd[2] = sdlTmp.u.Lo; - if (sdlTmp.int64 < sdlTmp2.int64) // detect carry - sdlTmp3.u.Hi++; - sdlTmp3.u.Lo = sdlTmp.u.Hi; - - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, pdecR->Hi32); - sdlTmp.int64 += sdlTmp3.int64; // this could generate carry - if (sdlTmp.int64 < sdlTmp3.int64) // detect carry - sdlTmp3.u.Hi = 1; - else - sdlTmp3.u.Hi = 0; - - sdlTmp2.int64 = UInt32x32To64(pdecL->Hi32, pdecR->v.v.Mid32); - sdlTmp.int64 += sdlTmp2.int64; // this could generate carry - rgulProd[3] = sdlTmp.u.Lo; - if (sdlTmp.int64 < sdlTmp2.int64) // detect carry - sdlTmp3.u.Hi++; - sdlTmp3.u.Lo = sdlTmp.u.Hi; - - sdlTmp.int64 = UInt32x32To64(pdecL->Hi32, pdecR->Hi32) + sdlTmp3.int64; - rgulProd[4] = sdlTmp.u.Lo; - rgulProd[5] = sdlTmp.u.Hi; - - iHiProd = 5; - } - else { - rgulProd[2] = sdlTmp.u.Lo; - rgulProd[3] = sdlTmp.u.Hi; - iHiProd = 3; - } - - // Check for leading zero ULONGs on the product - // - while (rgulProd[iHiProd] == 0) { - iHiProd--; - if (iHiProd < 0) - goto ReturnZero; - } - - iScale = ScaleResult(rgulProd, iHiProd, iScale); - if (iScale == -1) - return DISP_E_OVERFLOW; - - pdecRes->v.v.Lo32 = rgulProd[0]; - pdecRes->v.v.Mid32 = rgulProd[1]; - pdecRes->Hi32 = rgulProd[2]; - } - - pdecRes->u.u.sign = pdecR->u.u.sign ^ pdecL->u.u.sign; - pdecRes->u.u.scale = (char)iScale; - return NOERROR; -} - - -//********************************************************************** -// -// VarDecAdd - Decimal Addition -// VarDecSub - Decimal Subtraction -// -//********************************************************************** - -static HRESULT DecAddSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes, char bSign); - -STDAPI VarDecAdd(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes) -{ - return DecAddSub(pdecL, pdecR, pdecRes, 0); -} - - -STDAPI VarDecSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes) -{ - return DecAddSub(pdecL, pdecR, pdecRes, DECIMAL_NEG); -} - - -static HRESULT DecAddSub(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes, char bSign) -{ - ULONG rgulNum[6]; - ULONG ulPwr; - int iScale; - int iHiProd; - int iCur; - SPLIT64 sdlTmp; - DECIMAL decRes; - DECIMAL decTmp; - LPDECIMAL pdecTmp; - - bSign ^= (pdecR->u.u.sign ^ pdecL->u.u.sign) & DECIMAL_NEG; - - if (pdecR->u.u.scale == pdecL->u.u.scale) { - // Scale factors are equal, no alignment necessary. - // - decRes.u.signscale = pdecL->u.signscale; - -AlignedAdd: - if (bSign) { - // Signs differ - subtract - // - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecL) - DECIMAL_LO64_GET(*pdecR)); - DECIMAL_HI32(decRes) = DECIMAL_HI32(*pdecL) - DECIMAL_HI32(*pdecR); - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > DECIMAL_LO64_GET(*pdecL)) { - decRes.Hi32--; - if (decRes.Hi32 >= pdecL->Hi32) - goto SignFlip; - } - else if (decRes.Hi32 > pdecL->Hi32) { - // Got negative result. Flip its sign. - // -SignFlip: - DECIMAL_LO64_SET(decRes, -(LONGLONG)DECIMAL_LO64_GET(decRes)); - decRes.Hi32 = ~decRes.Hi32; - if (DECIMAL_LO64_GET(decRes) == 0) - decRes.Hi32++; - decRes.u.u.sign ^= DECIMAL_NEG; - } - - } - else { - // Signs are the same - add - // - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecL) + DECIMAL_LO64_GET(*pdecR)); - decRes.Hi32 = pdecL->Hi32 + pdecR->Hi32; - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < DECIMAL_LO64_GET(*pdecL)) { - decRes.Hi32++; - if (decRes.Hi32 <= pdecL->Hi32) - goto AlignedScale; - } - else if (decRes.Hi32 < pdecL->Hi32) { -AlignedScale: - // The addition carried above 96 bits. Divide the result by 10, - // dropping the scale factor. - // - if (decRes.u.u.scale == 0) - return DISP_E_OVERFLOW; - decRes.u.u.scale--; - - sdlTmp.u.Lo = decRes.Hi32; - sdlTmp.u.Hi = 1; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - decRes.Hi32 = sdlTmp.u.Lo; - - sdlTmp.u.Lo = decRes.v.v.Mid32; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - decRes.v.v.Mid32 = sdlTmp.u.Lo; - - sdlTmp.u.Lo = decRes.v.v.Lo32; - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10); - decRes.v.v.Lo32 = sdlTmp.u.Lo; - - // See if we need to round up. - // - if (sdlTmp.u.Hi >= 5 && (sdlTmp.u.Hi > 5 || (decRes.v.v.Lo32 & 1))) { - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(decRes)+1) - if (DECIMAL_LO64_GET(decRes) == 0) - decRes.Hi32++; - } - } - } - } - 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. - // - decRes.u.u.scale = pdecR->u.u.scale; // scale factor of "smaller" - decRes.u.u.sign = pdecL->u.u.sign; // but sign of "larger" - iScale = decRes.u.u.scale - pdecL->u.u.scale; - - if (iScale < 0) { - // Guessed scale factor wrong. Swap operands. - // - iScale = -iScale; - decRes.u.u.scale = pdecL->u.u.scale; - decRes.u.u.sign ^= bSign; - pdecTmp = pdecR; - pdecR = pdecL; - pdecL = pdecTmp; - } - - // *pdecL will need to be multiplied by 10^iScale so - // it will have the same scale as *pdecR. We could be - // extending it to up to 192 bits of precision. - // - if (iScale <= POWER10_MAX) { - // Scaling won't make it larger than 4 ULONGs - // - ulPwr = rgulPower10[iScale]; - DECIMAL_LO64_SET(decTmp, UInt32x32To64(pdecL->v.v.Lo32, ulPwr)); - sdlTmp.int64 = UInt32x32To64(pdecL->v.v.Mid32, ulPwr); - sdlTmp.int64 += decTmp.v.v.Mid32; - decTmp.v.v.Mid32 = sdlTmp.u.Lo; - decTmp.Hi32 = sdlTmp.u.Hi; - sdlTmp.int64 = UInt32x32To64(pdecL->Hi32, ulPwr); - sdlTmp.int64 += decTmp.Hi32; - if (sdlTmp.u.Hi == 0) { - // Result fits in 96 bits. Use standard aligned add. - // - decTmp.Hi32 = sdlTmp.u.Lo; - pdecL = &decTmp; - goto AlignedAdd; - } - rgulNum[0] = decTmp.v.v.Lo32; - rgulNum[1] = decTmp.v.v.Mid32; - rgulNum[2] = sdlTmp.u.Lo; - rgulNum[3] = sdlTmp.u.Hi; - iHiProd = 3; - } - else { - // Have to scale by a bunch. Move the number to a buffer - // where it has room to grow as it's scaled. - // - rgulNum[0] = pdecL->v.v.Lo32; - rgulNum[1] = pdecL->v.v.Mid32; - rgulNum[2] = pdecL->Hi32; - iHiProd = 2; - - // Scan for zeros in the upper words. - // - if (rgulNum[2] == 0) { - iHiProd = 1; - if (rgulNum[1] == 0) { - iHiProd = 0; - if (rgulNum[0] == 0) { - // Left arg is zero, return right. - // - DECIMAL_LO64_SET(decRes, DECIMAL_LO64_GET(*pdecR)); - decRes.Hi32 = pdecR->Hi32; - decRes.u.u.sign ^= bSign; - goto RetDec; - } - } - } - - // Scaling loop, up to 10^9 at a time. iHiProd stays updated - // with index of highest non-zero ULONG. - // - for (; iScale > 0; iScale -= POWER10_MAX) { - if (iScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iScale]; - - sdlTmp.u.Hi = 0; - for (iCur = 0; iCur <= iHiProd; iCur++) { - sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.u.Hi; - rgulNum[iCur] = sdlTmp.u.Lo; - } - - if (sdlTmp.u.Hi != 0) - // We're extending the result by another ULONG. - rgulNum[++iHiProd] = sdlTmp.u.Hi; - } - } - - // Scaling complete, do the add. Could be subtract if signs differ. - // - sdlTmp.u.Lo = rgulNum[0]; - sdlTmp.u.Hi = rgulNum[1]; - - if (bSign) { - // Signs differ, subtract. - // - DECIMAL_LO64_SET(decRes, sdlTmp.int64 - DECIMAL_LO64_GET(*pdecR)); - decRes.Hi32 = rgulNum[2] - pdecR->Hi32; - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) > sdlTmp.int64) { - decRes.Hi32--; - if (decRes.Hi32 >= rgulNum[2]) - goto LongSub; - } - else if (decRes.Hi32 > rgulNum[2]) { -LongSub: - // If rgulNum has more than 96 bits of precision, then we need to - // carry the subtraction into the higher bits. If it doesn't, - // then we subtracted in the wrong order and have to flip the - // sign of the result. - // - if (iHiProd <= 2) - goto SignFlip; - - iCur = 3; - while(rgulNum[iCur++]-- == 0); - if (rgulNum[iHiProd] == 0) - iHiProd--; - } - } - else { - // Signs the same, add. - // - DECIMAL_LO64_SET(decRes, sdlTmp.int64 + DECIMAL_LO64_GET(*pdecR)); - decRes.Hi32 = rgulNum[2] + pdecR->Hi32; - - // Propagate carry - // - if (DECIMAL_LO64_GET(decRes) < sdlTmp.int64) { - decRes.Hi32++; - if (decRes.Hi32 <= rgulNum[2]) - goto LongAdd; - } - else if (decRes.Hi32 < rgulNum[2]) { -LongAdd: - // Had a carry above 96 bits. - // - iCur = 3; - do { - if (iHiProd < iCur) { - rgulNum[iCur] = 1; - iHiProd = iCur; - break; - } - }while (++rgulNum[iCur++] == 0); - } - } - - if (iHiProd > 2) { - rgulNum[0] = decRes.v.v.Lo32; - rgulNum[1] = decRes.v.v.Mid32; - rgulNum[2] = decRes.Hi32; - decRes.u.u.scale = ScaleResult(rgulNum, iHiProd, decRes.u.u.scale); - if (decRes.u.u.scale == (BYTE) -1) - return DISP_E_OVERFLOW; - - decRes.v.v.Lo32 = rgulNum[0]; - decRes.v.v.Mid32 = rgulNum[1]; - decRes.Hi32 = rgulNum[2]; - } - } - -RetDec: - COPYDEC(*pdecRes, decRes) - return NOERROR; -} - - -//********************************************************************** -// -// VarDecDiv - Decimal Divide -// -//********************************************************************** - -STDAPI VarDecDiv(LPDECIMAL pdecL, LPDECIMAL pdecR, LPDECIMAL pdecRes) -{ - ULONG rgulQuo[3]; - ULONG rgulQuoSave[3]; - ULONG rgulRem[4]; - ULONG rgulDivisor[3]; - ULONG ulPwr; - ULONG ulTmp; - ULONG ulTmp1; - SPLIT64 sdlTmp; - SPLIT64 sdlDivisor; - int iScale; - int iCurScale; - - iScale = pdecL->u.u.scale - pdecR->u.u.scale; - rgulDivisor[0] = pdecR->v.v.Lo32; - rgulDivisor[1] = pdecR->v.v.Mid32; - rgulDivisor[2] = pdecR->Hi32; - - if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0) { - // Divisor is only 32 bits. Easy divide. - // - if (rgulDivisor[0] == 0) - return DISP_E_DIVBYZERO; - - rgulQuo[0] = pdecL->v.v.Lo32; - rgulQuo[1] = pdecL->v.v.Mid32; - rgulQuo[2] = pdecL->Hi32; - rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]); - - for (;;) { - if (rgulRem[0] == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale; - } - break; - } - - // 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 rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9 - // = 4.294 967 296. So the upper limit is rgulQuo[2] == 4 and - // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since - // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888 - // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is - // assured not to overflow. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - ulTmp = rgulRem[0] << 1; - if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] && - (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1)))) { -RoundUp: - if (++rgulQuo[0] == 0) - if (++rgulQuo[1] == 0) - rgulQuo[2]++; - } - break; - } - - if (iCurScale == -1) - return DISP_E_OVERFLOW; - -HaveScale: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) - return DISP_E_OVERFLOW; - - sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]); - rgulRem[0] = sdlTmp.u.Hi; - - rgulQuo[0] += sdlTmp.u.Lo; - if (rgulQuo[0] < sdlTmp.u.Lo) { - if (++rgulQuo[1] == 0) - rgulQuo[2]++; - } - } // for (;;) - } - else { - // Divisor has bits set in the upper 64 bits. - // - // Divisor must be fully normalized (shifted so bit 31 of the most - // significant ULONG 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. - // - if (rgulDivisor[2] == 0) - ulTmp = rgulDivisor[1]; - else - ulTmp = rgulDivisor[2]; - - iCurScale = 0; - if (!(ulTmp & 0xFFFF0000)) { - iCurScale += 16; - ulTmp <<= 16; - } - if (!(ulTmp & 0xFF000000)) { - iCurScale += 8; - ulTmp <<= 8; - } - if (!(ulTmp & 0xF0000000)) { - iCurScale += 4; - ulTmp <<= 4; - } - if (!(ulTmp & 0xC0000000)) { - iCurScale += 2; - ulTmp <<= 2; - } - if (!(ulTmp & 0x80000000)) { - iCurScale++; - ulTmp <<= 1; - } - - // Shift both dividend and divisor left by iCurScale. - // - sdlTmp.int64 = DECIMAL_LO64_GET(*pdecL) << iCurScale; - rgulRem[0] = sdlTmp.u.Lo; - rgulRem[1] = sdlTmp.u.Hi; - sdlTmp.u.Lo = pdecL->v.v.Mid32; - sdlTmp.u.Hi = pdecL->Hi32; - sdlTmp.int64 <<= iCurScale; - rgulRem[2] = sdlTmp.u.Hi; - rgulRem[3] = (pdecL->Hi32 >> (31 - iCurScale)) >> 1; - - sdlDivisor.u.Lo = rgulDivisor[0]; - sdlDivisor.u.Hi = rgulDivisor[1]; - sdlDivisor.int64 <<= iCurScale; - - if (rgulDivisor[2] == 0) { - // Have a 64-bit divisor in sdlDivisor. The remainder - // (currently 96 bits spread over 4 ULONGs) will be < divisor. - // - sdlTmp.u.Lo = rgulRem[2]; - sdlTmp.u.Hi = rgulRem[3]; - - rgulQuo[2] = 0; - rgulQuo[1] = Div96By64(&rgulRem[1], sdlDivisor); - rgulQuo[0] = Div96By64(rgulRem, sdlDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale64; - } - break; - } - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - sdlTmp.u.Lo = rgulRem[0]; - sdlTmp.u.Hi = rgulRem[1]; - if (sdlTmp.u.Hi >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 || - (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1))) - goto RoundUp; - break; - } - - if (iCurScale == -1) - return DISP_E_OVERFLOW; - -HaveScale64: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) - return DISP_E_OVERFLOW; - - rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96 - IncreaseScale(rgulRem, ulPwr); - ulTmp = Div96By64(rgulRem, sdlDivisor); - rgulQuo[0] += ulTmp; - if (rgulQuo[0] < ulTmp) - if (++rgulQuo[1] == 0) - rgulQuo[2]++; - - } // for (;;) - } - else { - // Have a 96-bit divisor in rgulDivisor[]. - // - // Start by finishing the shift left by iCurScale. - // - sdlTmp.u.Lo = rgulDivisor[1]; - sdlTmp.u.Hi = rgulDivisor[2]; - sdlTmp.int64 <<= iCurScale; - rgulDivisor[0] = sdlDivisor.u.Lo; - rgulDivisor[1] = sdlDivisor.u.Hi; - rgulDivisor[2] = sdlTmp.u.Hi; - - // The remainder (currently 96 bits spread over 4 ULONGs) - // will be < divisor. - // - rgulQuo[2] = 0; - rgulQuo[1] = 0; - rgulQuo[0] = Div128By96(rgulRem, rgulDivisor); - - for (;;) { - if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0) { - if (iScale < 0) { - iCurScale = min(9, -iScale); - goto HaveScale96; - } - break; - } - - // Remainder is non-zero. Scale up quotient and remainder by - // powers of 10 so we can compute more significant bits. - // - iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], iScale); - if (iCurScale == 0) { - // No more scaling to be done, but remainder is non-zero. - // Round quotient. - // - if (rgulRem[2] >= 0x80000000) - goto RoundUp; - - ulTmp = rgulRem[0] > 0x80000000; - ulTmp1 = rgulRem[1] > 0x80000000; - rgulRem[0] <<= 1; - rgulRem[1] = (rgulRem[1] << 1) + ulTmp; - rgulRem[2] = (rgulRem[2] << 1) + ulTmp1; - - if (rgulRem[2] > rgulDivisor[2] || (rgulRem[2] == rgulDivisor[2] && - (rgulRem[1] > rgulDivisor[1] || (rgulRem[1] == rgulDivisor[1] && - (rgulRem[0] > rgulDivisor[0] || (rgulRem[0] == rgulDivisor[0] && - (rgulQuo[0] & 1))))))) - goto RoundUp; - break; - } - - if (iCurScale == -1) - return DISP_E_OVERFLOW; - -HaveScale96: - ulPwr = rgulPower10[iCurScale]; - iScale += iCurScale; - - if (IncreaseScale(rgulQuo, ulPwr) != 0) - return DISP_E_OVERFLOW; - - rgulRem[3] = IncreaseScale(rgulRem, ulPwr); - ulTmp = Div128By96(rgulRem, rgulDivisor); - rgulQuo[0] += ulTmp; - if (rgulQuo[0] < ulTmp) - if (++rgulQuo[1] == 0) - rgulQuo[2]++; - - } // for (;;) - } - } - - // No more remainder. Try extracting any extra powers of 10 we may have - // added. We do this 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. If the remainder is not zero, then we restore - // the previous value. - // - // 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. - // - while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100000000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 8; - } - else - break; - } - - if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10000) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 4; - } - } - - if ((rgulQuo[0] & 3) == 0 && iScale >= 2) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 100) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 2; - } - } - - if ((rgulQuo[0] & 1) == 0 && iScale >= 1) { - rgulQuoSave[0] = rgulQuo[0]; - rgulQuoSave[1] = rgulQuo[1]; - rgulQuoSave[2] = rgulQuo[2]; - - if (Div96By32(rgulQuoSave, 10) == 0) { - rgulQuo[0] = rgulQuoSave[0]; - rgulQuo[1] = rgulQuoSave[1]; - rgulQuo[2] = rgulQuoSave[2]; - iScale -= 1; - } - } - - pdecRes->Hi32 = rgulQuo[2]; - pdecRes->v.v.Mid32 = rgulQuo[1]; - pdecRes->v.v.Lo32 = rgulQuo[0]; - pdecRes->u.u.scale = iScale; - pdecRes->u.u.sign = pdecL->u.u.sign ^ pdecR->u.u.sign; - return NOERROR; -} - - -//********************************************************************** -// -// VarDecAbs - Decimal Absolute Value -// -//********************************************************************** - -STDAPI VarDecAbs(LPDECIMAL pdecOprd, LPDECIMAL pdecRes) -{ - COPYDEC(*pdecRes, *pdecOprd) - pdecRes->u.u.sign &= ~DECIMAL_NEG; - return NOERROR; -} - - -//********************************************************************** -// -// VarDecFix - Decimal Fix (chop to integer) -// -//********************************************************************** - -STDAPI VarDecFix(LPDECIMAL pdecOprd, LPDECIMAL pdecRes) -{ - DecFixInt(pdecRes, pdecOprd); - return NOERROR; -} - - -//********************************************************************** -// -// VarDecInt - Decimal Int (round down to integer) -// -//********************************************************************** - -STDAPI VarDecInt(LPDECIMAL pdecOprd, LPDECIMAL pdecRes) -{ - if (DecFixInt(pdecRes, pdecOprd) != 0 && (pdecRes->u.u.sign & DECIMAL_NEG)) { - // We have chopped off a non-zero amount from a negative value. Since - // we round toward -infinity, we must increase the integer result by - // 1 to make it more negative. This will never overflow because - // in order to have a remainder, we must have had a non-zero scale factor. - // Our scale factor is back to zero now. - // - DECIMAL_LO64_SET(*pdecRes, DECIMAL_LO64_GET(*pdecRes) + 1); - if (DECIMAL_LO64_GET(*pdecRes) == 0) - pdecRes->Hi32++; - } - return NOERROR; -} - - -//********************************************************************** -// -// VarDecNeg - Decimal Negate -// -//********************************************************************** - -STDAPI VarDecNeg(LPDECIMAL pdecOprd, LPDECIMAL pdecRes) -{ - COPYDEC(*pdecRes, *pdecOprd) - pdecRes->u.u.sign ^= DECIMAL_NEG; - return NOERROR; -} - - -//********************************************************************** -// -// VarDecCmp - Decimal Compare -// -//********************************************************************** - -STDAPI VarDecCmp(LPDECIMAL pdecL, LPDECIMAL pdecR) -{ - ULONG ulSgnL; - ULONG ulSgnR; - - // First check signs and whether either are zero. If both are - // non-zero and of the same sign, just use subtraction to compare. - // - ulSgnL = pdecL->v.v.Lo32 | pdecL->v.v.Mid32 | pdecL->Hi32; - ulSgnR = pdecR->v.v.Lo32 | pdecR->v.v.Mid32 | pdecR->Hi32; - if (ulSgnL != 0) - ulSgnL = (pdecL->u.u.sign & DECIMAL_NEG) | 1; - - if (ulSgnR != 0) - ulSgnR = (pdecR->u.u.sign & DECIMAL_NEG) | 1; - - // ulSgnL & ulSgnR have values 1, 0, or 0x81 depending on if the left/right - // operand is +, 0, or -. - // - if (ulSgnL == ulSgnR) { - if (ulSgnL == 0) // both are zero - return VARCMP_EQ; // return equal - - DECIMAL decRes; - - DecAddSub(pdecL, pdecR, &decRes, DECIMAL_NEG); - if (DECIMAL_LO64_GET(decRes) == 0 && decRes.Hi32 == 0) - return VARCMP_EQ; - if (decRes.u.u.sign & DECIMAL_NEG) - return VARCMP_LT; - return VARCMP_GT; - } - - // Signs are different. Used signed byte compares - // - if ((char)ulSgnL > (char)ulSgnR) - return VARCMP_GT; - return VARCMP_LT; -} - -STDAPI VarDecRound(LPDECIMAL pdecIn, int cDecimals, LPDECIMAL pdecRes) -{ - ULONG rgulNum[3]; - ULONG ulRem; - ULONG ulSticky; - ULONG ulPwr; - int iScale; - - if (cDecimals < 0) - return E_INVALIDARG; - - iScale = pdecIn->u.u.scale - cDecimals; - if (iScale > 0) - { - rgulNum[0] = pdecIn->v.v.Lo32; - rgulNum[1] = pdecIn->v.v.Mid32; - rgulNum[2] = pdecIn->Hi32; - pdecRes->u.u.sign = pdecIn->u.u.sign; - ulRem = ulSticky = 0; - - do { - ulSticky |= ulRem; - if (iScale > POWER10_MAX) - ulPwr = ulTenToNine; - else - ulPwr = rgulPower10[iScale]; - - ulRem = Div96By32(rgulNum, ulPwr); - iScale -= 9; - }while (iScale > 0); - - // Now round. ulRem has last remainder, ulSticky has sticky bits. - // To do IEEE rounding, we add LSB of result to sticky bits so - // either causes round up if remainder * 2 == last divisor. - // - ulSticky |= rgulNum[0] & 1; - ulRem = (ulRem << 1) + (ulSticky != 0); - if (ulPwr < ulRem && - ++rgulNum[0] == 0 && - ++rgulNum[1] == 0 - ) - ++rgulNum[2]; - - pdecRes->v.v.Lo32 = rgulNum[0]; - pdecRes->v.v.Mid32 = rgulNum[1]; - pdecRes->Hi32 = rgulNum[2]; - pdecRes->u.u.scale = cDecimals; - return NOERROR; - } - - COPYDEC(*pdecRes, *pdecIn) - return NOERROR; -} diff --git a/src/palrt/decconv.cpp b/src/palrt/decconv.cpp deleted file mode 100644 index 9cb7575b04..0000000000 --- a/src/palrt/decconv.cpp +++ /dev/null @@ -1,602 +0,0 @@ -// 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. -// - -// -// =========================================================================== -// File: decconv.cpp -// -// =========================================================================== -/*** -* -*Purpose: -* This module contains the low level conversion for Decimal data type. -* -*Implementation Notes: -* -*****************************************************************************/ - -#include "common.h" -#include "convert.h" - -#include -#include -#include - -#define VALIDATEDECIMAL(dec) \ - if (DECIMAL_SCALE(dec) > DECMAX || (DECIMAL_SIGN(dec) & ~DECIMAL_NEG) != 0) \ - return E_INVALIDARG; - -#define RESULT(X) ((HRESULT)(X)) - -//*********************************************************************** -// -// Data tables -// - -const double dblPower10[] = { - 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 }; - -double fnDblPower10(int ix) -{ - const int maxIx = (sizeof(dblPower10)/sizeof(dblPower10[0])); - _ASSERTE(ix >= 0); - if (ix < maxIx) - return dblPower10[ix]; - return pow(10.0, ix); -} // double fnDblPower10() - -#define DBLBIAS 1022 -#define SNGBIAS 126 -#define DECMAX 28 - -const SPLIT64 sdlTenToEighteen = { UI64(1000000000000000000) }; -const DBLSTRUCT ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0); - -//*********************************************************************** -// -// Data tables -// - -const SPLIT64 sdlPower10[] = { {UI64(10000000000)}, // 1E10 - {UI64(100000000000)}, // 1E11 - {UI64(1000000000000)}, // 1E12 - {UI64(10000000000000)}, // 1E13 - {UI64(100000000000000)} }; // 1E14 - -const unsigned __int64 ulPower10[] = {1, - UI64(10), - UI64(100), - UI64(1000), - UI64(10000), - UI64(100000), - UI64(1000000), - UI64(10000000), - UI64(100000000), - UI64(1000000000), - UI64(10000000000), - UI64(100000000000), - UI64(1000000000000), - UI64(10000000000000), - UI64(100000000000000), - UI64(1000000000000000), - UI64(10000000000000000), - UI64(100000000000000000), - UI64(1000000000000000000), - UI64(10000000000000000000)}; - -DWORDLONG UInt64x64To128(SPLIT64 sdlOp1, SPLIT64 sdlOp2, DWORDLONG *pdlHi) -{ - SPLIT64 sdlTmp1; - SPLIT64 sdlTmp2; - SPLIT64 sdlTmp3; - - sdlTmp1.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Lo); // lo partial prod - sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Hi); // mid 1 partial prod - sdlTmp1.u.Hi += sdlTmp2.u.Lo; - if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry - sdlTmp2.u.Hi++; - sdlTmp3.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Hi) + (DWORDLONG)sdlTmp2.u.Hi; - sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Lo); - sdlTmp1.u.Hi += sdlTmp2.u.Lo; - if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry - sdlTmp2.u.Hi++; - sdlTmp3.int64 += (DWORDLONG)sdlTmp2.u.Hi; - - *pdlHi = sdlTmp3.int64; - return sdlTmp1.int64; -} - - -//*********************************************************************** -// -// Conversion to/from Decimal data type -// - - -STDAPI -VarDecFromR4(float fltIn, DECIMAL FAR* pdecOut) -{ - int iExp; // number of bits to left of binary point - int iPower; - ULONG ulMant; - double dbl; - SPLIT64 sdlLo; - SPLIT64 sdlHi; - int lmax, cur; // temps used during scale reduction - - // 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. - // - if ( (iExp = ((SNGSTRUCT *)&fltIn)->exp - SNGBIAS) < -94 ) - { - DECIMAL_SETZERO(*pdecOut); - return NOERROR; - } - - if (iExp > 96) - return RESULT(DISP_E_OVERFLOW); - - // 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. - // - dbl = fabs(fltIn); - iPower = 6 - ((iExp * 19728) >> 16); - - if (iPower >= 0) { - // We have less than 7 digits, scale input up. - // - if (iPower > DECMAX) - iPower = DECMAX; - - dbl = dbl * dblPower10[iPower]; - } - else { - if (iPower != -1 || dbl >= 1E7) - dbl = dbl / fnDblPower10(-iPower); - else - iPower = 0; // didn't scale it - } - - _ASSERTE(dbl < 1E7); - if (dbl < 1E6 && iPower < DECMAX) - { - dbl *= 10; - iPower++; - _ASSERTE(dbl >= 1E6); - } - - // Round to integer - // - ulMant = (LONG)dbl; - dbl -= (double)ulMant; // difference between input & integer - if ( dbl > 0.5 || (dbl == 0.5 && (ulMant & 1)) ) - ulMant++; - - if (ulMant == 0) - { - DECIMAL_SETZERO(*pdecOut); - return NOERROR; - } - - if (iPower < 0) { - // Add -iPower factors of 10, -iPower <= (29 - 7) = 22. - // - iPower = -iPower; - if (iPower < 10) { - sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower]); - - DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo; - DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi; - DECIMAL_HI32(*pdecOut) = 0; - } - else { - // Have a big power of 10. - // - if (iPower > 18) { - sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower - 18]); - sdlLo.int64 = UInt64x64To128(sdlLo, sdlTenToEighteen, &sdlHi.int64); - - if (sdlHi.u.Hi != 0) - return RESULT(DISP_E_OVERFLOW); - } - else { - sdlLo.int64 = UInt32x32To64(ulMant, (ULONG)ulPower10[iPower - 9]); - sdlHi.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Hi); - sdlLo.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Lo); - sdlHi.int64 += sdlLo.u.Hi; - sdlLo.u.Hi = sdlHi.u.Lo; - sdlHi.u.Lo = sdlHi.u.Hi; - } - DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo; - DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi; - DECIMAL_HI32(*pdecOut) = sdlHi.u.Lo; - } - DECIMAL_SCALE(*pdecOut) = 0; - } - 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. - // - // DivMod32by32 returns the quotient in Lo, the remainder in Hi. - // - lmax = min(iPower, 6); - - // lmax is the largest power of 10 to try, lmax <= 6. - // We'll try powers 4, 2, and 1 unless they're too big. - // - for (cur = 4; cur > 0; cur >>= 1) - { - if (cur > lmax) - continue; - - sdlLo.int64 = DivMod32by32(ulMant, (ULONG)ulPower10[cur]); - - if (sdlLo.u.Hi == 0) { - ulMant = sdlLo.u.Lo; - iPower -= cur; - lmax -= cur; - } - } - DECIMAL_LO32(*pdecOut) = ulMant; - DECIMAL_MID32(*pdecOut) = 0; - DECIMAL_HI32(*pdecOut) = 0; - DECIMAL_SCALE(*pdecOut) = iPower; - } - - DECIMAL_SIGN(*pdecOut) = (char)((SNGSTRUCT *)&fltIn)->sign << 7; - return NOERROR; -} - -STDAPI -VarDecFromR8(double dblIn, DECIMAL FAR* pdecOut) -{ - int iExp; // number of bits to left of binary point - int iPower; // power-of-10 scale factor - SPLIT64 sdlMant; - SPLIT64 sdlLo; - double dbl; - int lmax, cur; // temps used during scale reduction - ULONG ulPwrCur; - ULONG ulQuo; - - - // 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. - // - if ( (iExp = ((DBLSTRUCT *)&dblIn)->u.exp - DBLBIAS) < -94 ) - { - DECIMAL_SETZERO(*pdecOut); - return NOERROR; - } - - if (iExp > 96) - return RESULT(DISP_E_OVERFLOW); - - // 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. - // - dbl = fabs(dblIn); - iPower = 14 - ((iExp * 19728) >> 16); - - if (iPower >= 0) { - // We have less than 15 digits, scale input up. - // - if (iPower > DECMAX) - iPower = DECMAX; - - dbl = dbl * dblPower10[iPower]; - } - else { - if (iPower != -1 || dbl >= 1E15) - dbl = dbl / fnDblPower10(-iPower); - else - iPower = 0; // didn't scale it - } - - _ASSERTE(dbl < 1E15); - if (dbl < 1E14 && iPower < DECMAX) - { - dbl *= 10; - iPower++; - _ASSERTE(dbl >= 1E14); - } - - // Round to int64 - // - sdlMant.int64 = (LONGLONG)dbl; - dbl -= (double)(LONGLONG)sdlMant.int64; // dif between input & integer - if ( dbl > 0.5 || (dbl == 0.5 && (sdlMant.u.Lo & 1)) ) - sdlMant.int64++; - - if (sdlMant.int64 == 0) - { - DECIMAL_SETZERO(*pdecOut); - return NOERROR; - } - - if (iPower < 0) { - // Add -iPower factors of 10, -iPower <= (29 - 15) = 14. - // - iPower = -iPower; - if (iPower < 10) { - sdlLo.int64 = UInt32x32To64(sdlMant.u.Lo, (ULONG)ulPower10[iPower]); - sdlMant.int64 = UInt32x32To64(sdlMant.u.Hi, (ULONG)ulPower10[iPower]); - sdlMant.int64 += sdlLo.u.Hi; - sdlLo.u.Hi = sdlMant.u.Lo; - sdlMant.u.Lo = sdlMant.u.Hi; - } - else { - // Have a big power of 10. - // - _ASSERTE(iPower <= 14); - sdlLo.int64 = UInt64x64To128(sdlMant, sdlPower10[iPower-10], &sdlMant.int64); - - if (sdlMant.u.Hi != 0) - return RESULT(DISP_E_OVERFLOW); - } - DECIMAL_LO32(*pdecOut) = sdlLo.u.Lo; - DECIMAL_MID32(*pdecOut) = sdlLo.u.Hi; - DECIMAL_HI32(*pdecOut) = sdlMant.u.Lo; - DECIMAL_SCALE(*pdecOut) = 0; - } - 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. - // - // DivMod64by32 returns the quotient in Lo, the remainder in Hi. - // - lmax = min(iPower, 14); - - // lmax is the largest power of 10 to try, lmax <= 14. - // We'll try powers 8, 4, 2, and 1 unless they're too big. - // - for (cur = 8; cur > 0; cur >>= 1) - { - if (cur > lmax) - continue; - - ulPwrCur = (ULONG)ulPower10[cur]; - - if (sdlMant.u.Hi >= ulPwrCur) { - // Overflow if we try to divide in one step. - // - sdlLo.int64 = DivMod64by32(sdlMant.u.Hi, ulPwrCur); - ulQuo = sdlLo.u.Lo; - sdlLo.u.Lo = sdlMant.u.Lo; - sdlLo.int64 = DivMod64by32(sdlLo.int64, ulPwrCur); - } - else { - ulQuo = 0; - sdlLo.int64 = DivMod64by32(sdlMant.int64, ulPwrCur); - } - - if (sdlLo.u.Hi == 0) { - sdlMant.u.Hi = ulQuo; - sdlMant.u.Lo = sdlLo.u.Lo; - iPower -= cur; - lmax -= cur; - } - } - - DECIMAL_HI32(*pdecOut) = 0; - DECIMAL_SCALE(*pdecOut) = iPower; - DECIMAL_LO32(*pdecOut) = sdlMant.u.Lo; - DECIMAL_MID32(*pdecOut) = sdlMant.u.Hi; - } - - DECIMAL_SIGN(*pdecOut) = (char)((DBLSTRUCT *)&dblIn)->u.sign << 7; - return NOERROR; -} - -STDAPI -VarDecFromCy(CY cyIn, DECIMAL FAR* pdecOut) -{ - DECIMAL_SIGN(*pdecOut) = (UCHAR)((cyIn.u.Hi >> 24) & DECIMAL_NEG); - if (DECIMAL_SIGN(*pdecOut)) - cyIn.int64 = -cyIn.int64; - - DECIMAL_LO32(*pdecOut) = cyIn.u.Lo; - DECIMAL_MID32(*pdecOut) = cyIn.u.Hi; - DECIMAL_SCALE(*pdecOut) = 4; - DECIMAL_HI32(*pdecOut) = 0; - return NOERROR; -} - -STDAPI VarR4FromDec(DECIMAL FAR* pdecIn, float FAR* pfltOut) -{ - double dbl; - - VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check - - // Can't overflow; no errors possible. - // - VarR8FromDec(pdecIn, &dbl); - *pfltOut = (float)dbl; - return NOERROR; -} - -STDAPI VarR8FromDec(DECIMAL FAR* pdecIn, double FAR* pdblOut) -{ - SPLIT64 sdlTmp; - double dbl; - - VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check - - sdlTmp.u.Lo = DECIMAL_LO32(*pdecIn); - sdlTmp.u.Hi = DECIMAL_MID32(*pdecIn); - - if ( (LONG)DECIMAL_MID32(*pdecIn) < 0 ) - dbl = (ds2to64.dbl + (double)(LONGLONG)sdlTmp.int64 + - (double)DECIMAL_HI32(*pdecIn) * ds2to64.dbl) / fnDblPower10(DECIMAL_SCALE(*pdecIn)) ; - else - dbl = ((double)(LONGLONG)sdlTmp.int64 + - (double)DECIMAL_HI32(*pdecIn) * ds2to64.dbl) / fnDblPower10(DECIMAL_SCALE(*pdecIn)); - - if (DECIMAL_SIGN(*pdecIn)) - dbl = -dbl; - - *pdblOut = dbl; - return NOERROR; -} - -STDAPI VarCyFromDec(DECIMAL FAR* pdecIn, CY FAR* pcyOut) -{ - SPLIT64 sdlTmp; - SPLIT64 sdlTmp1; - int scale; - ULONG ulPwr; - ULONG ul; - - VALIDATEDECIMAL(*pdecIn); // E_INVALIDARG check - - scale = DECIMAL_SCALE(*pdecIn) - 4; // the power of 10 to divide by - - if (scale == 0) { - // No scaling needed -- the Decimal has 4 decimal places, - // just what Currency needs. - // - if ( DECIMAL_HI32(*pdecIn) != 0 || - (DECIMAL_MID32(*pdecIn) >= 0x80000000 && - (DECIMAL_MID32(*pdecIn) != 0x80000000 || DECIMAL_LO32(*pdecIn) != 0 || !DECIMAL_SIGN(*pdecIn))) ) - return RESULT(DISP_E_OVERFLOW); - - sdlTmp.u.Lo = DECIMAL_LO32(*pdecIn); - sdlTmp.u.Hi = DECIMAL_MID32(*pdecIn); - - if (DECIMAL_SIGN(*pdecIn)) - pcyOut->int64 = -(LONGLONG)sdlTmp.int64; - else - pcyOut->int64 = sdlTmp.int64; - return NOERROR; - } - - // Need to scale to get 4 decimal places. -4 <= scale <= 24. - // - if (scale < 0) { - sdlTmp1.int64 = UInt32x32To64((ULONG)ulPower10[-scale], DECIMAL_MID32(*pdecIn)); - sdlTmp.int64 = UInt32x32To64((ULONG)ulPower10[-scale], DECIMAL_LO32(*pdecIn)); - sdlTmp.u.Hi += sdlTmp1.u.Lo; - if (DECIMAL_HI32(*pdecIn) != 0 || sdlTmp1.u.Hi != 0 || sdlTmp1.u.Lo > sdlTmp.u.Hi) - return RESULT(DISP_E_OVERFLOW); - } - else if (scale < 10) { - // DivMod64by32 returns the quotient in Lo, the remainder in Hi. - // - ulPwr = (ULONG)ulPower10[scale]; - if (DECIMAL_HI32(*pdecIn) >= ulPwr) - return RESULT(DISP_E_OVERFLOW); - sdlTmp1.u.Lo = DECIMAL_MID32(*pdecIn); - sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn); - sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulPwr); - sdlTmp.u.Hi = sdlTmp1.u.Lo; // quotient to high half of result - sdlTmp1.u.Lo = DECIMAL_LO32(*pdecIn); // extended remainder - sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulPwr); - sdlTmp.u.Lo = sdlTmp1.u.Lo; // quotient to low half of result - - // Round result based on remainder in sdlTmp1.Hi. - // - ulPwr >>= 1; // compare to power/2 (power always even) - if (sdlTmp1.u.Hi > ulPwr || (sdlTmp1.u.Hi == ulPwr && (sdlTmp.u.Lo & 1))) - sdlTmp.int64++; - } - else { - // We have a power of 10 in the range 10 - 24. These powers do - // not fit in 32 bits. We'll handle this by scaling 2 or 3 times, - // first by 10^10, then by the remaining amount (or 10^9, then - // the last bit). - // - // To scale by 10^10, we'll actually divide by 10^10/4, which fits - // in 32 bits. The second scaling is multiplied by four - // to account for it, just barely assured of fitting in 32 bits - // (4E9 < 2^32). Note that the upper third of the quotient is - // either zero or one, so we skip the divide step to calculate it. - // (Max 4E9 divided by 2.5E9.) - // - // DivMod64by32 returns the quotient in Lo, the remainder in Hi. - // - if (DECIMAL_HI32(*pdecIn) >= ulTenToTenDiv4) { - sdlTmp.u.Hi = 1; // upper 1st quotient - sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn) - ulTenToTenDiv4; // remainder - } - else { - sdlTmp.u.Hi = 0; // upper 1st quotient - sdlTmp1.u.Hi = DECIMAL_HI32(*pdecIn); // remainder - } - sdlTmp1.u.Lo = DECIMAL_MID32(*pdecIn); // extended remainder - sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulTenToTenDiv4); - sdlTmp.u.Lo = sdlTmp1.u.Lo; // middle 1st quotient - - sdlTmp1.u.Lo = DECIMAL_LO32(*pdecIn); // extended remainder - sdlTmp1.int64 = DivMod64by32(sdlTmp1.int64, ulTenToTenDiv4); - - ulPwr = (ULONG)(ulPower10[min(scale-10, 9)] << 2); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr); - ul = sdlTmp.u.Lo; // upper 2nd quotient - - sdlTmp.u.Lo = sdlTmp1.u.Lo; // extended remainder - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr); - sdlTmp1.u.Lo = sdlTmp.u.Hi; // save final remainder - sdlTmp.u.Hi = ul; // position high result - - if (scale >= 20) { - ulPwr = (ULONG)(ulPower10[scale-19]); - sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr); - sdlTmp1.u.Hi |= sdlTmp1.u.Lo; // combine sticky bits - sdlTmp1.u.Lo = sdlTmp.u.Hi; // final remainder - sdlTmp.u.Hi = 0; // guaranteed result fits in 32 bits - } - - // Round result based on remainder in sdlTmp1.Lo. sdlTmp1.Hi is - // the remainder from the first division(s), representing sticky bits. - // Current result is in sdlTmp. - // - ulPwr >>= 1; // compare to power/2 (power always even) - if (sdlTmp1.u.Lo > ulPwr || (sdlTmp1.u.Lo == ulPwr && - ((sdlTmp.u.Lo & 1) || sdlTmp1.u.Hi != 0))) - sdlTmp.int64++; - } - - if (sdlTmp.u.Hi >= 0x80000000 && - (sdlTmp.int64 != UI64(0x8000000000000000) || !DECIMAL_SIGN(*pdecIn))) - return RESULT(DISP_E_OVERFLOW); - - if (DECIMAL_SIGN(*pdecIn)) - sdlTmp.int64 = -(LONGLONG)sdlTmp.int64; - - pcyOut->int64 = sdlTmp.int64; - return NOERROR; -} - - diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 8ee51b455b..25bc85abce 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -740,7 +740,6 @@ FCFuncEnd() FCFuncStart(gNumberFuncs) FCFuncElement("DoubleToNumber", COMNumber::DoubleToNumberFC) FCFuncElement("NumberToDouble", COMNumber::NumberToDoubleFC) - FCFuncElement("NumberBufferToDecimal", COMNumber::NumberBufferToDecimal) FCFuncEnd() #ifdef FEATURE_COMINTEROP @@ -760,26 +759,6 @@ FCFuncStart(gOAVariantFuncs) FCFuncEnd() #endif // FEATURE_COMINTEROP -FCFuncStart(gDecimalFuncs) - FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_Flt_RetVoid, COMDecimal::InitSingle) - FCFuncElementSig(COR_CTOR_METHOD_NAME, &gsig_IM_Dbl_RetVoid, COMDecimal::InitDouble) - FCFuncElement("FCallAddSub", COMDecimal::DoAddSubThrow) - FCFuncElement("FCallMultiply", COMDecimal::DoMultiplyThrow) - FCFuncElement("FCallDivide", COMDecimal::DoDivideThrow) - FCFuncElement("FCallCompare", COMDecimal::DoCompare) - FCFuncElement("FCallFloor", COMDecimal::DoFloor) - FCFuncElement("FCallRound", COMDecimal::DoRound) - FCFuncElement("FCallToCurrency", COMDecimal::DoToCurrency) - FCFuncElement("FCallToInt32", COMDecimal::ToInt32) - FCFuncElement("ToDouble", COMDecimal::ToDouble) - FCFuncElement("ToSingle", COMDecimal::ToSingle) - FCFuncElement("FCallTruncate", COMDecimal::DoTruncate) -FCFuncEnd() - -FCFuncStart(gCurrencyFuncs) - FCFuncElement("FCallToDecimal", COMCurrency::DoToDecimal) -FCFuncEnd() - FCFuncStart(gClrConfig) QCFuncElement("GetConfigBoolValue", ClrConfigNative::GetConfigBoolValue) FCFuncEnd() @@ -1122,7 +1101,6 @@ FCFuncStart(gStubHelperFuncs) FCFuncElement("ProfilerEndTransitionCallback", StubHelpers::ProfilerEndTransitionCallback) #endif FCFuncElement("CreateCustomMarshalerHelper", StubHelpers::CreateCustomMarshalerHelper) - FCFuncElement("DecimalCanonicalizeInternal", StubHelpers::DecimalCanonicalizeInternal) FCFuncElement("FmtClassUpdateNativeInternal", StubHelpers::FmtClassUpdateNativeInternal) FCFuncElement("FmtClassUpdateCLRInternal", StubHelpers::FmtClassUpdateCLRInternal) FCFuncElement("LayoutDestroyNativeInternal", StubHelpers::LayoutDestroyNativeInternal) @@ -1269,13 +1247,11 @@ FCClassElement("Buffer", "System", gBufferFuncs) FCClassElement("CLRConfig", "System", gClrConfig) FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs) FCClassElement("CriticalHandle", "System.Runtime.InteropServices", gCriticalHandleFuncs) -FCClassElement("Currency", "System", gCurrencyFuncs) FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) FCClassElement("DateMarshaler", "System.StubHelpers", gDateMarshalerFuncs) FCClassElement("DateTime", "System", gDateTimeFuncs) FCClassElement("Debugger", "System.Diagnostics", gDiagnosticsDebugger) -FCClassElement("Decimal", "System", gDecimalFuncs) FCClassElement("DefaultBinder", "System", gCOMDefaultBinderFuncs) FCClassElement("Delegate", "System", gDelegateFuncs) FCClassElement("DependentHandle", "System.Runtime.CompilerServices", gDependentHandleFuncs) diff --git a/src/vm/fieldmarshaler.cpp b/src/vm/fieldmarshaler.cpp index 1015657bac..50f12a838d 100644 --- a/src/vm/fieldmarshaler.cpp +++ b/src/vm/fieldmarshaler.cpp @@ -4200,12 +4200,7 @@ VOID FieldMarshaler_Currency::ScalarUpdateCLRImpl(const VOID *pNative, LPVOID pC // no need to switch to preemptive mode because it's very primitive operaion, doesn't take // long and is guaranteed not to call 3rd party code. // But if we do need to switch to preemptive mode, we can't pass the managed pointer to native code directly - HRESULT hr = VarDecFromCy( *(CURRENCY*)pNative, (DECIMAL *)pCLR ); - if (FAILED(hr)) - COMPlusThrowHR(hr); - - if (FAILED(DecimalCanonicalize((DECIMAL*)pCLR))) - COMPlusThrow(kOverflowException, W("Overflow_Currency")); + VarDecFromCyCanonicalize( *(CURRENCY*)pNative, (DECIMAL *)pCLR ); } VOID FieldMarshaler_DateTimeOffset::ScalarUpdateNativeImpl(LPVOID pCLR, LPVOID pNative) const diff --git a/src/vm/ilmarshalers.cpp b/src/vm/ilmarshalers.cpp index a72276ef4a..11b3e0b8d6 100644 --- a/src/vm/ilmarshalers.cpp +++ b/src/vm/ilmarshalers.cpp @@ -1332,11 +1332,6 @@ void ILCurrencyMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit EmitLoadNativeValue(pslILEmit); pslILEmit->EmitCALL(METHOD__DECIMAL__CURRENCY_CTOR, 2, 0); - - EmitLoadManagedHomeAddr(pslILEmit); - - // static void System.StubHelpers.DecimalCanonicalizeInternal(ref Decimal dec); - pslILEmit->EmitCALL(METHOD__STUBHELPERS__DECIMAL_CANONICALIZE_INTERNAL, 1, 0); } diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp index 6cd93ad80a..a526ebd35c 100644 --- a/src/vm/mscorlib.cpp +++ b/src/vm/mscorlib.cpp @@ -41,8 +41,6 @@ #include "comsynchronizable.h" #include "floatdouble.h" #include "floatsingle.h" -#include "decimal.h" -#include "currency.h" #include "comdatetime.h" #include "number.h" #include "compatibilityswitch.h" diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h index c57290ff12..d6caea23a7 100644 --- a/src/vm/mscorlib.h +++ b/src/vm/mscorlib.h @@ -1041,7 +1041,6 @@ DEFINE_METHOD(STUBHELPERS, GET_HR_EXCEPTION_OBJECT, GetHRExceptionObjec DEFINE_METHOD(STUBHELPERS, CREATE_CUSTOM_MARSHALER_HELPER, CreateCustomMarshalerHelper, SM_IntPtr_Int_IntPtr_RetIntPtr) DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, SM_Int_RetVoid) -DEFINE_METHOD(STUBHELPERS, DECIMAL_CANONICALIZE_INTERNAL, DecimalCanonicalizeInternal, SM_RefDec_RetVoid) DEFINE_METHOD(STUBHELPERS, FMT_CLASS_UPDATE_NATIVE_INTERNAL, FmtClassUpdateNativeInternal, SM_Obj_PtrByte_RefCleanupWorkList_RetVoid) DEFINE_METHOD(STUBHELPERS, FMT_CLASS_UPDATE_CLR_INTERNAL, FmtClassUpdateCLRInternal, SM_Obj_PtrByte_RetVoid) diff --git a/src/vm/olevariant.cpp b/src/vm/olevariant.cpp index 4f62be6bdf..330710bf37 100644 --- a/src/vm/olevariant.cpp +++ b/src/vm/olevariant.cpp @@ -3770,11 +3770,7 @@ void OleVariant::MarshalCurrencyVariantOleToCom(VARIANT *pOleVariant, DECIMAL DecVal; // Convert the currency to a decimal. - HRESULT hr = VarDecFromCy(V_CY(pOleVariant), &DecVal); - IfFailThrow(hr); - - if (FAILED(DecimalCanonicalize(&DecVal))) - COMPlusThrow(kOverflowException, W("Overflow_Currency")); + VarDecFromCyCanonicalize(V_CY(pOleVariant), &DecVal); // Store the value into the unboxes decimal and store the decimal in the variant. *(DECIMAL *) pDecimalRef->UnBox() = DecVal; @@ -3822,11 +3818,7 @@ void OleVariant::MarshalCurrencyVariantOleRefToCom(VARIANT *pOleVariant, DECIMAL DecVal; // Convert the currency to a decimal. - HRESULT hr = VarDecFromCy(*V_CYREF(pOleVariant), &DecVal); - IfFailThrow(hr); - - if (FAILED(DecimalCanonicalize(&DecVal))) - COMPlusThrow(kOverflowException, W("Overflow_Currency")); + VarDecFromCyCanonicalize(*V_CYREF(pOleVariant), &DecVal); // Store the value into the unboxes decimal and store the decimal in the variant. *(DECIMAL *) pDecimalRef->UnBox() = DecVal; @@ -3856,11 +3848,7 @@ void OleVariant::MarshalCurrencyArrayOleToCom(void *oleArray, BASEARRAYREF *pCom while (pOle < pOleEnd) { - IfFailThrow(VarDecFromCy(*pOle++, pCom)); - if (FAILED(DecimalCanonicalize(pCom))) - COMPlusThrow(kOverflowException, W("Overflow_Currency")); - - pCom++; + VarDecFromCyCanonicalize(*pOle++, pCom++); } } diff --git a/src/vm/stubhelpers.cpp b/src/vm/stubhelpers.cpp index 7475d98061..0be8be33a6 100644 --- a/src/vm/stubhelpers.cpp +++ b/src/vm/stubhelpers.cpp @@ -1696,17 +1696,6 @@ FCIMPL1(Object*, StubHelpers::AllocateInternal, EnregisteredTypeHandle pRegister } FCIMPLEND -FCIMPL1(void, StubHelpers::DecimalCanonicalizeInternal, DECIMAL *pDec) -{ - FCALL_CONTRACT; - - if (FAILED(DecimalCanonicalize(pDec))) - { - FCThrowResVoid(kOverflowException, W("Overflow_Currency")); - } -} -FCIMPLEND - FCIMPL1(int, StubHelpers::AnsiStrlen, __in_z char* pszStr) { FCALL_CONTRACT; diff --git a/src/vm/stubhelpers.h b/src/vm/stubhelpers.h index 560e0b3e9b..e5376f73c3 100644 --- a/src/vm/stubhelpers.h +++ b/src/vm/stubhelpers.h @@ -99,7 +99,6 @@ public: static FCDECL3(void*, CreateCustomMarshalerHelper, MethodDesc* pMD, mdToken paramToken, TypeHandle hndManagedType); - static FCDECL1(void, DecimalCanonicalizeInternal, DECIMAL *pDec); static FCDECL3(void, FmtClassUpdateNativeInternal, Object* pObjUNSAFE, BYTE* pbNative, OBJECTREF *ppCleanupWorkListOnStack); static FCDECL2(void, FmtClassUpdateCLRInternal, Object* pObjUNSAFE, BYTE* pbNative); static FCDECL2(void, LayoutDestroyNativeInternal, BYTE* pbNative, MethodTable* pMT); -- cgit v1.2.3