Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kotas <jkotas@microsoft.com>2017-10-03 20:43:59 +0300
committerGitHub <noreply@github.com>2017-10-03 20:43:59 +0300
commite3f80eaec6f16742b77925f822d8e513ba8876e5 (patch)
tree8583005fb15f003f2733975f6566bcd1c1ff8ef0
parentb889e1b1eb5674c8bdac6da1421256099cdb495d (diff)
parent5a0aef044fa14602210ea06533796186c3790c11 (diff)
Merge pull request #4652 from dotnet/master
Merge master to nmirror
-rw-r--r--dependencies.props2
-rw-r--r--src/Framework/Framework-native.depproj2
-rw-r--r--src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs16
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems1
-rw-r--r--src/System.Private.CoreLib/src/System.Private.CoreLib.csproj2
-rw-r--r--src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs1918
-rw-r--r--src/System.Private.CoreLib/src/System/Decimal.cs27
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs15
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs4
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs8
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs3
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs26
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs16
-rw-r--r--src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs5
15 files changed, 976 insertions, 1072 deletions
diff --git a/dependencies.props b/dependencies.props
index a536e2a9a..ec96f60ea 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -4,7 +4,7 @@
<ObjectWriterVersion>1.0.19-prerelease-00001</ObjectWriterVersion>
<CoreFxVersion>4.5.0-preview1-25729-01</CoreFxVersion>
<CoreFxUapVersion>4.6.0-preview1-25729-01</CoreFxUapVersion>
- <MicrosoftNETCoreNativeVersion>2.0.0-beta-25021-03</MicrosoftNETCoreNativeVersion>
+ <MicrosoftNETCoreNativeVersion>2.1.0-preview1-25730-01</MicrosoftNETCoreNativeVersion>
<MicrosoftNETCoreAppPackageVersion>2.0.0-preview2-25312-01</MicrosoftNETCoreAppPackageVersion>
<XunitNetcoreExtensionsVersion>1.0.1-prerelease-01616-05</XunitNetcoreExtensionsVersion>
</PropertyGroup>
diff --git a/src/Framework/Framework-native.depproj b/src/Framework/Framework-native.depproj
index cc3093531..8719e1c85 100644
--- a/src/Framework/Framework-native.depproj
+++ b/src/Framework/Framework-native.depproj
@@ -9,6 +9,8 @@
<NuGetTargetMoniker>.NETCoreApp,Version=v2.0</NuGetTargetMoniker>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>$(NuPkgRid)</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('osx.'))">osx-x64</RuntimeIdentifiers>
+ <RuntimeIdentifiers Condition="$(NuPkgRid.StartsWith('ubuntu.'))">linux-x64</RuntimeIdentifiers>
<RidSpecificAssets>true</RidSpecificAssets>
</PropertyGroup>
diff --git a/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
new file mode 100644
index 000000000..c69088414
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.ICU.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+internal static partial class Interop
+{
+ internal static partial class GlobalizationInterop
+ {
+ [DllImport(Libraries.GlobalizationInterop, EntryPoint = "GlobalizationNative_LoadICU")]
+ internal static extern int LoadICU();
+ }
+}
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 0831e58af..6f24b8782 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -644,6 +644,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Calendar.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Casing.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Collation.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
+ <Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.ICU.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Idna.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Locale.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Globalization.Native\Interop.Normalization.cs" Condition="'$(EnableDummyGlobalizationImplementation)' != 'true'" />
diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index cd81f6ce9..403aa3bdc 100644
--- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -205,6 +205,8 @@
<Compile Include="System\Empty.cs" />
<Compile Include="System\Globalization\CharUnicodeInfoData.cs" />
<Compile Include="System\Globalization\GlobalizationMode.cs" />
+ <Compile Include="System\Globalization\GlobalizationMode.Windows.cs" Condition="'$(TargetsWindows)'=='true'" />
+ <Compile Include="System\Globalization\GlobalizationMode.Unix.cs" Condition="'$(TargetsUnix)'=='true'" />
<Compile Include="System\Globalization\RegionInfo.cs" />
<Compile Include="System\Guid.CoreRT.cs" />
<Compile Include="System\InvokeUtils.cs" />
diff --git a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
index 5e67f3f61..af85d65df 100644
--- a/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
+++ b/src/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
@@ -2,8 +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.Diagnostics;
+using System;
+using System.Globalization;
using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Diagnostics.Contracts;
namespace System
{
@@ -36,19 +39,14 @@ namespace System
internal int Scale
{
- get { return (byte)(uflags >> ScaleShift); }
+ get { return (int)((uflags & ScaleMask) >> ScaleShift); }
set { uflags = (uflags & ~ScaleMask) | ((uint)value << 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
}
#region APIs need by number formatting.
@@ -73,7 +71,7 @@ namespace System
/// <summary>
/// Class that contains all the mathematical calculations for decimal. Most of which have been ported from oleaut32.
/// </summary>
- private static class DecCalc
+ private class DecCalc
{
// Constant representing the negative number that is the closest possible
// Decimal value to -0m.
@@ -85,19 +83,24 @@ namespace System
private const int DEC_SCALE_MAX = 28;
+ private const uint OVFL_MAX_9_HI = 4;
+ private const uint OVFL_MAX_9_MID = 1266874889;
+ private const uint OVFL_MAX_9_LO = 3047500985;
+ private const uint OVFL_MAX_5_HI = 42949;
+ private const uint OVFL_MAX_1_HI = 429496729;
+
private const uint SNGBIAS = 126;
private const uint DBLBIAS = 1022;
private const uint TenToPowerNine = 1000000000;
+ private const uint TenToPowerTenDiv4 = 2500000000;
private static readonly Split64 s_tenToPowerEighteen = new Split64() { int64 = 1000000000000000000 };
// The maximum power of 10 that a 32 bit integer can store
private const Int32 MaxInt32Scale = 9;
- // The maximum power of 10 that a 64 bit integer can store
- private const Int32 MaxInt64Scale = 19;
// Fast access for 10^n where n is 0-9
- private static readonly UInt32[] s_powers10 = new UInt32[] {
+ private static UInt32[] s_powers10 = new UInt32[] {
1,
10,
100,
@@ -110,30 +113,7 @@ namespace System
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[] {
+ private static 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,
@@ -201,6 +181,30 @@ namespace System
return sdl.int64;
}
+ private static uint FullDiv64By32(ref ulong pdlNum, uint ulDen)
+ {
+ Split64 sdlTmp = new Split64();
+ Split64 sdlRes = new Split64();
+
+ sdlTmp.int64 = pdlNum;
+ sdlRes.High32 = 0;
+
+ if (sdlTmp.High32 >= ulDen)
+ {
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlRes.Low32 = sdlTmp.High32;
+ sdlRes.int64 = DivMod64by32(sdlRes.int64, ulDen);
+ sdlTmp.High32 = sdlRes.High32;
+ sdlRes.High32 = sdlRes.Low32;
+ }
+
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ sdlRes.Low32 = sdlTmp.Low32;
+ pdlNum = sdlRes.int64;
+ return sdlTmp.High32;
+ }
+
private static ulong UInt32x32To64(uint a, uint b)
{
return (ulong)a * (ulong)b;
@@ -232,7 +236,7 @@ namespace System
* Div96By32
*
* Entry:
- * bufNum - 96-bit dividend as array of ULONGs, least-sig first
+ * rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
* ulDen - 32-bit divisor.
*
* Purpose:
@@ -246,80 +250,43 @@ namespace System
* None.
*
***********************************************************************/
- private static uint Div96By32(ref Buf12 bufNum, uint ulDen)
+ private static uint Div96By32(uint[] rgulNum, 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 - div * ulDen) << 32) | bufNum.U0;
- uint div32 = (uint)(tmp / ulDen);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * ulDen;
- }
+ Split64 sdlTmp = new Split64();
- tmp = bufNum.Low64;
- div = tmp / ulDen;
- bufNum.Low64 = div;
- return (uint)(tmp - div * ulDen);
- }
+ sdlTmp.High32 = 0;
- private static uint Div96ByConst100000000(ref Buf12 bufNum)
- {
- const uint ulDen = 100000000;
- ulong tmp = bufNum.High64;
- ulong div = tmp / ulDen;
- bufNum.High64 = div;
- tmp = ((tmp - div * ulDen) << 32) | bufNum.U0;
- uint div32 = (uint)(tmp / ulDen);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * ulDen;
- }
+ if (rgulNum[2] != 0)
+ goto Div3Word;
- private static uint Div96ByConst10000(ref Buf12 bufNum)
- {
- const uint ulDen = 10000;
- ulong tmp = bufNum.High64;
- ulong div = tmp / ulDen;
- bufNum.High64 = div;
- tmp = ((tmp - div * ulDen) << 32) | bufNum.U0;
- uint div32 = (uint)(tmp / ulDen);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * ulDen;
- }
+ if (rgulNum[1] >= ulDen)
+ goto Div2Word;
- private static uint Div96ByConst100(ref Buf12 bufNum)
- {
- const uint ulDen = 100;
- ulong tmp = bufNum.High64;
- ulong div = tmp / ulDen;
- bufNum.High64 = div;
- tmp = ((tmp - div * ulDen) << 32) | bufNum.U0;
- uint div32 = (uint)(tmp / ulDen);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * ulDen;
- }
+ sdlTmp.High32 = rgulNum[1];
+ rgulNum[1] = 0;
+ goto Div1Word;
- private static uint Div96ByConst10(ref Buf12 bufNum)
- {
- const uint ulDen = 10;
- ulong tmp = bufNum.High64;
- ulong div = tmp / ulDen;
- bufNum.High64 = div;
- tmp = ((tmp - div * ulDen) << 32) | bufNum.U0;
- uint div32 = (uint)(tmp / ulDen);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * ulDen;
+ Div3Word:
+ sdlTmp.Low32 = rgulNum[2];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[2] = sdlTmp.Low32;
+ Div2Word:
+ sdlTmp.Low32 = rgulNum[1];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[1] = sdlTmp.Low32;
+ Div1Word:
+ sdlTmp.Low32 = rgulNum[0];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
+ rgulNum[0] = sdlTmp.Low32;
+
+ return sdlTmp.High32;
}
/***
* Div96By64
*
* Entry:
- * bufNum - 96-bit dividend as array of ULONGs, least-sig first
+ * rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
* sdlDen - 64-bit divisor.
*
* Purpose:
@@ -334,87 +301,77 @@ namespace System
* None.
*
***********************************************************************/
- private static uint Div96By64(ref Buf12 bufNum, ulong den)
+ private static uint Div96By64(System.Collections.Generic.IList<uint> rgulNum, Split64 sdlDen)
{
- 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;
+ Split64 sdlQuo = new Split64();
+ Split64 sdlNum = new Split64();
+ Split64 sdlProd = new Split64();
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- quo = (uint)(num / den);
- num -= quo * den; // remainder
- bufNum.Low64 = num;
- return quo;
- }
+ sdlNum.Low32 = rgulNum[0];
- uint denHigh32 = (uint)(den >> 32);
- if (num2 >= denHigh32)
+ if (rgulNum[2] >= sdlDen.High32)
{
// Divide would overflow. Assume a quotient of 2^32, and set
// up remainder accordingly.
//
- num = bufNum.Low64;
- num -= den << 32;
- quo = 0;
+ sdlNum.High32 = rgulNum[1] - sdlDen.Low32;
+ sdlQuo.Low32 = 0;
// Remainder went negative. Add divisor back in until it's positive,
// a max of 2 times.
//
do
{
- quo--;
- num += den;
- } while (num >= den);
+ sdlQuo.Low32--;
+ sdlNum.int64 += sdlDen.int64;
+ } while (sdlNum.int64 >= sdlDen.int64);
- bufNum.Low64 = num;
- return quo;
+ goto Done;
}
// Hardware divide won't overflow
//
- ulong num64 = bufNum.High64;
- if (num64 < denHigh32)
+ if (rgulNum[2] == 0 && rgulNum[1] < sdlDen.High32)
// 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
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlQuo.Low32 = rgulNum[1];
+ sdlQuo.High32 = rgulNum[2];
+ sdlQuo.int64 = DivMod64by32(sdlQuo.int64, sdlDen.High32);
+ sdlNum.High32 = sdlQuo.High32; // remainder
// Compute full remainder, rem = dividend - (quo * divisor).
//
- ulong prod = UInt32x32To64(quo, (uint)den); // quo * lo divisor
- num -= prod;
+ sdlProd.int64 = UInt32x32To64(sdlQuo.Low32, sdlDen.Low32); // quo * lo divisor
+ sdlNum.int64 -= sdlProd.int64;
- if (num > ~prod)
+ if (sdlNum.int64 > ~sdlProd.int64)
{
// Remainder went negative. Add divisor back in until it's positive,
// a max of 2 times.
//
do
{
- quo--;
- num += den;
- } while (num >= den);
+ sdlQuo.Low32--;
+ sdlNum.int64 += sdlDen.int64;
+ } while (sdlNum.int64 >= sdlDen.int64);
}
- bufNum.Low64 = num;
- return quo;
+ Done:
+ rgulNum[0] = sdlNum.Low32;
+ rgulNum[1] = sdlNum.High32;
+ return sdlQuo.Low32;
}
/***
* Div128By96
*
* Entry:
- * bufNum - 128-bit dividend as array of ULONGs, least-sig first
- * bufDen - 96-bit divisor.
+ * 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.
@@ -432,77 +389,83 @@ namespace System
* None.
*
***********************************************************************/
- private static uint Div128By96(ref Buf16 bufNum, ref Buf12 bufDen)
+ private static uint Div128By96(uint[] rgulNum, uint[] rgulDen)
{
- ulong dividend = bufNum.High64;
- uint den = bufDen.U2;
- if (dividend < den)
+ Split64 sdlQuo = new Split64();
+ Split64 sdlNum = new Split64();
+ Split64 sdlProd1 = new Split64();
+ Split64 sdlProd2 = new Split64();
+
+ sdlNum.Low32 = rgulNum[0];
+ sdlNum.High32 = rgulNum[1];
+
+ if (rgulNum[3] == 0 && rgulNum[2] < rgulDen[2])
// 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;
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlQuo.Low32 = rgulNum[2];
+ sdlQuo.High32 = rgulNum[3];
+ sdlQuo.int64 = DivMod64by32(sdlQuo.int64, rgulDen[2]);
// 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;
+ sdlProd1.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[0]); // quo * lo divisor
+ sdlProd2.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[1]); // quo * mid divisor
+ sdlProd2.int64 += sdlProd1.High32;
+ sdlProd1.High32 = sdlProd2.Low32;
- ulong num = bufNum.Low64;
- num -= prod1;
- remainder -= (uint)prod2;
+ sdlNum.int64 -= sdlProd1.int64;
+ rgulNum[2] = sdlQuo.High32 - sdlProd2.High32; // sdlQuo.Hi is remainder
// Propagate carries
//
- if (num > ~prod1)
+ bool fallthru = false;
+ if (sdlNum.int64 > ~sdlProd1.int64)
{
- remainder--;
- if (remainder < ~(uint)prod2)
- goto PosRem;
+ rgulNum[2]--;
+ if (rgulNum[2] >= ~sdlProd2.High32)
+ fallthru = true;
}
- else if (remainder <= ~(uint)prod2)
- goto PosRem;
+ if (fallthru || rgulNum[2] > ~sdlProd2.High32)
{
// Remainder went negative. Add divisor back in until it's positive,
// a max of 2 times.
//
- prod1 = bufDen.Low64;
+ sdlProd1.Low32 = rgulDen[0];
+ sdlProd1.High32 = rgulDen[1];
for (;;)
{
- quo--;
- num += prod1;
- remainder += den;
+ sdlQuo.Low32--;
+ sdlNum.int64 += sdlProd1.int64;
+ rgulNum[2] += rgulDen[2];
- if (num < prod1)
+ if (sdlNum.int64 < sdlProd1.int64)
{
// Detected carry. Check for carry out of top
// before adding it in.
//
- if (remainder++ < den)
+ if (rgulNum[2]++ < rgulDen[2])
break;
}
- if (remainder < den)
+ if (rgulNum[2] < rgulDen[2])
break; // detected carry
}
}
-PosRem:
- bufNum.Low64 = num;
- bufNum.U2 = remainder;
- return quo;
+ rgulNum[0] = sdlNum.Low32;
+ rgulNum[1] = sdlNum.High32;
+ return sdlQuo.Low32;
}
/***
* IncreaseScale
*
* Entry:
- * bufNum - 96-bit number as array of ULONGs, least-sig first
+ * rgulNum - Pointer to 96-bit number as array of ULONGs, least-sig first
* ulPwr - Scale factor to multiply by
*
* Purpose:
@@ -516,34 +479,25 @@ PosRem:
* None.
*
***********************************************************************/
- private static uint IncreaseScale(ref Buf12 bufNum, uint ulPwr)
+ private static uint IncreaseScale(uint[] rgulNum, 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);
- }
+ Split64 sdlTmp = new Split64();
- 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;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[0], ulPwr);
+ rgulNum[0] = sdlTmp.Low32;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[1], ulPwr) + sdlTmp.High32;
+ rgulNum[1] = sdlTmp.Low32;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[2], ulPwr) + sdlTmp.High32;
+ rgulNum[2] = sdlTmp.Low32;
+ return sdlTmp.High32;
}
/***
* ScaleResult
*
* Entry:
- * bufRes - Array of ULONGs with value, least-significant first.
- * iHiRes - Index of last non-zero value in bufRes.
+ * 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:
@@ -551,14 +505,18 @@ PosRem:
* Perform needed scaling. Adjust scale factor accordingly.
*
* Exit:
- * bufRes updated in place, always 3 ULONGs.
- * New scale factor returned.
+ * rgulRes updated in place, always 3 ULONGs.
+ * New scale factor returned, -1 if overflow error.
*
***********************************************************************/
- private static unsafe int ScaleResult(Buf24* bufRes, uint iHiRes, int iScale)
+ private static int ScaleResult(uint[] rgulRes, int iHiRes, int iScale)
{
- Debug.Assert(iHiRes < bufRes->Length);
- uint* rgulRes = (uint*)bufRes;
+ int iNewScale;
+ int iCur;
+ uint ulPwr;
+ uint ulTmp;
+ uint ulSticky;
+ Split64 sdlTmp = new Split64();
// See if we need to scale the result. The combined scale must
// be <= DEC_SCALE_MAX and the upper 96 bits must be zero.
@@ -567,35 +525,37 @@ PosRem:
// the upper 96 bits zero. iHiRes is the index into rgulRes[]
// of the highest non-zero ULONG.
//
- int iNewScale = 0;
- if (iHiRes > 2)
+ iNewScale = iHiRes * 32 - 64 - 1;
+ if (iNewScale > 0)
{
- iNewScale = (int)iHiRes * 32 - 64 - 1;
// Find the MSB.
//
- uint ulTmp = rgulRes[iHiRes];
- iNewScale--;
+ ulTmp = rgulRes[iHiRes];
if ((ulTmp & 0xFFFF0000) == 0)
{
- ulTmp <<= 16;
iNewScale -= 16;
+ ulTmp <<= 16;
}
if ((ulTmp & 0xFF000000) == 0)
{
- ulTmp <<= 8;
iNewScale -= 8;
+ ulTmp <<= 8;
}
if ((ulTmp & 0xF0000000) == 0)
{
- ulTmp <<= 4;
iNewScale -= 4;
+ ulTmp <<= 4;
}
if ((ulTmp & 0xC0000000) == 0)
{
- ulTmp <<= 2;
iNewScale -= 2;
+ ulTmp <<= 2;
+ }
+ if ((ulTmp & 0x80000000) == 0)
+ {
+ iNewScale--;
+ ulTmp <<= 1;
}
- iNewScale -= (int)ulTmp >> 31;
// 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
@@ -614,8 +574,10 @@ PosRem:
// current scale of the result, we'll overflow.
//
if (iNewScale > iScale)
- goto ThrowOverflow;
+ return -1;
}
+ else
+ iNewScale = 0;
// Make sure we scale by enough to bring the current scale factor
// into valid range.
@@ -630,132 +592,42 @@ PosRem:
// be 1 power of 10 short.
//
iScale -= iNewScale;
- uint ulSticky = 0;
- uint quotient, remainder = 0;
+ ulSticky = 0;
+ sdlTmp.High32 = 0; // initialize remainder
for (;;)
{
- ulSticky |= remainder; // record remainder as sticky bit
+ ulSticky |= sdlTmp.High32; // record remainder as sticky bit
+
+ if (iNewScale > MaxInt32Scale)
+ ulPwr = TenToPowerNine;
+ else
+ ulPwr = s_powers10[iNewScale];
- uint ulPwr;
- uint high = rgulRes[iHiRes];
- // 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)
+ // Compute first quotient.
+ // DivMod64by32 returns quotient in Lo, remainder in Hi.
+ //
+ sdlTmp.int64 = DivMod64by32(rgulRes[iHiRes], ulPwr);
+ rgulRes[iHiRes] = sdlTmp.Low32;
+ iCur = iHiRes - 1;
+
+ if (iCur >= 0)
{
- case 1:
- {
- const uint power = 10;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 2:
- {
- const uint power = 100;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 3:
- {
- const uint power = 1000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 4:
- {
- const uint power = 10000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 5:
- {
- const uint power = 100000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 6:
- {
- const uint power = 1000000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 7:
- {
- const uint power = 10000000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- case 8:
- {
- const uint power = 100000000;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
- default:
- {
- const uint power = TenToPowerNine;
- remainder = high - (quotient = high / power) * power;
- for (uint i = iHiRes - 1; (int)i >= 0; i--)
- {
- ulong num = rgulRes[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (rgulRes[i] = (uint)(num / power)) * power;
- }
- ulPwr = power;
- break;
- }
+ // If first quotient was 0, update iHiRes.
+ //
+ if (sdlTmp.Low32 == 0)
+ iHiRes--;
+
+ // Compute subsequent quotients.
+ //
+ do
+ {
+ sdlTmp.Low32 = rgulRes[iCur];
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
+ rgulRes[iCur] = sdlTmp.Low32;
+ iCur--;
+ } while (iCur >= 0);
}
- rgulRes[iHiRes] = quotient;
- // If first quotient was 0, update iHiRes.
- //
- if (quotient == 0 && iHiRes != 0)
- iHiRes--;
iNewScale -= MaxInt32Scale;
if (iNewScale > 0)
@@ -766,8 +638,6 @@ PosRem:
//
if (iHiRes > 2)
{
- if (iScale == 0)
- goto ThrowOverflow;
iNewScale = 1;
iScale--;
continue; // scale by 10
@@ -777,106 +647,90 @@ PosRem:
// 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)
+ if (ulPwr <= sdlTmp.High32 && (ulPwr < sdlTmp.High32 ||
+ ((rgulRes[0] & 1) | ulSticky) != 0))
{
- uint iCur = 0;
- do
- {
- Debug.Assert(iCur + 1 < bufRes->Length);
- }
- while (++rgulRes[++iCur] == 0);
+ iCur = -1;
+ 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
+ sdlTmp.High32 = 0; // or remainder
iNewScale = 1;
iScale--;
continue; // scale by 10
}
}
- break;
+ // 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;
-
-ThrowOverflow:
- throw new OverflowException(SR.Overflow_Decimal);
}
// 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)
+ private static void OverflowUnscale(uint[] rgulQuo, bool fRemainder)
{
- if (--iScale < 0)
- throw new OverflowException(SR.Overflow_Decimal);
-
Split64 sdlTmp = new Split64();
// We have overflown, so load the high bit with a one.
sdlTmp.High32 = 1;
- sdlTmp.Low32 = bufQuo.U2;
+ sdlTmp.Low32 = rgulQuo[2];
sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U2 = sdlTmp.Low32;
- sdlTmp.Low32 = bufQuo.U1;
+ rgulQuo[2] = sdlTmp.Low32;
+ sdlTmp.Low32 = rgulQuo[1];
sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U1 = sdlTmp.Low32;
- sdlTmp.Low32 = bufQuo.U0;
+ rgulQuo[1] = sdlTmp.Low32;
+ sdlTmp.Low32 = rgulQuo[0];
sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
- bufQuo.U0 = sdlTmp.Low32;
+ rgulQuo[0] = sdlTmp.Low32;
// 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.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (bufQuo.U0 & 1) != 0)))
- Add32To96(ref bufQuo, 1);
- return iScale;
+ if ((sdlTmp.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (rgulQuo[0] & 1) != 0)))
+ Add32To96(rgulQuo, 1);
}
/***
* SearchScale
*
* Entry:
- * bufQuo - 96-bit quotient
- * iScale - Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX-1
+ * ulResHi - Top ULONG of quotient
+ * ulResMid - Middle ULONG of quotient
+ * ulResLo - Lower 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.
+ * Returns power of 10 to scale by, -1 if overflow error.
*
***********************************************************************/
- private static int SearchScale(ref Buf12 bufQuo, int iScale)
+ private static int SearchScale(uint ulResHi, uint ulResMid, uint ulResLo, 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;
+ int iCurScale;
// Quick check to stop us from trying to scale any more.
//
- if (ulResHi > OVFL_MAX_1_HI)
+ if (ulResHi > OVFL_MAX_1_HI || iScale >= DEC_SCALE_MAX)
{
+ iCurScale = 0;
goto HaveScale;
}
- var powerOvfl = PowerOvflValues;
if (iScale > DEC_SCALE_MAX - 9)
{
// We can't scale by 10^9 without exceeding the max scale factor.
@@ -884,91 +738,120 @@ ThrowOverflow:
// standard search for scale factor.
//
iCurScale = DEC_SCALE_MAX - iScale;
- if (ulResHi < powerOvfl[iCurScale - 1].Hi)
+ if (ulResHi < PowerOvfl.Hi(iCurScale - 1))
goto HaveScale;
+
+ if (ulResHi == PowerOvfl.Hi(iCurScale - 1))
+ {
+ UpperEq(ulResMid, ulResLo, ref iCurScale);
+ goto HaveScale;
+ }
}
- else if (ulResHi < OVFL_MAX_9_HI || ulResHi == OVFL_MAX_9_HI && ulResMidLo <= OVFL_MAX_9_MIDLO)
+ 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.
+ // Search for a power to scale by < 9. Do a binary search
+ // on PowerOvfl[].
//
- if (ulResHi > OVFL_MAX_5_HI)
+ iCurScale = 5;
+ if (ulResHi < OVFL_MAX_5_HI)
+ iCurScale = 7;
+ else if (ulResHi > OVFL_MAX_5_HI)
+ iCurScale = 3;
+ else
{
- 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--;
- }
+ UpperEq(ulResMid, ulResLo, ref iCurScale);
+ goto HaveScale;
}
+
+ // iCurScale is 3 or 7.
+ //
+ if (ulResHi < PowerOvfl.Hi(iCurScale - 1))
+ iCurScale++;
+ else if (ulResHi > PowerOvfl.Hi(iCurScale - 1))
+ 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--;
- }
+ UpperEq(ulResMid, ulResLo, ref iCurScale);
+ goto HaveScale;
}
+ // 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 && ulResMidLo > powerOvfl[iCurScale - 1].MidLo)
+ if (ulResHi > PowerOvfl.Hi(iCurScale - 1))
iCurScale--;
+ if (ulResHi == PowerOvfl.Hi(iCurScale - 1))
+ UpperEq(ulResMid, ulResLo, ref 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);
+ iCurScale = -1;
return iCurScale;
}
+ private static void UpperEq(uint ulResMid, uint ulResLo, ref int iCurScale)
+ {
+ if (ulResMid > PowerOvfl.Mid(iCurScale - 1) ||
+ (ulResMid == PowerOvfl.Mid(iCurScale - 1) && ulResLo > PowerOvfl.Lo(iCurScale - 1)))
+ {
+ 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)
+ private static bool Add32To96(uint[] rgulNum, uint ulValue)
{
- if ((bufNum.Low64 += ulValue) < ulValue)
+ rgulNum[0] += ulValue;
+ if (rgulNum[0] < ulValue)
{
- if (++bufNum.U2 == 0)
- return false;
+ if (++rgulNum[1] == 0)
+ {
+ if (++rgulNum[2] == 0)
+ return false;
+ }
}
return true;
}
// DecAddSub adds or subtracts two decimal values. On return, d1 contains the result
// of the operation. Passing in true for bSign means subtract and false means add.
- private static unsafe void DecAddSub(ref Decimal d1, ref Decimal d2, bool bSign)
+ //
+ // Returns true if we overflow otherwise false.
+ private static bool DecAddSub(ref Decimal d1, ref Decimal d2, bool bSign)
{
- ulong low64 = d1.Low64;
- uint high = d1.High, flags = d1.uflags, d2flags = d2.uflags;
+ uint[] rgulNum = new uint[6];
+ uint ulPwr;
+ int iScale;
+ int iHiProd;
+ int iCur;
+ Split64 sdlTmp = new Split64();
+ Decimal result = new Decimal();
+ Decimal tmp = new Decimal();
- uint xorflags = d2flags ^ flags;
- bSign ^= (xorflags & SignMask) != 0;
+ bSign ^= d2.IsNegative ^ d1.IsNegative;
- if ((xorflags & ScaleMask) == 0)
+ if (d2.Scale == d1.Scale)
{
// Scale factors are equal, no alignment necessary.
//
- goto AlignedAdd;
+ result.IsNegative = d1.IsNegative;
+ result.Scale = d1.Scale;
+
+ if (AlignedAdd(ref result, ref d1, ref d2, bSign))
+ return true;
}
else
{
@@ -978,28 +861,22 @@ ThrowOverflow:
// 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;
+ result.Scale = d2.Scale; // scale factor of "smaller"
+ result.IsNegative = d1.IsNegative; // but sign of "larger"
+ iScale = result.Scale - d1.Scale;
if (iScale < 0)
{
// Guessed scale factor wrong. Swap operands.
//
iScale = -iScale;
- flags = d1flags;
- if (bSign)
- flags ^= SignMask;
- low64 = d2.Low64;
- high = d2.High;
+ result.Scale = d1.Scale;
+ result.IsNegative ^= bSign;
+ tmp = d2;
d2 = d1;
+ d1 = tmp;
}
- Buf24 bufNum;
- _ = &bufNum; // workaround for CS0165
- uint ulPwr, iHiProd;
- ulong tmp64;
-
// 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.
@@ -1009,268 +886,302 @@ ThrowOverflow:
// Scaling won't make it larger than 4 ULONGs
//
ulPwr = s_powers10[iScale];
- ulong tmpLow = UInt32x32To64((uint)low64, ulPwr);
- tmp64 = (low64 >> 32) * ulPwr + (tmpLow >> 32);
- low64 = (uint)tmpLow + (tmp64 << 32);
- tmp64 >>= 32;
- tmp64 += UInt32x32To64(high, ulPwr);
- if (tmp64 <= uint.MaxValue)
+ tmp = UInt32x32To64(d1.Low, ulPwr);
+ sdlTmp.int64 = UInt32x32To64(d1.Mid, ulPwr);
+ sdlTmp.int64 += tmp.Mid;
+ tmp.Mid = sdlTmp.Low32;
+ tmp.High = sdlTmp.High32;
+ sdlTmp.int64 = UInt32x32To64(d1.High, ulPwr);
+ sdlTmp.int64 += tmp.High;
+ if (sdlTmp.High32 == 0)
{
// Result fits in 96 bits. Use standard aligned add.
//
- high = (uint)tmp64;
- goto AlignedAdd;
+ tmp.High = sdlTmp.Low32;
+ d1 = tmp;
+ if (AlignedAdd(ref result, ref d1, ref d2, bSign))
+ return true;
+ d1 = result;
+ return false;
}
- bufNum.Low64 = low64;
- bufNum.Mid64 = tmp64;
+ rgulNum[0] = tmp.Low;
+ rgulNum[1] = tmp.Mid;
+ rgulNum[2] = sdlTmp.Low32;
+ rgulNum[3] = sdlTmp.High32;
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] = d1.Low;
+ rgulNum[1] = d1.Mid;
+ rgulNum[2] = d1.High;
iHiProd = 2;
// Scan for zeros in the upper words.
//
- if (high == 0)
+ if (rgulNum[2] == 0)
{
iHiProd = 1;
- if (low64 >> 32 == 0)
+ if (rgulNum[1] == 0)
{
iHiProd = 0;
- if ((uint)low64 == 0)
+ if (rgulNum[0] == 0)
{
// Left arg is zero, return right.
//
- uint signFlags = flags & SignMask;
- if (bSign)
- signFlags ^= SignMask;
- d1 = d2;
- d1.uflags = d2.uflags & ScaleMask | signFlags;
- return;
+ result.Low64 = d2.Low64;
+ result.High = d2.High;
+ result.IsNegative ^= bSign;
+ d1 = result;
+ return false;
}
}
}
- // Have to scale by a bunch. Move the number to a buffer
- // where it has room to grow as it's scaled.
- //
- bufNum.Low64 = low64;
- bufNum.U2 = high;
-
// Scaling loop, up to 10^9 at a time. iHiProd stays updated
// with index of highest non-zero ULONG.
//
for (; iScale > 0; iScale -= MaxInt32Scale)
{
- if (iScale >= MaxInt32Scale)
+ if (iScale > MaxInt32Scale)
ulPwr = TenToPowerNine;
else
ulPwr = s_powers10[iScale];
- tmp64 = 0;
- uint* rgulNum = (uint*)&bufNum;
- for (uint iCur = 0; ;)
+ sdlTmp.High32 = 0;
+ for (iCur = 0; iCur <= iHiProd; iCur++)
{
- Debug.Assert(iCur < bufNum.Length);
- tmp64 += UInt32x32To64(rgulNum[iCur], ulPwr);
- rgulNum[iCur] = (uint)tmp64;
- iCur++;
- tmp64 >>= 32;
- if (iCur > iHiProd)
- break;
+ sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.High32;
+ rgulNum[iCur] = sdlTmp.Low32;
}
- if ((uint)tmp64 != 0)
- {
+ if (sdlTmp.High32 != 0)
// We're extending the result by another ULONG.
- Debug.Assert(iHiProd + 1 < bufNum.Length);
- rgulNum[++iHiProd] = (uint)tmp64;
- }
+ rgulNum[++iHiProd] = sdlTmp.High32;
}
}
// Scaling complete, do the add. Could be subtract if signs differ.
//
- tmp64 = bufNum.Low64;
- low64 = d2.Low64;
- uint tmpHigh = bufNum.U2;
- high = d2.High;
+ sdlTmp.Low32 = rgulNum[0];
+ sdlTmp.High32 = rgulNum[1];
if (bSign)
{
// Signs differ, subtract.
//
- low64 = tmp64 - low64;
- high = tmpHigh - high;
+ result.Low64 = sdlTmp.int64 - d2.Low64;
+ result.High = rgulNum[2] - d2.High;
// Propagate carry
//
- if (low64 > tmp64)
+ if (result.Low64 > sdlTmp.int64)
{
- high--;
- if (high < tmpHigh)
- goto NoCarry;
+ result.High--;
+ if (result.High >= rgulNum[2])
+ if (LongSub(ref result, ref iHiProd, rgulNum))
+ {
+ d1 = result;
+ return false;
+ }
}
- else if (high <= tmpHigh)
- goto NoCarry;
-
- // 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)
+ else if (result.High > rgulNum[2])
{
- goto SignFlip;
+ if (LongSub(ref result, ref iHiProd, rgulNum))
+ {
+ d1 = result;
+ return false;
+ }
}
-
- 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--;
}
else
{
// Signs the same, add.
//
- low64 += tmp64;
- high += tmpHigh;
+ result.Low64 = sdlTmp.int64 + d2.Low64;
+ result.High = rgulNum[2] + d2.High;
// Propagate carry
//
- if (low64 < tmp64)
+ if (result.Low64 < sdlTmp.int64)
{
- high++;
- if (high > tmpHigh)
- goto NoCarry;
+ result.High++;
+ if (result.High <= rgulNum[2])
+ LongAdd(ref iHiProd, rgulNum);
}
- else if (high >= tmpHigh)
- goto NoCarry;
-
- uint* rgulNum = (uint*)&bufNum;
- uint iCur = 3;
- do
+ else if (result.High < rgulNum[2])
{
- Debug.Assert(iCur < bufNum.Length);
- if (iHiProd < iCur)
- {
- rgulNum[iCur] = 1;
- iHiProd = iCur;
- break;
- }
- } while (++rgulNum[iCur++] == 0);
+ LongAdd(ref iHiProd, rgulNum);
+ }
}
-NoCarry:
if (iHiProd > 2)
{
- 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;
+ rgulNum[0] = result.Low;
+ rgulNum[1] = result.Mid;
+ rgulNum[2] = result.High;
+ int scale = ScaleResult(rgulNum, iHiProd, result.Scale);
+ if (scale == -1)
+ return true;
+ result.Scale = scale;
+
+ result.Low = rgulNum[0];
+ result.Mid = rgulNum[1];
+ result.High = rgulNum[2];
}
+ }
+
+ d1 = result;
+ return false;
+ }
+
+ private static void SignFlip(ref Decimal value)
+ {
+ value.Low64 = (ulong)-(long)value.Low64;
+ value.High = ~value.High;
+ if (value.Low64 == 0)
+ value.High++;
+ value.IsNegative ^= true;
+ }
+
+ // Returns true if we overflowed
+ private static bool AlignedScale(ref Decimal value)
+ {
+ Split64 sdlTmp = new Split64();
+
+ // Divide the value by 10, dropping the scale factor.
+ //
+ if (value.Scale == 0)
+ return true;
+ value.Scale--;
- goto ReturnResult;
+ sdlTmp.Low32 = value.High;
+ sdlTmp.High32 = 1;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ value.High = sdlTmp.Low32;
+
+ sdlTmp.Low32 = value.Mid;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ value.Mid = sdlTmp.Low32;
+
+ sdlTmp.Low32 = value.Low;
+ sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
+ value.Low = sdlTmp.Low32;
+
+ // See if we need to round up.
+ //
+ if (sdlTmp.High32 >= 5 && (sdlTmp.High32 > 5 || (value.Low & 1) != 0))
+ {
+ value.Low64 = value.Low64 + 1;
+ if (value.Low64 == 0)
+ value.High++;
}
+ return false;
+ }
-SignFlip:
+ private static bool LongSub(ref Decimal value, ref int iHiProd, uint[] rgulNum)
+ {
+ // 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)
{
- // Got negative result. Flip its sign.
- flags ^= SignMask;
- high = ~high;
- low64 = (ulong)-(long)low64;
- if (low64 == 0)
- high++;
- goto ReturnResult;
+ SignFlip(ref value);
+ return true;
}
-AlignedScale:
+ int iCur = 3;
+ while (rgulNum[iCur++]-- == 0)
+ ;
+ if (rgulNum[iHiProd] == 0)
+ iHiProd--;
+
+ return false;
+ }
+
+ private static void LongAdd(ref int iHiProd, uint[] rgulNum)
+ {
+ int iCur = 3;
+ do
{
- // 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 (iHiProd < iCur)
{
- if (++low64 == 0)
- high++;
+ rgulNum[iCur] = 1;
+ iHiProd = iCur;
+ break;
}
- goto ReturnResult;
- }
+ } while (++rgulNum[iCur++] == 0);
+ }
-AlignedAdd:
+ // Returns true if we overflowed
+ private static bool AlignedAdd(ref Decimal value, ref Decimal d1, ref Decimal d2, bool bSign)
+ {
+ if (bSign)
{
- ulong d1Low64 = low64;
- uint d1High = high;
- if (bSign)
- {
- // Signs differ - subtract
- //
- low64 = d1Low64 - d2.Low64;
- high = d1High - d2.High;
+ // Signs differ - subtract
+ //
+ value.Low64 = d1.Low64 - d2.Low64;
+ value.High = d1.High - d2.High;
- // Propagate carry
- //
- if (low64 > d1Low64)
- {
- high--;
- if (high >= d1High)
- goto SignFlip;
- }
- else if (high > d1High)
- goto SignFlip;
+ // Propagate carry
+ //
+ if (value.Low64 > d1.Low64)
+ {
+ value.High--;
+ if (value.High >= d1.High)
+ SignFlip(ref value);
}
- else
+ else if (value.High > d1.High)
{
- // Signs are the same - add
+ // Got negative result. Flip its sign.
//
- low64 = d1Low64 + d2.Low64;
- high = d1High + d2.High;
+ SignFlip(ref value);
+ }
+ }
+ else
+ {
+ // Signs are the same - add
+ //
+ value.Low64 = d1.Low64 + d2.Low64;
+ value.High = d1.High + d2.High;
- // Propagate carry
- //
- if (low64 < d1Low64)
+ // Propagate carry
+ //
+ if (value.Low64 < d1.Low64)
+ {
+ value.High++;
+ if (value.High <= d1.High)
{
- high++;
- if (high <= d1High)
- goto AlignedScale;
+ if (AlignedScale(ref value))
+ return true;
}
- else if (high < d1High)
- goto AlignedScale;
}
- goto ReturnResult;
+ else if (value.High < d1.High)
+ {
+ // The addition carried above 96 bits. Divide the result by 10,
+ // dropping the scale factor.
+ //
+ if (AlignedScale(ref value))
+ return true;
+ }
}
+ return false;
+ }
-ReturnResult:
- d1.uflags = flags;
- d1.High = high;
- d1.Low64 = low64;
- return;
+ private static void RoundUp(uint[] rgulQuo, ref int iScale)
+ {
+ if (!Add32To96(rgulQuo, 1))
+ {
+ if (iScale == 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+ iScale--;
+ OverflowUnscale(rgulQuo, true);
+ }
}
// Returns the absolute value of the given Decimal. If d is
@@ -1299,19 +1210,18 @@ ReturnResult:
* None.
*
***********************************************************************/
- private static unsafe uint DecFixInt(ref Decimal input, ref Decimal result)
+ private static uint DecFixInt(ref Decimal input, ref Decimal result)
{
- Buf12 bufNum;
- _ = &bufNum; // workaround for CS0165
+ uint[] tmpNum = new uint[3];
uint remainder;
uint power;
int scale;
if (input.Scale > 0)
{
- bufNum.U0 = input.ulo;
- bufNum.U1 = input.umid;
- bufNum.U2 = input.uhi;
+ tmpNum[0] = input.ulo;
+ tmpNum[1] = input.umid;
+ tmpNum[2] = input.uhi;
scale = input.Scale;
result.IsNegative = input.IsNegative;
remainder = 0;
@@ -1323,13 +1233,13 @@ ReturnResult:
else
power = s_powers10[scale];
- remainder |= Div96By32(ref bufNum, power);
+ remainder |= Div96By32(tmpNum, power);
scale -= MaxInt32Scale;
} while (scale > 0);
- result.ulo = bufNum.U0;
- result.umid = bufNum.U1;
- result.uhi = bufNum.U2;
+ result.ulo = tmpNum[0];
+ result.umid = tmpNum[1];
+ result.uhi = tmpNum[2];
result.Scale = 0;
return remainder;
@@ -1482,120 +1392,101 @@ ReturnResult:
//**********************************************************************
internal static int VarDecCmp(ref Decimal pdecL, ref 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(ref pdecL, ref pdecR);
- }
+ int signLeft = 0;
+ int signRight = 0;
- private static int VarDecCmpSub(ref Decimal d1, ref Decimal d2)
- {
- int flags = d2.flags;
- int sign = (flags >> 31) | 1;
- int iScale = flags - d1.flags;
+ if (pdecL.Low != 0 || pdecL.Mid != 0 || pdecL.High != 0)
+ signLeft = pdecL.IsNegative ? -1 : 1;
- ulong low64 = d1.Low64;
- uint high = d1.High;
+ if (pdecR.Low != 0 || pdecR.Mid != 0 || pdecR.High != 0)
+ signRight = pdecR.IsNegative ? -1 : 1;
- if (iScale != 0)
+ if (signLeft == signRight)
{
- 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;
- low64 = d2.Low64;
- high = d2.High;
- d2 = d1;
- }
-
- // 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 = (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);
- }
+ if (signLeft == 0) // both are zero
+ return 0; // return equal
- uint cmpHigh = high - d2.High;
- if (cmpHigh != 0)
- {
- // check for overflow
- if (cmpHigh > high)
- sign = -sign;
- return sign;
+ Decimal decLAndResult = pdecL; // Copy the left and pass that to AddSub because it gets mutated.
+ DecAddSub(ref decLAndResult, ref pdecR, true); // Call DecAddSub instead of VarDecSub to avoid exceptions
+ if (decLAndResult.Low == 0 && decLAndResult.Mid == 0 && decLAndResult.High == 0)
+ return 0;
+ if (decLAndResult.IsNegative)
+ return -1;
+ return 1;
}
- ulong cmpLow64 = low64 - d2.Low64;
- if (cmpLow64 == 0)
- sign = 0;
- // check for overflow
- else if (cmpLow64 > low64)
- sign = -sign;
- return sign;
+ // Signs are different. Used signed byte compares
+ //
+ if (signLeft > signRight)
+ return 1;
+ return -1;
}
//**********************************************************************
// VarDecMul - Decimal Multiply
//**********************************************************************
- internal static unsafe void VarDecMul(ref Decimal pdecL, ref Decimal pdecR)
+ internal static void VarDecMul(ref Decimal pdecL, ref Decimal pdecR, out Decimal pdecRes)
{
- int iScale = (byte)(pdecL.uflags + pdecR.uflags >> ScaleShift);
+ Split64 sdlTmp = new Split64();
+ Split64 sdlTmp2 = new Split64();
+ Split64 sdlTmp3 = new Split64();
+ int iScale;
+ int iHiProd;
+ uint ulPwr;
+ uint ulRemLo;
+ uint ulRemHi;
+ uint[] rgulProd = new uint[6];
+
+ pdecRes = new Decimal();
+ iScale = pdecL.Scale + pdecR.Scale;
if ((pdecL.High | pdecL.Mid | pdecR.High | pdecR.Mid) == 0)
{
// Upper 64 bits are zero.
//
- ulong low64 = UInt32x32To64(pdecL.Low, pdecR.Low);
+ sdlTmp.int64 = 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]
//
- iScale -= DEC_SCALE_MAX + 1;
- if (iScale >= MaxInt64Scale)
- goto ReturnZero;
-
- ulong ulPwr = s_ulongPowers10[iScale];
+ iScale -= DEC_SCALE_MAX;
+ if (iScale > 19)
+ {
+ //DECIMAL_SETZERO(*pdecRes);
+ return;
+ }
+ if (iScale > MaxInt32Scale)
+ {
+ // 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(ref sdlTmp.int64, TenToPowerTenDiv4);
+ ulPwr = s_powers10[iScale - 10] << 2;
+ }
+ else
+ {
+ ulPwr = s_powers10[iScale];
+ ulRemLo = 0;
+ }
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- ulong div = low64 / ulPwr;
- ulong remainder = low64 - div * ulPwr;
- low64 = div;
+ // Power to divide by fits in 32 bits.
+ //
+ ulRemHi = FullDiv64By32(ref 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 (remainder >= ulPwr && (remainder > ulPwr || (low64 & 1) > 0))
- low64++;
+ if (ulRemHi >= ulPwr && (ulRemHi > ulPwr || (ulRemLo | (sdlTmp.Low32 & 1)) > 0))
+ sdlTmp.int64++;
iScale = DEC_SCALE_MAX;
}
- pdecL.Low64 = low64;
+ pdecRes.Low64 = sdlTmp.int64;
+ pdecRes.High = 0;
}
else
{
@@ -1620,93 +1511,87 @@ ReturnResult:
// ------------------------------
// [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array
//
- uint iHiProd;
- Buf24 bufProd;
- _ = &bufProd; // workaround for CS0165
+ sdlTmp.int64 = UInt32x32To64(pdecL.Low, pdecR.Low);
+ rgulProd[0] = sdlTmp.Low32;
- ulong tmp = UInt32x32To64(pdecL.Low, pdecR.Low);
- bufProd.U0 = (uint)tmp;
+ sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.Mid) + sdlTmp.High32;
- 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 = 1UL << 32;
+ sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Low);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[1] = sdlTmp.Low32;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp2.High32 = 1;
else
- tmp2 = 0;
- tmp2 += tmp >> 32;
+ sdlTmp2.High32 = 0;
+ sdlTmp2.Low32 = sdlTmp.High32;
- tmp = UInt32x32To64(pdecL.Mid, pdecR.Mid) + tmp2;
+ sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Mid) + sdlTmp2.int64;
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
- ulong tmp3 = 0;
- if (tmp < tmp2) // detect carry
- tmp3 = 1UL << 32;
-
- tmp2 = UInt32x32To64(pdecL.High, pdecR.Low);
- tmp += tmp2; // this could generate carry
- bufProd.U2 = (uint)tmp;
- if (tmp < tmp2) // detect carry
- tmp3 += 1UL << 32;
- tmp2 = tmp3 + (tmp >> 32);
-
- tmp = UInt32x32To64(pdecL.Mid, pdecR.High);
- tmp += tmp2; // this could generate carry
- tmp3 = 0;
- if (tmp < tmp2) // detect carry
- tmp3 = 1UL << 32;
-
- tmp2 = UInt32x32To64(pdecL.High, pdecR.Mid);
- tmp += tmp2; // this could generate carry
- bufProd.U3 = (uint)tmp;
- if (tmp < tmp2) // detect carry
- tmp3 += 1UL << 32;
- tmp3 += tmp >> 32;
-
- tmp = UInt32x32To64(pdecL.High, pdecR.High) + tmp3;
- bufProd.High64 = tmp;
+ sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.High);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.High32 = 1;
+ else
+ sdlTmp3.High32 = 0;
+
+ sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Low);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[2] = sdlTmp.Low32;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.High32++;
+ sdlTmp3.Low32 = sdlTmp.High32;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.High);
+ sdlTmp.int64 += sdlTmp3.int64; // this could generate carry
+ if (sdlTmp.int64 < sdlTmp3.int64) // detect carry
+ sdlTmp3.High32 = 1;
+ else
+ sdlTmp3.High32 = 0;
+
+ sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Mid);
+ sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
+ rgulProd[3] = sdlTmp.Low32;
+ if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
+ sdlTmp3.High32++;
+ sdlTmp3.Low32 = sdlTmp.High32;
+
+ sdlTmp.int64 = UInt32x32To64(pdecL.High, pdecR.High) + sdlTmp3.int64;
+ rgulProd[4] = sdlTmp.Low32;
+ rgulProd[5] = sdlTmp.High32;
iHiProd = 5;
}
- else if (tmp != 0)
+ else
{
- bufProd.Mid64 = tmp;
+ rgulProd[2] = sdlTmp.Low32;
+ rgulProd[3] = sdlTmp.High32;
iHiProd = 3;
}
- else
- iHiProd = 1;
// Check for leading zero ULONGs on the product
//
- uint* rgulProd = (uint*)&bufProd;
while (rgulProd[iHiProd] == 0)
{
- if (iHiProd == 0)
- goto ReturnZero;
iHiProd--;
+ if (iHiProd < 0)
+ return;
}
- if (iHiProd > 2 || iScale > DEC_SCALE_MAX)
- {
- iScale = ScaleResult(&bufProd, iHiProd, iScale);
- }
+ iScale = ScaleResult(rgulProd, iHiProd, iScale);
+ if (iScale == -1)
+ throw new OverflowException(SR.Overflow_Decimal);
- pdecL.Low64 = bufProd.Low64;
- pdecL.High = bufProd.U2;
+ pdecRes.Low = rgulProd[0];
+ pdecRes.Mid = rgulProd[1];
+ pdecRes.High = rgulProd[2];
}
- pdecL.uflags = ((pdecR.uflags ^ pdecL.uflags) & SignMask) | ((uint)iScale << ScaleShift);
- return;
-
-ReturnZero:
- pdecL = default(Decimal);
+ pdecRes.IsNegative = pdecR.IsNegative ^ pdecL.IsNegative;
+ pdecRes.Scale = (char)iScale;
}
//**********************************************************************
@@ -2048,45 +1933,56 @@ ReturnZero:
// of the operation
internal static void VarDecAdd(ref Decimal d1, ref Decimal d2)
{
- DecAddSub(ref d1, ref d2, false);
+ if (DecAddSub(ref d1, ref d2, false))
+ throw new OverflowException(SR.Overflow_Decimal);
}
// VarDecSub divides two decimal values. On return, d1 contains the result
// of the operation.
internal static void VarDecSub(ref Decimal d1, ref Decimal d2)
{
- DecAddSub(ref d1, ref d2, true);
+ if (DecAddSub(ref d1, ref d2, true))
+ throw new OverflowException(SR.Overflow_Decimal);
}
// VarDecDiv divides two decimal values. On return, d1 contains the result
// of the operation.
- internal static unsafe void VarDecDiv(ref Decimal d1, ref Decimal d2)
+ internal static void VarDecDiv(ref Decimal d1, ref Decimal d2)
{
- Buf12 bufQuo, bufDivisor;
- _ = &bufQuo; // workaround for CS0165
- _ = &bufDivisor; // workaround for CS0165
+ uint[] rgulQuo = new uint[3];
+ uint[] rgulQuoSave = new uint[3];
+ uint[] rgulRem = new uint[4];
+ uint[] rgulDivisor = new uint[3];
uint ulPwr;
+ uint ulTmp;
+ uint ulTmp1;
+ Split64 sdlTmp = new Split64();
+ Split64 sdlDivisor = new Split64();
+ int iScale;
int iCurScale;
+ bool fUnscale;
- int iScale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift);
- bool fUnscale = false;
- uint ulTmp;
+ iScale = d1.Scale - d2.Scale;
+ fUnscale = false;
+ rgulDivisor[0] = d2.Low;
+ rgulDivisor[1] = d2.Mid;
+ rgulDivisor[2] = d2.High;
- if (d2.High == 0 && d2.Mid == 0)
+ if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0)
{
// Divisor is only 32 bits. Easy divide.
//
- uint den = d2.Low;
- if (den == 0)
+ if (rgulDivisor[0] == 0)
throw new DivideByZeroException(SR.Overflow_Decimal);
- bufQuo.Low64 = d1.Low64;
- bufQuo.U2 = d1.High;
- uint remainder = Div96By32(ref bufQuo, den);
+ rgulQuo[0] = d1.Low;
+ rgulQuo[1] = d1.Mid;
+ rgulQuo[2] = d1.High;
+ rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]);
for (;;)
{
- if (remainder == 0)
+ if (rgulRem[0] == 0)
{
if (iScale < 0)
{
@@ -2116,32 +2012,38 @@ ReturnZero:
// 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)
+ 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 = remainder << 1;
- if (ulTmp < remainder || ulTmp >= den && (ulTmp > den || (bufQuo.U0 & 1) != 0))
- goto RoundUp;
+ ulTmp = rgulRem[0] << 1;
+ if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] &&
+ (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1) != 0)))
+ RoundUp(rgulQuo, ref iScale);
break;
}
+ if (iCurScale < 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+
HaveScale:
ulPwr = s_powers10[iCurScale];
iScale += iCurScale;
- if (IncreaseScale(ref bufQuo, ulPwr) != 0)
- goto ThrowOverflow;
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ throw new OverflowException(SR.Overflow_Decimal);
- ulong num = UInt32x32To64(remainder, ulPwr);
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- uint div = (uint)(num / den);
- remainder = (uint)num - div * den;
+ sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]);
+ rgulRem[0] = sdlTmp.High32;
- if (!Add32To96(ref bufQuo, div))
+ if (!Add32To96(rgulQuo, sdlTmp.Low32))
{
- iScale = OverflowUnscale(ref bufQuo, iScale, remainder != 0);
+ if (iScale == 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0));
break;
}
} // for (;;)
@@ -2155,56 +2057,68 @@ ReturnZero:
// 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;
+ if (rgulDivisor[2] == 0)
+ ulTmp = rgulDivisor[1];
+ else
+ ulTmp = rgulDivisor[2];
- iCurScale = 1;
+ iCurScale = 0;
if ((ulTmp & 0xFFFF0000) == 0)
{
- ulTmp <<= 16;
iCurScale += 16;
+ ulTmp <<= 16;
}
if ((ulTmp & 0xFF000000) == 0)
{
- ulTmp <<= 8;
iCurScale += 8;
+ ulTmp <<= 8;
}
if ((ulTmp & 0xF0000000) == 0)
{
- ulTmp <<= 4;
iCurScale += 4;
+ ulTmp <<= 4;
}
if ((ulTmp & 0xC0000000) == 0)
{
- ulTmp <<= 2;
iCurScale += 2;
+ ulTmp <<= 2;
+ }
+ if ((ulTmp & 0x80000000) == 0)
+ {
+ iCurScale++;
+ ulTmp <<= 1;
}
- iCurScale += (int)ulTmp >> 31;
// 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)
+ sdlTmp.int64 = d1.Low64 << iCurScale;
+ rgulRem[0] = sdlTmp.Low32;
+ rgulRem[1] = sdlTmp.High32;
+ sdlTmp.Low32 = d1.Mid;
+ sdlTmp.High32 = d1.High;
+ sdlTmp.int64 <<= iCurScale;
+ rgulRem[2] = sdlTmp.High32;
+ rgulRem[3] = (d1.High >> (31 - iCurScale)) >> 1;
+
+ sdlDivisor.Low32 = rgulDivisor[0];
+ sdlDivisor.High32 = 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.Low32 = rgulRem[2];
+ sdlTmp.High32 = rgulRem[3];
- bufQuo.U1 = Div96By64(ref *(Buf12*)&bufRem.U1, divisor);
- bufQuo.U0 = Div96By64(ref *(Buf12*)&bufRem, divisor);
+ rgulQuo[2] = 0;
+ rgulQuo[1] = Div96By64(new ArraySegment<uint>(rgulRem, 1, 3), sdlDivisor);
+ rgulQuo[0] = Div96By64(rgulRem, sdlDivisor);
for (;;)
{
- if (bufRem.Low64 == 0)
+ if ((rgulRem[0] | rgulRem[1]) == 0)
{
if (iScale < 0)
{
@@ -2220,30 +2134,39 @@ ReturnZero:
// 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)
+ 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.
//
- ulong tmp = bufRem.Low64;
- if ((long)tmp < 0 || (tmp <<= 1) > divisor ||
- (tmp == divisor && (bufQuo.U0 & 1) != 0))
- goto RoundUp;
+ sdlTmp.Low32 = rgulRem[0];
+ sdlTmp.High32 = rgulRem[1];
+ if (sdlTmp.High32 >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
+ (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1) != 0))
+ RoundUp(rgulQuo, ref iScale);
break;
}
+ if (iCurScale < 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+
HaveScale64:
ulPwr = s_powers10[iCurScale];
iScale += iCurScale;
- if (IncreaseScale(ref bufQuo, ulPwr) != 0)
- goto ThrowOverflow;
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ throw new OverflowException(SR.Overflow_Decimal);
- IncreaseScale64(ref *(Buf12*)&bufRem, ulPwr);
- ulTmp = Div96By64(ref *(Buf12*)&bufRem, divisor);
- if (!Add32To96(ref bufQuo, ulTmp))
+ rgulRem[2] = 0; // rem is 64 bits, IncreaseScale uses 96
+ IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div96By64(rgulRem, sdlDivisor);
+ if (!Add32To96(rgulQuo, ulTmp))
{
- iScale = OverflowUnscale(ref bufQuo, iScale, bufRem.Low64 != 0);
+ if (iScale == 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0));
break;
}
} // for (;;)
@@ -2254,18 +2177,23 @@ ReturnZero:
//
// Start by finishing the shift left by iCurScale.
//
- uint tmp = (uint)(bufDivisor.High64 >> (31 - iCurScale) >> 1);
- bufDivisor.Low64 = divisor;
- bufDivisor.U2 = tmp;
+ sdlTmp.Low32 = rgulDivisor[1];
+ sdlTmp.High32 = rgulDivisor[2];
+ sdlTmp.int64 <<= iCurScale;
+ rgulDivisor[0] = sdlDivisor.Low32;
+ rgulDivisor[1] = sdlDivisor.High32;
+ rgulDivisor[2] = sdlTmp.High32;
// The remainder (currently 96 bits spread over 4 ULONGs)
// will be < divisor.
//
- bufQuo.Low64 = Div128By96(ref bufRem, ref bufDivisor);
+ rgulQuo[2] = 0;
+ rgulQuo[1] = 0;
+ rgulQuo[0] = Div128By96(rgulRem, rgulDivisor);
for (;;)
{
- if ((bufRem.Low64 | bufRem.U2) == 0)
+ if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0)
{
if (iScale < 0)
{
@@ -2281,48 +2209,56 @@ ReturnZero:
// 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)
+ 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 ((int)bufRem.U2 < 0)
+ if (rgulRem[2] >= 0x80000000)
{
- goto RoundUp;
+ RoundUp(rgulQuo, ref iScale);
+ break;
}
- 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;
+ ulTmp = (rgulRem[0] > 0x80000000) ? 1u : 0u;
+ ulTmp1 = (rgulRem[1] > 0x80000000) ? 1u : 0u;
+ 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) != 0)))
+ RoundUp(rgulQuo, ref iScale);
break;
}
+ if (iCurScale < 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+
HaveScale96:
ulPwr = s_powers10[iCurScale];
iScale += iCurScale;
- if (IncreaseScale(ref bufQuo, ulPwr) != 0)
- goto ThrowOverflow;
+ if (IncreaseScale(rgulQuo, ulPwr) != 0)
+ throw new OverflowException(SR.Overflow_Decimal);
- bufRem.U3 = IncreaseScale(ref *(Buf12*)&bufRem, ulPwr);
- ulTmp = Div128By96(ref bufRem, ref bufDivisor);
- if (!Add32To96(ref bufQuo, ulTmp))
+ rgulRem[3] = IncreaseScale(rgulRem, ulPwr);
+ ulTmp = Div128By96(rgulRem, rgulDivisor);
+ if (!Add32To96(rgulQuo, ulTmp))
{
- iScale = OverflowUnscale(ref bufQuo, iScale, (bufRem.Low64 | bufRem.High64) != 0);
+ if (iScale == 0)
+ throw new OverflowException(SR.Overflow_Decimal);
+ iScale--;
+ OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0));
break;
}
} // for (;;)
}
}
-Unscale:
- ulong low64 = bufQuo.Low64;
- uint high = bufQuo.U2;
// We need to unscale if and only if we have a non-zero remainder
if (fUnscale)
{
@@ -2336,79 +2272,74 @@ Unscale:
// we can extract. We use this as a quick test on whether to try a
// given power.
//
- while (((uint)low64 & 0xFF) == 0 && iScale >= 8)
+ while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8)
{
- if (Div96ByConst100000000(ref bufQuo) == 0)
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100000000) == 0)
{
- low64 = bufQuo.Low64;
- high = bufQuo.U2;
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
iScale -= 8;
}
else
- {
- bufQuo.Low64 = low64;
- bufQuo.U2 = high;
break;
- }
}
- if (((uint)low64 & 0xF) == 0 && iScale >= 4)
+ if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4)
{
- if (Div96ByConst10000(ref bufQuo) == 0)
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 10000) == 0)
{
- low64 = bufQuo.Low64;
- high = bufQuo.U2;
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
iScale -= 4;
}
- else
- {
- bufQuo.Low64 = low64;
- bufQuo.U2 = high;
- }
}
- if (((uint)low64 & 3) == 0 && iScale >= 2)
+ if ((rgulQuo[0] & 3) == 0 && iScale >= 2)
{
- if (Div96ByConst100(ref bufQuo) == 0)
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
+
+ if (Div96By32(rgulQuoSave, 100) == 0)
{
- low64 = bufQuo.Low64;
- high = bufQuo.U2;
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
iScale -= 2;
}
- else
- {
- bufQuo.Low64 = low64;
- bufQuo.U2 = high;
- }
}
- if (((uint)low64 & 1) == 0 && iScale >= 1 && Div96ByConst10(ref bufQuo) == 0)
+ if ((rgulQuo[0] & 1) == 0 && iScale >= 1)
{
- low64 = bufQuo.Low64;
- high = bufQuo.U2;
- iScale -= 1;
- }
- }
+ rgulQuoSave[0] = rgulQuo[0];
+ rgulQuoSave[1] = rgulQuo[1];
+ rgulQuoSave[2] = rgulQuo[2];
- d1.uflags = ((d1.uflags ^ d2.uflags) & SignMask) | ((uint)iScale << ScaleShift);
- d1.High = high;
- d1.Low64 = low64;
- return;
-
-RoundUp:
- {
- if (++bufQuo.Low64 == 0)
- {
- if (++bufQuo.U2 == 0)
+ if (Div96By32(rgulQuoSave, 10) == 0)
{
- iScale = OverflowUnscale(ref bufQuo, iScale, true);
+ rgulQuo[0] = rgulQuoSave[0];
+ rgulQuo[1] = rgulQuoSave[1];
+ rgulQuo[2] = rgulQuoSave[2];
+ iScale -= 1;
}
}
- goto Unscale;
}
-ThrowOverflow:
- throw new OverflowException(SR.Overflow_Decimal);
+ d1.IsNegative = d1.IsNegative ^ d2.IsNegative;
+ d1.High = rgulQuo[2];
+ d1.Mid = rgulQuo[1];
+ d1.Low = rgulQuo[0];
+ d1.Scale = iScale;
}
//**********************************************************************
@@ -2444,10 +2375,9 @@ ThrowOverflow:
//**********************************************************************
// VarDecRound - Decimal Round
//**********************************************************************
- internal static unsafe void VarDecRound(ref Decimal input, int decimals, ref Decimal result)
+ internal static void VarDecRound(ref Decimal input, int decimals, ref Decimal result)
{
- Buf12 bufNum;
- _ = &bufNum; // workaround for CS0165
+ uint[] tmpNum = new uint[3];
uint remainder;
uint sticky;
uint power;
@@ -2458,9 +2388,9 @@ ThrowOverflow:
scale = input.Scale - decimals;
if (scale > 0)
{
- bufNum.U0 = input.ulo;
- bufNum.U1 = input.umid;
- bufNum.U2 = input.uhi;
+ tmpNum[0] = input.ulo;
+ tmpNum[1] = input.umid;
+ tmpNum[2] = input.uhi;
result.IsNegative = input.IsNegative;
remainder = sticky = 0;
@@ -2472,23 +2402,23 @@ ThrowOverflow:
else
power = s_powers10[scale];
- remainder = Div96By32(ref bufNum, power);
+ remainder = Div96By32(tmpNum, power);
scale -= MaxInt32Scale;
} while (scale > 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.
- sticky |= bufNum.U0 & 1;
+ sticky |= tmpNum[0] & 1;
remainder = (remainder << 1) + (uint)(sticky != 0 ? 1 : 0);
if (power < remainder
- && ++bufNum.U0 == 0
- && ++bufNum.U1 == 0)
- ++bufNum.U2;
+ && ++tmpNum[0] == 0
+ && ++tmpNum[1] == 0)
+ ++tmpNum[2];
- result.ulo = bufNum.U0;
- result.umid = bufNum.U1;
- result.uhi = bufNum.U2;
+ result.ulo = tmpNum[0];
+ result.umid = tmpNum[1];
+ result.uhi = tmpNum[2];
result.Scale = decimals;
return;
}
@@ -2721,174 +2651,46 @@ ThrowOverflow:
}
}
- 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
+ private static class PowerOvfl
{
- [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
+ private static uint[] s_powerOvfl =
{
-#if BIGENDIAN
- get => ((ulong)U3 << 32) | U2;
- set { U3 = (uint)(value >> 32); U2 = (uint)value; }
-#else
- get => uhigh64LE;
- set => uhigh64LE = value;
-#endif
- }
- }
+ // 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.
+ //
+ // Table logically consists of three components for each entry (high,
+ // mid and low) but we declare it as a flat array of uints since that's
+ // all C# will support. We hide this via the accessor methods on this
+ // class.
+ //
+ 429496729, 2576980377, 2576980377, // 10^1 remainder 0.6
+ 42949672, 4123168604, 687194767, // 10^2 remainder 0.16
+ 4294967, 1271310319, 2645699854, // 10^3 remainder 0.616
+ 429496, 3133608139, 694066715, // 10^4 remainder 0.1616
+ 42949, 2890341191, 2216890319, // 10^5 remainder 0.51616
+ 4294, 4154504685, 2369172679, // 10^6 remainder 0.551616
+ 429, 2133437386, 4102387834, // 10^7 remainder 0.9551616
+ 42, 4078814305, 410238783, // 10^8 remainder 0.09991616
+ 4, 1266874889, 3047500985, // 10^9 remainder 0.709551616
+ };
- [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
+ public static uint Hi(int index)
{
-#if BIGENDIAN
- get => ((ulong)U1 << 32) | U0;
- set { U1 = (uint)(value >> 32); U0 = (uint)value; }
-#else
- get => ulo64LE;
- set => ulo64LE = value;
-#endif
+ return s_powerOvfl[index * 3];
}
- public ulong Mid64
+ public static uint Mid(int index)
{
-#if BIGENDIAN
- get => ((ulong)U3 << 32) | U2;
- set { U3 = (uint)(value >> 32); U2 = (uint)value; }
-#else
- get => umid64LE;
- set => umid64LE = value;
-#endif
+ return s_powerOvfl[(index * 3) + 1];
}
- public ulong High64
+ public static uint Lo(int index)
{
-#if BIGENDIAN
- get => ((ulong)U5 << 32) | U4;
- set { U5 = (uint)(value >> 32); U4 = (uint)value; }
-#else
- get => uhigh64LE;
- set => uhigh64LE = value;
-#endif
+ return s_powerOvfl[(index * 3) + 2];
}
-
- public int Length => 6;
}
}
}
diff --git a/src/System.Private.CoreLib/src/System/Decimal.cs b/src/System.Private.CoreLib/src/System/Decimal.cs
index 2fc98da8c..8d89068f7 100644
--- a/src/System.Private.CoreLib/src/System/Decimal.cs
+++ b/src/System.Private.CoreLib/src/System/Decimal.cs
@@ -123,11 +123,13 @@ namespace System
[FieldOffset(12), NonSerialized]
private uint umid;
- /// <summary>
- /// The low and mid fields combined in little-endian order
- /// </summary>
- [FieldOffset(8), NonSerialized]
- private ulong ulomidLE;
+ // Constructs a zero Decimal.
+ //public Decimal() {
+ // lo = 0;
+ // mid = 0;
+ // hi = 0;
+ // flags = 0;
+ //}
// Constructs a Decimal from an integer value.
//
@@ -177,7 +179,8 @@ namespace System
uflags = SignMask;
value_copy = -value_copy;
}
- Low64 = (ulong)value;
+ ulo = (uint)value_copy;
+ umid = (uint)(value_copy >> 32);
uhi = 0;
}
@@ -187,7 +190,8 @@ namespace System
public Decimal(ulong value)
{
uflags = 0;
- Low64 = value;
+ ulo = (uint)value;
+ umid = (uint)(value >> 32);
uhi = 0;
}
@@ -276,6 +280,10 @@ namespace System
//
public Decimal(int[] bits)
{
+ lo = 0;
+ mid = 0;
+ hi = 0;
+ flags = 0;
SetBits(bits);
}
@@ -644,8 +652,9 @@ namespace System
//
public static Decimal Multiply(Decimal d1, Decimal d2)
{
- DecCalc.VarDecMul(ref d1, ref d2);
- return d1;
+ Decimal decRes;
+ DecCalc.VarDecMul(ref d1, ref d2, out decRes);
+ return decRes;
}
// Returns the negated value of the given Decimal. If d is non-zero,
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs
index 50e93841f..55a6ca4bf 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs
@@ -3,9 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
-using System.Diagnostics.Contracts;
using System.Collections.Generic;
namespace System.Globalization
@@ -14,6 +14,8 @@ namespace System.Globalization
{
private bool LoadCalendarDataFromSystem(String localeName, CalendarId calendarId)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
bool ret = true;
uint useOverrides = this.bUseUserOverrides ? 0 : CAL_NOUSEROVERRIDE;
@@ -108,6 +110,11 @@ namespace System.Globalization
// Get native two digit year max
internal static int GetTwoDigitYearMax(CalendarId calendarId)
{
+ if (GlobalizationMode.Invariant)
+ {
+ return Invariant.iTwoDigitYearMax;
+ }
+
int twoDigitYearMax = -1;
if (!CallGetCalendarInfoEx(null, calendarId, (uint)CAL_ITWODIGITYEARMAX, out twoDigitYearMax))
@@ -121,6 +128,8 @@ namespace System.Globalization
// Call native side to figure out which calendars are allowed
internal static int GetCalendars(String localeName, bool useUserOverride, CalendarId[] calendars)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
EnumCalendarsData data = new EnumCalendarsData();
data.userOverride = 0;
data.calendars = new LowLevelList<int>();
@@ -154,6 +163,8 @@ namespace System.Globalization
private static bool SystemSupportsTaiwaneseCalendar()
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
string data;
// Taiwanese calendar get listed as one of the optional zh-TW calendars only when having zh-TW UI
return CallGetCalendarInfoEx("zh-TW", CalendarId.TAIWAN, CAL_SCALNAME, out data);
@@ -442,6 +453,8 @@ namespace System.Globalization
private static unsafe String GetUserDefaultLocaleName()
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
const int LOCALE_NAME_MAX_LENGTH = 85;
const uint LOCALE_SNAME = 0x0000005c;
const string LOCALE_NAME_USER_DEFAULT = null;
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs
index 31f5a8b63..04c9019df 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs
@@ -71,6 +71,10 @@ namespace System.Globalization
[OptionalField(VersionAdded = 3)]
private SortVersion m_SortVersion; // Do not rename (binary serialization)
+ // _invariantMode is defined for the perf reason as accessing the instance field is faster than access the static property GlobalizationMode.Invariant
+ [NonSerialized]
+ private readonly bool _invariantMode = GlobalizationMode.Invariant;
+
private int culture; // Do not rename (binary serialization). The fields sole purpose is to support Desktop serialization.
internal CompareInfo(CultureInfo culture)
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
index 28ebe7239..4f67cc6a8 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
@@ -630,13 +630,17 @@ namespace System.Globalization
return null;
}
- private int LocaleNameToLCID(string cultureName)
+ private static int LocaleNameToLCID(string cultureName)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
return Interop.Kernel32.LocaleNameToLCID(cultureName, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
}
private static unsafe string LCIDToLocaleName(int culture)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
char *pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1]; // +1 for the null termination
int length = Interop.Kernel32.LCIDToLocaleName(culture, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH + 1, Interop.Kernel32.LOCALE_ALLOW_NEUTRAL_NAMES);
@@ -685,6 +689,8 @@ namespace System.Globalization
private static CultureInfo[] EnumCultures(CultureTypes types)
{
+ Debug.Assert(!GlobalizationMode.Invariant);
+
uint flags = 0;
#pragma warning disable 618
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
index e9fe86ace..5a06bccd4 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Unix.cs
@@ -13,6 +13,9 @@ namespace System.Globalization
internal static CultureInfo GetUserDefaultCulture()
{
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
CultureInfo cultureInfo = null;
string localeName;
if (CultureData.GetDefaultLocaleName(out localeName))
diff --git a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
index b395c1a7f..f5a6992e6 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/CultureInfo.Windows.cs
@@ -31,6 +31,9 @@ namespace System.Globalization
internal static CultureInfo GetUserDefaultCulture()
{
+ if (GlobalizationMode.Invariant)
+ return CultureInfo.InvariantCulture;
+
const uint LOCALE_SNAME = 0x0000005c;
const string LOCALE_NAME_USER_DEFAULT = null;
const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale";
diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
new file mode 100644
index 000000000..e4ab832d5
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs
@@ -0,0 +1,26 @@
+// 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.
+
+namespace System.Globalization
+{
+ internal static partial class GlobalizationMode
+ {
+ private static bool GetGlobalizationInvariantMode()
+ {
+ // CORERT-TODO: Enable System.Globalization.Invariant switch
+ // bool invariantEnabled = CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch);
+ bool invariantEnabled = false;
+ if (!invariantEnabled)
+ {
+ if (Interop.GlobalizationInterop.LoadICU() == 0)
+ {
+ string message = "Couldn't find a valid ICU package installed on the system. " +
+ "Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.";
+ Environment.FailFast(message);
+ }
+ }
+ return invariantEnabled;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs
new file mode 100644
index 000000000..7c6bf0161
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs
@@ -0,0 +1,16 @@
+// 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.
+
+namespace System.Globalization
+{
+ internal static partial class GlobalizationMode
+ {
+ private static bool GetGlobalizationInvariantMode()
+ {
+ // CORERT-TODO: Enable System.Globalization.Invariant switch
+ // return CLRConfig.GetBoolValue(c_InvariantModeConfigSwitch);
+ return false;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
index ea56795c9..266774d91 100644
--- a/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
+++ b/src/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs
@@ -4,8 +4,9 @@
namespace System.Globalization
{
- internal static class GlobalizationMode
+ internal static partial class GlobalizationMode
{
- internal static bool Invariant { get; } = false;
+ private const string c_InvariantModeConfigSwitch = "System.Globalization.Invariant";
+ internal static bool Invariant { get; } = GetGlobalizationInvariantMode();
}
}