summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPent Ploompuu <kaalikas@gmail.com>2018-07-17 18:41:39 +0300
committerJan Kotas <jkotas@microsoft.com>2018-07-17 08:41:39 -0700
commit2b50bba8131acca2ab535e144796941ad93487b7 (patch)
tree5c3f9901749fceb1aa1e14c67665a76b5a9ee408
parent624f72d55a92e49aef3c3cd6e69150fa3b085fac (diff)
downloadcoreclr-2b50bba8131acca2ab535e144796941ad93487b7.tar.gz
coreclr-2b50bba8131acca2ab535e144796941ad93487b7.tar.bz2
coreclr-2b50bba8131acca2ab535e144796941ad93487b7.zip
Move Decimal to shared (#18948)
* Move Decimal to shared * Remove DecimalCanonicalize{Internal}
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj2
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems2
-rw-r--r--src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs2711
-rw-r--r--src/System.Private.CoreLib/shared/System/Decimal.cs (renamed from src/System.Private.CoreLib/src/System/Decimal.cs)494
-rw-r--r--src/System.Private.CoreLib/shared/System/Number.Parsing.cs77
-rw-r--r--src/System.Private.CoreLib/src/System/Currency.cs49
-rw-r--r--src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs96
-rw-r--r--src/System.Private.CoreLib/src/System/Number.CoreCLR.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/StubHelpers.cs3
-rw-r--r--src/classlibnative/bcltype/CMakeLists.txt2
-rw-r--r--src/classlibnative/bcltype/currency.cpp42
-rw-r--r--src/classlibnative/bcltype/currency.h24
-rw-r--r--src/classlibnative/bcltype/decimal.cpp2534
-rw-r--r--src/classlibnative/bcltype/decimal.h52
-rw-r--r--src/classlibnative/bcltype/number.cpp26
-rw-r--r--src/classlibnative/bcltype/number.h1
-rw-r--r--src/inc/utilcode.h65
-rw-r--r--src/palrt/CMakeLists.txt2
-rw-r--r--src/palrt/decarith.cpp1267
-rw-r--r--src/palrt/decconv.cpp602
-rw-r--r--src/vm/ecalllist.h24
-rw-r--r--src/vm/fieldmarshaler.cpp7
-rw-r--r--src/vm/ilmarshalers.cpp5
-rw-r--r--src/vm/mscorlib.cpp2
-rw-r--r--src/vm/mscorlib.h1
-rw-r--r--src/vm/olevariant.cpp18
-rw-r--r--src/vm/stubhelpers.cpp11
-rw-r--r--src/vm/stubhelpers.h1
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);