diff options
author | Pent Ploompuu <kaalikas@gmail.com> | 2018-07-17 18:41:39 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2018-07-17 08:41:39 -0700 |
commit | 2b50bba8131acca2ab535e144796941ad93487b7 (patch) | |
tree | 5c3f9901749fceb1aa1e14c67665a76b5a9ee408 | |
parent | 624f72d55a92e49aef3c3cd6e69150fa3b085fac (diff) | |
download | coreclr-2b50bba8131acca2ab535e144796941ad93487b7.tar.gz coreclr-2b50bba8131acca2ab535e144796941ad93487b7.tar.bz2 coreclr-2b50bba8131acca2ab535e144796941ad93487b7.zip |
Move Decimal to shared (#18948)
* Move Decimal to shared
* Remove DecimalCanonicalize{Internal}
28 files changed, 2965 insertions, 5158 deletions
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 @@ <Compile Include="$(BclSourcesRoot)\System\BadImageFormatException.CoreCLR.cs" /> <Compile Include="$(BclSourcesRoot)\System\Buffer.cs" /> <Compile Include="$(BclSourcesRoot)\System\Currency.cs" /> - <Compile Include="$(BclSourcesRoot)\System\Decimal.cs" /> - <Compile Include="$(BclSourcesRoot)\System\Decimal.DecCalc.cs" /> <Compile Include="$(BclSourcesRoot)\System\DefaultBinder.CanConvert.cs" /> <Compile Include="$(BclSourcesRoot)\System\Enum.cs" /> <Compile Include="$(BclSourcesRoot)\System\Environment.cs" /> 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 @@ <Compile Include="$(MSBuildThisFileDirectory)System\DateTimeOffset.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DayOfWeek.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DBNull.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Decimal.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Decimal.DecCalc.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\DefaultBinder.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\CodeAnalysis\SuppressMessageAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\ConditionalAttribute.cs" /> 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<int, ulong>(ref Unsafe.AsRef(in lo)); +#endif + + private static ref DecCalc AsMutable(ref decimal d) => ref Unsafe.As<decimal, DecCalc>(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 + + /// <summary> + /// Class that contains all the mathematical calculations for decimal. Most of which have been ported from oleaut32. + /// </summary> + [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; + + /// <summary> + /// The low and mid fields combined in little-endian order + /// </summary> + [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 + // ( <dividend scale> - <divisor 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<DecCalc, decimal>(ref d1), in Unsafe.As<DecCalc, decimal>(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<DecCalc, decimal>(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 + } + + /// <summary> + /// U1-U2 combined (overlaps with Low64) + /// </summary> + 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/src/System/Decimal.cs b/src/System.Private.CoreLib/shared/System/Decimal.cs index 0a37664aed..a79a7c3e7f 100644 --- a/src/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/System.Private.CoreLib/shared/System/Decimal.cs @@ -2,15 +2,11 @@ // 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.Diagnostics; using System.Globalization; -using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.Versioning; +using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Diagnostics; namespace System { @@ -60,7 +56,7 @@ namespace System [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<decimal>, IEquatable<decimal>, IDeserializationCallback, ISpanFormattable + public readonly partial struct Decimal : IFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal>, 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 @@ -69,8 +65,6 @@ namespace System // 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 @@ -80,23 +74,6 @@ namespace System // 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; @@ -114,15 +91,6 @@ namespace System // 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 @@ -135,22 +103,10 @@ namespace System // 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; - //} + private readonly int flags; + private readonly int hi; + private readonly int lo; + private readonly int mid; // Constructs a Decimal from an integer value. // @@ -212,33 +168,60 @@ namespace System // Constructs a Decimal from a float value. // - [MethodImplAttribute(MethodImplOptions.InternalCall)] - public extern Decimal(float value); + public Decimal(float value) + { + DecCalc.VarDecFromR4(value, out AsMutable(ref this)); + } // 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) + public Decimal(double value) { - this = Currency.ToDecimal(value); + DecCalc.VarDecFromR8(value, out AsMutable(ref this)); } - // 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) + // + // 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) { - return new Currency(value).ToOACurrency(); + 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 decimal FromOACurrency(long cy) + public static long ToOACurrency(decimal value) { - return Currency.ToDecimal(Currency.FromOACurrency(cy)); + 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 @@ -266,7 +249,7 @@ namespace System if (bits.Length == 4) { int f = bits[3]; - if ((f & ~(SignMask | ScaleMask)) == 0 && (f & ScaleMask) <= (28 << 16)) + if (IsValid(f)) { lo = bits[0]; mid = bits[1]; @@ -292,40 +275,18 @@ namespace System 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); - } + 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 ((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)) + if (IsValid(flags)) { this.lo = lo; this.mid = mid; @@ -336,39 +297,38 @@ namespace System 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(d.lo, d.mid, d.hi, d.flags & ~SignMask); + return new decimal(in d, d.flags & ~SignMask); } // Adds two Decimal values. // public static decimal Add(decimal d1, decimal d2) { - FCallAddSub(ref d1, ref d2, DECIMAL_ADD); + DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), false); 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))); + 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 @@ -376,12 +336,9 @@ namespace System // public static int Compare(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2); + return DecCalc.VarDecCmp(in d1, in 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 @@ -396,29 +353,22 @@ namespace System throw new ArgumentException(SR.Arg_MustBeDecimal); decimal other = (decimal)value; - return FCallCompare(ref this, ref other); + return DecCalc.VarDecCmp(in this, in other); } public int CompareTo(decimal value) { - return FCallCompare(ref this, ref value); + return DecCalc.VarDecCmp(in this, in value); } // Divides two Decimal values. // public static decimal Divide(decimal d1, decimal d2) { - FCallDivide(ref d1, ref d2); + DecCalc.VarDecDiv(ref AsMutable(ref d1), ref AsMutable(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. @@ -428,26 +378,26 @@ namespace System if (value is decimal) { decimal other = (decimal)value; - return FCallCompare(ref this, ref other) == 0; + return DecCalc.VarDecCmp(in this, in other) == 0; } return false; } public bool Equals(decimal value) { - return FCallCompare(ref this, ref value) == 0; + return DecCalc.VarDecCmp(in this, in value) == 0; } // Returns the hash code for this Decimal. // - public override int GetHashCode() => GetHashCode(ref this); + 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 FCallCompare(ref d1, ref d2) == 0; + return DecCalc.VarDecCmp(in d1, in d2) == 0; } // Rounds a Decimal to an integer value. The Decimal argument is rounded @@ -455,13 +405,12 @@ namespace System // public static decimal Floor(decimal d) { - FCallFloor(ref d); + int flags = d.flags; + if ((flags & ScaleMask) != 0) + DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), DecCalc.RoundingMode.Floor); 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 @@ -615,171 +564,40 @@ namespace System 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) + internal static ref decimal Max(ref decimal d1, ref decimal d2) { - return FCallCompare(ref d1, ref d2) >= 0 ? d1 : d2; + return ref DecCalc.VarDecCmp(in d1, in d2) >= 0 ? ref d1 : ref d2; } // Returns the smaller of two Decimal values. // - internal static decimal Min(ref decimal d1, ref decimal d2) + internal static ref decimal Min(ref decimal d1, ref decimal d2) { - return FCallCompare(ref d1, ref d2) < 0 ? d1 : d2; + return ref DecCalc.VarDecCmp(in d1, in d2) < 0 ? ref d1 : ref 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; + DecCalc.VarDecMod(ref AsMutable(ref d1), ref AsMutable(ref d2)); + return d1; } // Multiplies two Decimal values. // public static decimal Multiply(decimal d1, decimal d2) { - FCallMultiply(ref d1, ref d2); + DecCalc.VarDecMul(ref AsMutable(ref d1), ref AsMutable(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); + return new decimal(in d, d.flags ^ SignMask); } // Rounds a Decimal value to a given number of decimal places. The value @@ -790,52 +608,31 @@ namespace System // 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) => 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); - public static decimal Round(decimal d, int decimals) + private static decimal Round(ref decimal d, int decimals, MidpointRounding mode) { - 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)) + if ((uint)decimals > 28) throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound); - if (mode < MidpointRounding.ToEven || mode > MidpointRounding.AwayFromZero) - { + if ((uint)mode > (uint)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); - } + int scale = d.Scale - decimals; + if (scale > 0) + DecCalc.InternalRound(ref AsMutable(ref d), (uint)scale, (DecCalc.RoundingMode)mode); 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); + DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), true); return d1; } @@ -854,7 +651,7 @@ namespace System { throw new OverflowException(SR.Overflow_Byte, e); } - if (temp < byte.MinValue || temp > byte.MaxValue) throw new OverflowException(SR.Overflow_Byte); + if (temp != (byte)temp) throw new OverflowException(SR.Overflow_Byte); return (byte)temp; } @@ -874,7 +671,7 @@ namespace System { throw new OverflowException(SR.Overflow_SByte, e); } - if (temp < sbyte.MinValue || temp > sbyte.MaxValue) throw new OverflowException(SR.Overflow_SByte); + if (temp != (sbyte)temp) throw new OverflowException(SR.Overflow_SByte); return (sbyte)temp; } @@ -893,33 +690,17 @@ namespace System { throw new OverflowException(SR.Overflow_Int16, e); } - if (temp < short.MinValue || temp > short.MaxValue) throw new OverflowException(SR.Overflow_Int16); + if (temp != (short)temp) 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); + 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 @@ -927,11 +708,11 @@ namespace System // public static int ToInt32(decimal d) { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0 && d.mid == 0) + Truncate(ref d); + if ((d.hi | d.mid) == 0) { int i = d.lo; - if (d.flags >= 0) + if (!d.IsNegative) { if (i >= 0) return i; } @@ -950,11 +731,11 @@ namespace System // public static long ToInt64(decimal d) { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); + Truncate(ref d); if (d.hi == 0) { - long l = d.lo & 0xFFFFFFFFL | (long)d.mid << 32; - if (d.flags >= 0) + long l = (long)d.Low64; + if (!d.IsNegative) { if (l >= 0) return l; } @@ -983,7 +764,7 @@ namespace System { throw new OverflowException(SR.Overflow_UInt16, e); } - if (temp < ushort.MinValue || temp > ushort.MaxValue) throw new OverflowException(SR.Overflow_UInt16); + if (temp != (ushort)temp) throw new OverflowException(SR.Overflow_UInt16); return (ushort)temp; } @@ -994,11 +775,11 @@ namespace System [CLSCompliant(false)] public static uint ToUInt32(decimal d) { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); - if (d.hi == 0 && d.mid == 0) + Truncate(ref d); + if ((d.hi | d.mid) == 0) { - uint i = (uint)d.lo; - if (d.flags >= 0 || i == 0) + uint i = d.Low; + if (!d.IsNegative || i == 0) return i; } throw new OverflowException(SR.Overflow_UInt32); @@ -1011,11 +792,11 @@ namespace System [CLSCompliant(false)] public static ulong ToUInt64(decimal d) { - if ((d.flags & ScaleMask) != 0) FCallTruncate(ref d); + Truncate(ref d); if (d.hi == 0) { - ulong l = ((ulong)(uint)d.lo) | ((ulong)(uint)d.mid << 32); - if (d.flags >= 0 || l == 0) + ulong l = d.Low64; + if (!d.IsNegative || l == 0) return l; } throw new OverflowException(SR.Overflow_UInt64); @@ -1024,8 +805,10 @@ namespace System // 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); + 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 @@ -1033,14 +816,17 @@ namespace System // public static decimal Truncate(decimal d) { - FCallTruncate(ref d); + Truncate(ref d); return d; } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void FCallTruncate(ref decimal 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) { @@ -1192,26 +978,22 @@ namespace System public static decimal operator +(decimal d1, decimal d2) { - FCallAddSub(ref d1, ref d2, DECIMAL_ADD); - return d1; + return Add(d1, d2); } public static decimal operator -(decimal d1, decimal d2) { - FCallAddSub(ref d1, ref d2, DECIMAL_NEG); - return d1; + return Subtract(d1, d2); } public static decimal operator *(decimal d1, decimal d2) { - FCallMultiply(ref d1, ref d2); - return d1; + return Multiply(d1, d2); } public static decimal operator /(decimal d1, decimal d2) { - FCallDivide(ref d1, ref d2); - return d1; + return Divide(d1, d2); } public static decimal operator %(decimal d1, decimal d2) @@ -1221,32 +1003,32 @@ namespace System public static bool operator ==(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) == 0; + return DecCalc.VarDecCmp(in d1, in d2) == 0; } public static bool operator !=(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) != 0; + return DecCalc.VarDecCmp(in d1, in d2) != 0; } public static bool operator <(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) < 0; + return DecCalc.VarDecCmp(in d1, in d2) < 0; } public static bool operator <=(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) <= 0; + return DecCalc.VarDecCmp(in d1, in d2) <= 0; } public static bool operator >(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) > 0; + return DecCalc.VarDecCmp(in d1, in d2) > 0; } public static bool operator >=(decimal d1, decimal d2) { - return FCallCompare(ref d1, ref d2) >= 0; + return DecCalc.VarDecCmp(in d1, in d2) >= 0; } // 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<char> 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<int, uint>(ref value.hi)), - ref Unsafe.As<int, uint>(ref value.mid)), - ref Unsafe.As<int, uint>(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/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 @@ -1695,9 +1695,6 @@ namespace System.StubHelpers 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)] internal static extern unsafe void FmtClassUpdateCLRInternal(object obj, byte* pNative); 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 <oleauto.h> -#include <pshpack1.h> - -class COMCurrency -{ -public: - static FCDECL2_IV(void, DoToDecimal, DECIMAL * result, CY c); -}; - -#include <poppack.h> - -#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<BYTE>(-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 - // ( <dividend scale> - <divisor 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 - // ( <dividend scale> - <divisor 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 <oleauto.h> - -#include <pshpack1.h> - -#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 <poppack.h> - -#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<LARGE_BUFFER_SIZE * sizeof(WCHAR)> 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 <oleauto.h> -#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 - // ( <dividend scale> - <divisor 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 <oleauto.h> -#include <math.h> -#include <limits.h> - -#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); |