| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707 |
- // 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.Numerics;
- 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));
- }
- #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
- };
- // Used to fill uninitialized stack variables with non-zero pattern in debug builds
- [Conditional("DEBUG")]
- private static unsafe void DebugPoison<T>(ref T s) where T: unmanaged
- {
- MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref s, 1)).Fill(0xCD);
- }
- #region Decimal Math Helpers
- private static unsafe uint GetExponent(float f)
- {
- // Based on pulling out the exp from this single struct layout
- //typedef struct {
- // ULONG mant:23;
- // ULONG exp:8;
- // ULONG sign:1;
- //} SNGSTRUCT;
- return (byte)(*(uint*)&f >> 23);
- }
- private static unsafe uint GetExponent(double d)
- {
- // Based on pulling out the exp from this double struct layout
- //typedef struct {
- // DWORDLONG mant:52;
- // DWORDLONG signexp:12;
- // } DBLSTRUCT;
- return (uint)(*(ulong*)&d >> 52) & 0x7FFu;
- }
- private static ulong UInt32x32To64(uint a, uint b)
- {
- return (ulong)a * (ulong)b;
- }
- private static void UInt64x64To128(ulong a, ulong b, ref DecCalc result)
- {
- ulong low = UInt32x32To64((uint)a, (uint)b); // lo partial prod
- ulong mid = UInt32x32To64((uint)a, (uint)(b >> 32)); // mid 1 partial prod
- ulong high = UInt32x32To64((uint)(a >> 32), (uint)(b >> 32));
- high += mid >> 32;
- low += mid <<= 32;
- if (low < mid) // test for carry
- high++;
- mid = UInt32x32To64((uint)(a >> 32), (uint)b);
- high += mid >> 32;
- low += mid <<= 32;
- if (low < mid) // test for carry
- high++;
- if (high > uint.MaxValue)
- Number.ThrowOverflowException(TypeCode.Decimal);
- result.Low64 = low;
- result.High = (uint)high;
- }
- /// <summary>
- /// Do full divide, yielding 96-bit result and 32-bit remainder.
- /// </summary>
- /// <param name="bufNum">96-bit dividend as array of uints, least-sig first</param>
- /// <param name="den">32-bit divisor</param>
- /// <returns>Returns remainder. Quotient overwrites dividend.</returns>
- private static uint Div96By32(ref Buf12 bufNum, uint den)
- {
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- ulong tmp, div;
- if (bufNum.U2 != 0)
- {
- tmp = bufNum.High64;
- div = tmp / den;
- bufNum.High64 = div;
- tmp = ((tmp - (uint)div * den) << 32) | bufNum.U0;
- if (tmp == 0)
- return 0;
- uint div32 = (uint)(tmp / den);
- bufNum.U0 = div32;
- return (uint)tmp - div32 * den;
- }
- tmp = bufNum.Low64;
- if (tmp == 0)
- return 0;
- div = tmp / den;
- bufNum.Low64 = div;
- return (uint)(tmp - div * den);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool Div96ByConst(ref ulong high64, ref uint low, uint pow)
- {
- #if BIT64
- ulong div64 = high64 / pow;
- uint div = (uint)((((high64 - div64 * pow) << 32) + low) / pow);
- if (low == div * pow)
- {
- high64 = div64;
- low = div;
- return true;
- }
- #else
- // 32-bit RyuJIT doesn't convert 64-bit division by constant into multiplication by reciprocal. Do half-width divisions instead.
- Debug.Assert(pow <= ushort.MaxValue);
- uint num, mid32, low16, div;
- if (high64 <= uint.MaxValue)
- {
- num = (uint)high64;
- mid32 = num / pow;
- num = (num - mid32 * pow) << 16;
- num += low >> 16;
- low16 = num / pow;
- num = (num - low16 * pow) << 16;
- num += (ushort)low;
- div = num / pow;
- if (num == div * pow)
- {
- high64 = mid32;
- low = (low16 << 16) + div;
- return true;
- }
- }
- else
- {
- num = (uint)(high64 >> 32);
- uint high32 = num / pow;
- num = (num - high32 * pow) << 16;
- num += (uint)high64 >> 16;
- mid32 = num / pow;
- num = (num - mid32 * pow) << 16;
- num += (ushort)high64;
- div = num / pow;
- num = (num - div * pow) << 16;
- mid32 = div + (mid32 << 16);
- num += low >> 16;
- low16 = num / pow;
- num = (num - low16 * pow) << 16;
- num += (ushort)low;
- div = num / pow;
- if (num == div * pow)
- {
- high64 = ((ulong)high32 << 32) | mid32;
- low = (low16 << 16) + div;
- return true;
- }
- }
- #endif
- return false;
- }
- /// <summary>
- /// 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.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void Unscale(ref uint low, ref ulong high64, ref int scale)
- {
- // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10 we can extract.
- // We use this as a quick test on whether to try a given power.
- #if BIT64
- while ((byte)low == 0 && scale >= 8 && Div96ByConst(ref high64, ref low, 100000000))
- scale -= 8;
- if ((low & 0xF) == 0 && scale >= 4 && Div96ByConst(ref high64, ref low, 10000))
- scale -= 4;
- #else
- while ((low & 0xF) == 0 && scale >= 4 && Div96ByConst(ref high64, ref low, 10000))
- scale -= 4;
- #endif
- if ((low & 3) == 0 && scale >= 2 && Div96ByConst(ref high64, ref low, 100))
- scale -= 2;
- if ((low & 1) == 0 && scale >= 1 && Div96ByConst(ref high64, ref low, 10))
- scale--;
- }
- /// <summary>
- /// Do partial divide, yielding 32-bit result and 64-bit remainder.
- /// Divisor must be larger than upper 64 bits of dividend.
- /// </summary>
- /// <param name="bufNum">96-bit dividend as array of uints, least-sig first</param>
- /// <param name="den">64-bit divisor</param>
- /// <returns>Returns quotient. Remainder overwrites lower 64-bits of dividend.</returns>
- private static uint Div96By64(ref Buf12 bufNum, ulong den)
- {
- Debug.Assert(den > bufNum.High64);
- uint quo;
- ulong num;
- uint num2 = bufNum.U2;
- if (num2 == 0)
- {
- num = bufNum.Low64;
- if (num < den)
- // Result is zero. Entire dividend is remainder.
- return 0;
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- quo = (uint)(num / den);
- num -= quo * den; // remainder
- bufNum.Low64 = num;
- return quo;
- }
- uint denHigh32 = (uint)(den >> 32);
- if (num2 >= denHigh32)
- {
- // Divide would overflow. Assume a quotient of 2^32, and set
- // up remainder accordingly.
- //
- num = bufNum.Low64;
- num -= den << 32;
- quo = 0;
- // Remainder went negative. Add divisor back in until it's positive,
- // a max of 2 times.
- //
- do
- {
- quo--;
- num += den;
- } while (num >= den);
- bufNum.Low64 = num;
- return quo;
- }
- // Hardware divide won't overflow
- //
- ulong num64 = bufNum.High64;
- if (num64 < denHigh32)
- // Result is zero. Entire dividend is remainder.
- //
- return 0;
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- quo = (uint)(num64 / denHigh32);
- num = bufNum.U0 | ((num64 - quo * denHigh32) << 32); // remainder
- // Compute full remainder, rem = dividend - (quo * divisor).
- //
- ulong prod = UInt32x32To64(quo, (uint)den); // quo * lo divisor
- num -= prod;
- if (num > ~prod)
- {
- // Remainder went negative. Add divisor back in until it's positive,
- // a max of 2 times.
- //
- do
- {
- quo--;
- num += den;
- } while (num >= den);
- }
- bufNum.Low64 = num;
- return quo;
- }
- /// <summary>
- /// Do partial divide, yielding 32-bit result and 96-bit remainder.
- /// Top divisor uint must be larger than top dividend uint. This is
- /// assured in the initial call because the divisor is normalized
- /// and the dividend can't be. In subsequent calls, the remainder
- /// is multiplied by 10^9 (max), so it can be no more than 1/4 of
- /// the divisor which is effectively multiplied by 2^32 (4 * 10^9).
- /// </summary>
- /// <param name="bufNum">128-bit dividend as array of uints, least-sig first</param>
- /// <param name="bufDen">96-bit divisor</param>
- /// <returns>Returns quotient. Remainder overwrites lower 96-bits of dividend.</returns>
- private static uint Div128By96(ref Buf16 bufNum, ref Buf12 bufDen)
- {
- Debug.Assert(bufDen.U2 > bufNum.U3);
- ulong dividend = bufNum.High64;
- uint den = bufDen.U2;
- if (dividend < den)
- // Result is zero. Entire dividend is remainder.
- //
- return 0;
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- uint quo = (uint)(dividend / den);
- uint remainder = (uint)dividend - quo * den;
- // Compute full remainder, rem = dividend - (quo * divisor).
- //
- ulong prod1 = UInt32x32To64(quo, bufDen.U0); // quo * lo divisor
- ulong prod2 = UInt32x32To64(quo, bufDen.U1); // quo * mid divisor
- prod2 += prod1 >> 32;
- prod1 = (uint)prod1 | (prod2 << 32);
- prod2 >>= 32;
- ulong num = bufNum.Low64;
- num -= prod1;
- remainder -= (uint)prod2;
- // Propagate carries
- //
- if (num > ~prod1)
- {
- remainder--;
- if (remainder < ~(uint)prod2)
- goto PosRem;
- }
- else if (remainder <= ~(uint)prod2)
- goto PosRem;
- {
- // Remainder went negative. Add divisor back in until it's positive,
- // a max of 2 times.
- //
- prod1 = bufDen.Low64;
- for (;;)
- {
- quo--;
- num += prod1;
- remainder += den;
- if (num < prod1)
- {
- // Detected carry. Check for carry out of top
- // before adding it in.
- //
- if (remainder++ < den)
- break;
- }
- if (remainder < den)
- break; // detected carry
- }
- }
- PosRem:
- bufNum.Low64 = num;
- bufNum.U2 = remainder;
- return quo;
- }
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="bufNum">96-bit number as array of uints, least-sig first</param>
- /// <param name="power">Scale factor to multiply by</param>
- /// <returns>Returns highest 32 bits of product</returns>
- private static uint IncreaseScale(ref Buf12 bufNum, uint power)
- {
- ulong tmp = UInt32x32To64(bufNum.U0, power);
- bufNum.U0 = (uint)tmp;
- tmp >>= 32;
- tmp += UInt32x32To64(bufNum.U1, power);
- bufNum.U1 = (uint)tmp;
- tmp >>= 32;
- tmp += UInt32x32To64(bufNum.U2, power);
- bufNum.U2 = (uint)tmp;
- return (uint)(tmp >> 32);
- }
- private static void IncreaseScale64(ref Buf12 bufNum, uint power)
- {
- ulong tmp = UInt32x32To64(bufNum.U0, power);
- bufNum.U0 = (uint)tmp;
- tmp >>= 32;
- tmp += UInt32x32To64(bufNum.U1, power);
- bufNum.High64 = tmp;
- }
- /// <summary>
- /// See if we need to scale the result to fit it in 96 bits.
- /// Perform needed scaling. Adjust scale factor accordingly.
- /// </summary>
- /// <param name="bufRes">Array of uints with value, least-significant first</param>
- /// <param name="hiRes">Index of last non-zero value in bufRes</param>
- /// <param name="scale">Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX</param>
- /// <returns>Returns new scale factor. bufRes updated in place, always 3 uints.</returns>
- private static unsafe int ScaleResult(Buf24* bufRes, uint hiRes, int scale)
- {
- Debug.Assert(hiRes < bufRes->Length);
- uint* result = (uint*)bufRes;
- // See if we need to scale the result. The combined scale must
- // be <= DEC_SCALE_MAX and the upper 96 bits must be zero.
- //
- // Start by figuring a lower bound on the scaling needed to make
- // the upper 96 bits zero. hiRes is the index into result[]
- // of the highest non-zero uint.
- //
- int newScale = 0;
- if (hiRes > 2)
- {
- newScale = (int)hiRes * 32 - 64 - 1;
- newScale -= BitOperations.LeadingZeroCount(result[hiRes]);
- // Multiply bit position by log10(2) to figure it's power of 10.
- // We scale the log by 256. log(2) = .30103, * 256 = 77. Doing this
- // with a multiply saves a 96-byte lookup table. The power returned
- // is <= the power of the number, so we must add one power of 10
- // to make it's integer part zero after dividing by 256.
- //
- // Note: the result of this multiplication by an approximation of
- // log10(2) have been exhaustively checked to verify it gives the
- // correct result. (There were only 95 to check...)
- //
- newScale = ((newScale * 77) >> 8) + 1;
- // newScale = min scale factor to make high 96 bits zero, 0 - 29.
- // This reduces the scale factor of the result. If it exceeds the
- // current scale of the result, we'll overflow.
- //
- if (newScale > scale)
- goto ThrowOverflow;
- }
- // Make sure we scale by enough to bring the current scale factor
- // into valid range.
- //
- if (newScale < scale - DEC_SCALE_MAX)
- newScale = scale - DEC_SCALE_MAX;
- if (newScale != 0)
- {
- // Scale by the power of 10 given by newScale. Note that this is
- // NOT guaranteed to bring the number within 96 bits -- it could
- // be 1 power of 10 short.
- //
- scale -= newScale;
- uint sticky = 0;
- uint quotient, remainder = 0;
- for (;;)
- {
- sticky |= remainder; // record remainder as sticky bit
- uint power;
- // Scaling loop specialized for each power of 10 because division by constant is an order of magnitude faster (especially for 64-bit division that's actually done by 128bit DIV on x64)
- switch (newScale)
- {
- case 1:
- power = DivByConst(result, hiRes, out quotient, out remainder, 10);
- break;
- case 2:
- power = DivByConst(result, hiRes, out quotient, out remainder, 100);
- break;
- case 3:
- power = DivByConst(result, hiRes, out quotient, out remainder, 1000);
- break;
- case 4:
- power = DivByConst(result, hiRes, out quotient, out remainder, 10000);
- break;
- #if BIT64
- case 5:
- power = DivByConst(result, hiRes, out quotient, out remainder, 100000);
- break;
- case 6:
- power = DivByConst(result, hiRes, out quotient, out remainder, 1000000);
- break;
- case 7:
- power = DivByConst(result, hiRes, out quotient, out remainder, 10000000);
- break;
- case 8:
- power = DivByConst(result, hiRes, out quotient, out remainder, 100000000);
- break;
- default:
- power = DivByConst(result, hiRes, out quotient, out remainder, TenToPowerNine);
- break;
- #else
- default:
- goto case 4;
- #endif
- }
- result[hiRes] = quotient;
- // If first quotient was 0, update hiRes.
- //
- if (quotient == 0 && hiRes != 0)
- hiRes--;
- #if BIT64
- newScale -= MaxInt32Scale;
- #else
- newScale -= 4;
- #endif
- if (newScale > 0)
- continue; // scale some more
- // If we scaled enough, hiRes would be 2 or less. If not,
- // divide by 10 more.
- //
- if (hiRes > 2)
- {
- if (scale == 0)
- goto ThrowOverflow;
- newScale = 1;
- scale--;
- continue; // scale by 10
- }
- // Round final result. See if remainder >= 1/2 of divisor.
- // If remainder == 1/2 divisor, round up if odd or sticky bit set.
- //
- power >>= 1; // power of 10 always even
- if (power <= remainder && (power < remainder || ((result[0] & 1) | sticky) != 0) && ++result[0] == 0)
- {
- uint cur = 0;
- do
- {
- Debug.Assert(cur + 1 < bufRes->Length);
- }
- while (++result[++cur] == 0);
- if (cur > 2)
- {
- // The rounding caused us to carry beyond 96 bits.
- // Scale by 10 more.
- //
- if (scale == 0)
- goto ThrowOverflow;
- hiRes = cur;
- sticky = 0; // no sticky bit
- remainder = 0; // or remainder
- newScale = 1;
- scale--;
- continue; // scale by 10
- }
- }
- break;
- } // for(;;)
- }
- return scale;
- ThrowOverflow:
- Number.ThrowOverflowException(TypeCode.Decimal);
- return 0;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static unsafe uint DivByConst(uint* result, uint hiRes, out uint quotient, out uint remainder, uint power)
- {
- uint high = result[hiRes];
- remainder = high - (quotient = high / power) * power;
- for (uint i = hiRes - 1; (int)i >= 0; i--)
- {
- #if BIT64
- ulong num = result[i] + ((ulong)remainder << 32);
- remainder = (uint)num - (result[i] = (uint)(num / power)) * power;
- #else
- // 32-bit RyuJIT doesn't convert 64-bit division by constant into multiplication by reciprocal. Do half-width divisions instead.
- Debug.Assert(power <= ushort.MaxValue);
- #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*)result + i * 4 + high16) + (remainder << 16);
- uint div = num / power;
- remainder = num - div * power;
- *(ushort*)((byte*)result + i * 4 + high16) = (ushort)div;
- num = *(ushort*)((byte*)result + i * 4 + low16) + (remainder << 16);
- div = num / power;
- remainder = num - div * power;
- *(ushort*)((byte*)result + i * 4 + low16) = (ushort)div;
- #endif
- }
- return power;
- }
- /// <summary>
- /// 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.
- /// </summary>
- private static int OverflowUnscale(ref Buf12 bufQuo, int scale, bool sticky)
- {
- if (--scale < 0)
- Number.ThrowOverflowException(TypeCode.Decimal);
- Debug.Assert(bufQuo.U2 == 0);
- // We have overflown, so load the high bit with a one.
- const ulong highbit = 1UL << 32;
- bufQuo.U2 = (uint)(highbit / 10);
- ulong tmp = ((highbit % 10) << 32) + bufQuo.U1;
- uint div = (uint)(tmp / 10);
- bufQuo.U1 = div;
- tmp = ((tmp - div * 10) << 32) + bufQuo.U0;
- div = (uint)(tmp / 10);
- bufQuo.U0 = div;
- uint remainder = (uint)(tmp - div * 10);
- // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up
- if (remainder > 5 || remainder == 5 && (sticky || (bufQuo.U0 & 1) != 0))
- Add32To96(ref bufQuo, 1);
- return scale;
- }
- /// <summary>
- /// Determine the max power of 10, <= 9, that the quotient can be scaled
- /// up by and still fit in 96 bits.
- /// </summary>
- /// <param name="bufQuo">96-bit quotient</param>
- /// <param name="scale ">Scale factor of quotient, range -DEC_SCALE_MAX to DEC_SCALE_MAX-1</param>
- /// <returns>power of 10 to scale by</returns>
- private static int SearchScale(ref Buf12 bufQuo, int scale)
- {
- const uint OVFL_MAX_9_HI = 4;
- const uint OVFL_MAX_8_HI = 42;
- const uint OVFL_MAX_7_HI = 429;
- const uint OVFL_MAX_6_HI = 4294;
- const uint OVFL_MAX_5_HI = 42949;
- const uint OVFL_MAX_4_HI = 429496;
- const uint OVFL_MAX_3_HI = 4294967;
- const uint OVFL_MAX_2_HI = 42949672;
- const uint OVFL_MAX_1_HI = 429496729;
- const ulong OVFL_MAX_9_MIDLO = 5441186219426131129;
- uint resHi = bufQuo.U2;
- ulong resMidLo = bufQuo.Low64;
- int curScale = 0;
- // Quick check to stop us from trying to scale any more.
- //
- if (resHi > OVFL_MAX_1_HI)
- {
- goto HaveScale;
- }
- var powerOvfl = PowerOvflValues;
- if (scale > DEC_SCALE_MAX - 9)
- {
- // We can't scale by 10^9 without exceeding the max scale factor.
- // See if we can scale to the max. If not, we'll fall into
- // standard search for scale factor.
- //
- curScale = DEC_SCALE_MAX - scale;
- if (resHi < powerOvfl[curScale - 1].Hi)
- goto HaveScale;
- }
- else if (resHi < OVFL_MAX_9_HI || resHi == OVFL_MAX_9_HI && resMidLo <= OVFL_MAX_9_MIDLO)
- return 9;
- // Search for a power to scale by < 9. Do a binary search.
- //
- if (resHi > OVFL_MAX_5_HI)
- {
- if (resHi > OVFL_MAX_3_HI)
- {
- curScale = 2;
- if (resHi > OVFL_MAX_2_HI)
- curScale--;
- }
- else
- {
- curScale = 4;
- if (resHi > OVFL_MAX_4_HI)
- curScale--;
- }
- }
- else
- {
- if (resHi > OVFL_MAX_7_HI)
- {
- curScale = 6;
- if (resHi > OVFL_MAX_6_HI)
- curScale--;
- }
- else
- {
- curScale = 8;
- if (resHi > OVFL_MAX_8_HI)
- curScale--;
- }
- }
- // In all cases, we already found we could not use the power one larger.
- // So if we can use this power, it is the biggest, and we're done. If
- // we can't use this power, the one below it is correct for all cases
- // unless it's 10^1 -- we might have to go to 10^0 (no scaling).
- //
- if (resHi == powerOvfl[curScale - 1].Hi && resMidLo > powerOvfl[curScale - 1].MidLo)
- curScale--;
- HaveScale:
- // curScale = largest power of 10 we can scale by without overflow,
- // curScale < 9. See if this is enough to make scale factor
- // positive if it isn't already.
- //
- if (curScale + scale < 0)
- Number.ThrowOverflowException(TypeCode.Decimal);
- return curScale;
- }
- /// <summary>
- /// Add a 32-bit uint to an array of 3 uints representing a 96-bit integer.
- /// </summary>
- /// <returns>Returns false if there is an overflow</returns>
- private static bool Add32To96(ref Buf12 bufNum, uint value)
- {
- if ((bufNum.Low64 += value) < value)
- {
- if (++bufNum.U2 == 0)
- return false;
- }
- return true;
- }
- /// <summary>
- /// Adds or subtracts two decimal values.
- /// On return, d1 contains the result of the operation and d2 is trashed.
- /// </summary>
- /// <param name="sign">True means subtract and false means add.</param>
- internal static unsafe void DecAddSub(ref DecCalc d1, ref DecCalc d2, bool sign)
- {
- ulong low64 = d1.Low64;
- uint high = d1.High, flags = d1.uflags, d2flags = d2.uflags;
- uint xorflags = d2flags ^ flags;
- sign ^= (xorflags & SignMask) != 0;
- if ((xorflags & ScaleMask) == 0)
- {
- // Scale factors are equal, no alignment necessary.
- //
- goto AlignedAdd;
- }
- else
- {
- // Scale factors are not equal. Assume that a larger scale
- // factor (more decimal places) is likely to mean that number
- // is smaller. Start by guessing that the right operand has
- // the larger scale factor. The result will have the larger
- // scale factor.
- //
- uint d1flags = flags;
- flags = d2flags & ScaleMask | flags & SignMask; // scale factor of "smaller", but sign of "larger"
- int scale = (int)(flags - d1flags) >> ScaleShift;
- if (scale < 0)
- {
- // Guessed scale factor wrong. Swap operands.
- //
- scale = -scale;
- flags = d1flags;
- if (sign)
- flags ^= SignMask;
- low64 = d2.Low64;
- high = d2.High;
- d2 = d1;
- }
- uint power;
- ulong tmp64, tmpLow;
- // d1 will need to be multiplied by 10^scale so
- // it will have the same scale as d2. We could be
- // extending it to up to 192 bits of precision.
- // Scan for zeros in the upper words.
- //
- if (high == 0)
- {
- if (low64 <= uint.MaxValue)
- {
- if ((uint)low64 == 0)
- {
- // Left arg is zero, return right.
- //
- uint signFlags = flags & SignMask;
- if (sign)
- signFlags ^= SignMask;
- d1 = d2;
- d1.uflags = d2.uflags & ScaleMask | signFlags;
- return;
- }
- do
- {
- if (scale <= MaxInt32Scale)
- {
- low64 = UInt32x32To64((uint)low64, s_powers10[scale]);
- goto AlignedAdd;
- }
- scale -= MaxInt32Scale;
- low64 = UInt32x32To64((uint)low64, TenToPowerNine);
- } while (low64 <= uint.MaxValue);
- }
- do
- {
- power = TenToPowerNine;
- if (scale < MaxInt32Scale)
- power = s_powers10[scale];
- tmpLow = UInt32x32To64((uint)low64, power);
- tmp64 = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32);
- low64 = (uint)tmpLow + (tmp64 << 32);
- high = (uint)(tmp64 >> 32);
- if ((scale -= MaxInt32Scale) <= 0)
- goto AlignedAdd;
- } while (high == 0);
- }
- while (true)
- {
- // Scaling won't make it larger than 4 uints
- //
- power = TenToPowerNine;
- if (scale < MaxInt32Scale)
- power = s_powers10[scale];
- tmpLow = UInt32x32To64((uint)low64, power);
- tmp64 = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32);
- low64 = (uint)tmpLow + (tmp64 << 32);
- tmp64 >>= 32;
- tmp64 += UInt32x32To64(high, power);
- scale -= MaxInt32Scale;
- if (tmp64 > uint.MaxValue)
- break;
- high = (uint)tmp64;
- // Result fits in 96 bits. Use standard aligned add.
- if (scale <= 0)
- goto AlignedAdd;
- }
- // Have to scale by a bunch. Move the number to a buffer where it has room to grow as it's scaled.
- //
- Buf24 bufNum;
- _ = &bufNum; // workaround for CS0165
- DebugPoison(ref bufNum);
- bufNum.Low64 = low64;
- bufNum.Mid64 = tmp64;
- uint hiProd = 3;
- // Scaling loop, up to 10^9 at a time. hiProd stays updated with index of highest non-zero uint.
- //
- for (; scale > 0; scale -= MaxInt32Scale)
- {
- power = TenToPowerNine;
- if (scale < MaxInt32Scale)
- power = s_powers10[scale];
- tmp64 = 0;
- uint* rgulNum = (uint*)&bufNum;
- for (uint cur = 0; ;)
- {
- Debug.Assert(cur < bufNum.Length);
- tmp64 += UInt32x32To64(rgulNum[cur], power);
- rgulNum[cur] = (uint)tmp64;
- cur++;
- tmp64 >>= 32;
- if (cur > hiProd)
- break;
- }
- if ((uint)tmp64 != 0)
- {
- // We're extending the result by another uint.
- Debug.Assert(hiProd + 1 < bufNum.Length);
- rgulNum[++hiProd] = (uint)tmp64;
- }
- }
- // Scaling complete, do the add. Could be subtract if signs differ.
- //
- tmp64 = bufNum.Low64;
- low64 = d2.Low64;
- uint tmpHigh = bufNum.U2;
- high = d2.High;
- if (sign)
- {
- // Signs differ, subtract.
- //
- low64 = tmp64 - low64;
- high = tmpHigh - high;
- // Propagate carry
- //
- if (low64 > tmp64)
- {
- high--;
- if (high < tmpHigh)
- goto NoCarry;
- }
- else if (high <= tmpHigh)
- goto NoCarry;
- // Carry the subtraction into the higher bits.
- //
- uint* number = (uint*)&bufNum;
- uint cur = 3;
- do
- {
- Debug.Assert(cur < bufNum.Length);
- } while (number[cur++]-- == 0);
- Debug.Assert(hiProd < bufNum.Length);
- if (number[hiProd] == 0 && --hiProd <= 2)
- goto ReturnResult;
- }
- else
- {
- // Signs the same, add.
- //
- low64 += tmp64;
- high += tmpHigh;
- // Propagate carry
- //
- if (low64 < tmp64)
- {
- high++;
- if (high > tmpHigh)
- goto NoCarry;
- }
- else if (high >= tmpHigh)
- goto NoCarry;
- uint* number = (uint*)&bufNum;
- for (uint cur = 3; ++number[cur++] == 0;)
- {
- Debug.Assert(cur < bufNum.Length);
- if (hiProd < cur)
- {
- number[cur] = 1;
- hiProd = cur;
- break;
- }
- }
- }
- NoCarry:
- bufNum.Low64 = low64;
- bufNum.U2 = high;
- scale = ScaleResult(&bufNum, hiProd, (byte)(flags >> ScaleShift));
- flags = (flags & ~ScaleMask) | ((uint)scale << ScaleShift);
- low64 = bufNum.Low64;
- high = bufNum.U2;
- goto ReturnResult;
- }
- SignFlip:
- {
- // Got negative result. Flip its sign.
- flags ^= SignMask;
- high = ~high;
- low64 = (ulong)-(long)low64;
- if (low64 == 0)
- high++;
- goto ReturnResult;
- }
- AlignedScale:
- {
- // The addition carried above 96 bits.
- // Divide the value by 10, dropping the scale factor.
- //
- if ((flags & ScaleMask) == 0)
- Number.ThrowOverflowException(TypeCode.Decimal);
- flags -= 1 << ScaleShift;
- const uint den = 10;
- ulong num = high + (1UL << 32);
- high = (uint)(num / den);
- num = ((num - high * den) << 32) + (low64 >> 32);
- uint div = (uint)(num / den);
- num = ((num - div * den) << 32) + (uint)low64;
- low64 = div;
- low64 <<= 32;
- div = (uint)(num / den);
- low64 += div;
- div = (uint)num - div * den;
- // See if we need to round up.
- //
- if (div >= 5 && (div > 5 || (low64 & 1) != 0))
- {
- if (++low64 == 0)
- high++;
- }
- goto ReturnResult;
- }
- AlignedAdd:
- {
- ulong d1Low64 = low64;
- uint d1High = high;
- if (sign)
- {
- // Signs differ - subtract
- //
- low64 = d1Low64 - d2.Low64;
- high = d1High - d2.High;
- // Propagate carry
- //
- if (low64 > d1Low64)
- {
- high--;
- if (high >= d1High)
- goto SignFlip;
- }
- else if (high > d1High)
- goto SignFlip;
- }
- else
- {
- // Signs are the same - add
- //
- low64 = d1Low64 + d2.Low64;
- high = d1High + d2.High;
- // Propagate carry
- //
- if (low64 < d1Low64)
- {
- high++;
- if (high <= d1High)
- goto AlignedScale;
- }
- else if (high < d1High)
- goto AlignedScale;
- }
- goto ReturnResult;
- }
- ReturnResult:
- d1.uflags = flags;
- d1.High = high;
- d1.Low64 = low64;
- return;
- }
- #endregion
- /// <summary>
- /// Convert Decimal to Currency (similar to OleAut32 api.)
- /// </summary>
- 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, MidpointRounding.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);
- }
- /// <summary>
- /// Decimal Compare updated to return values similar to ICompareTo
- /// </summary>
- internal static int VarDecCmp(in decimal d1, in decimal d2)
- {
- if ((d2.Low | d2.Mid | d2.High) == 0)
- {
- if ((d1.Low | d1.Mid | d1.High) == 0)
- return 0;
- return (d1.flags >> 31) | 1;
- }
- if ((d1.Low | d1.Mid | d1.High) == 0)
- return -((d2.flags >> 31) | 1);
- int sign = (d1.flags >> 31) - (d2.flags >> 31);
- if (sign != 0)
- return sign;
- return VarDecCmpSub(in d1, in d2);
- }
- private static int VarDecCmpSub(in decimal d1, in decimal d2)
- {
- int flags = d2.flags;
- int sign = (flags >> 31) | 1;
- int scale = flags - d1.flags;
- ulong low64 = d1.Low64;
- uint high = d1.High;
- ulong d2Low64 = d2.Low64;
- uint d2High = d2.High;
- if (scale != 0)
- {
- scale >>= ScaleShift;
- // Scale factors are not equal. Assume that a larger scale factor (more decimal places) is likely to mean that number is smaller.
- // Start by guessing that the right operand has the larger scale factor.
- if (scale < 0)
- {
- // Guessed scale factor wrong. Swap operands.
- scale = -scale;
- sign = -sign;
- ulong tmp64 = low64;
- low64 = d2Low64;
- d2Low64 = tmp64;
- uint tmp = high;
- high = d2High;
- d2High = tmp;
- }
- // d1 will need to be multiplied by 10^scale so it will have the same scale as d2.
- // Scaling loop, up to 10^9 at a time.
- do
- {
- uint power = scale >= MaxInt32Scale ? TenToPowerNine : s_powers10[scale];
- ulong tmpLow = UInt32x32To64((uint)low64, power);
- ulong tmp = UInt32x32To64((uint)(low64 >> 32), power) + (tmpLow >> 32);
- low64 = (uint)tmpLow + (tmp << 32);
- tmp >>= 32;
- tmp += UInt32x32To64(high, power);
- // If the scaled value has more than 96 significant bits then it's greater than d2
- if (tmp > uint.MaxValue)
- return sign;
- high = (uint)tmp;
- } while ((scale -= MaxInt32Scale) > 0);
- }
- uint cmpHigh = high - d2High;
- if (cmpHigh != 0)
- {
- // check for overflow
- if (cmpHigh > high)
- sign = -sign;
- return sign;
- }
- ulong cmpLow64 = low64 - d2Low64;
- if (cmpLow64 == 0)
- sign = 0;
- // check for overflow
- else if (cmpLow64 > low64)
- sign = -sign;
- return sign;
- }
- /// <summary>
- /// Decimal Multiply
- /// </summary>
- internal static unsafe void VarDecMul(ref DecCalc d1, ref DecCalc d2)
- {
- int scale = (byte)(d1.uflags + d2.uflags >> ScaleShift);
- ulong tmp;
- uint hiProd;
- Buf24 bufProd;
- _ = &bufProd; // workaround for CS0165
- DebugPoison(ref bufProd);
- if ((d1.High | d1.Mid) == 0)
- {
- if ((d2.High | d2.Mid) == 0)
- {
- // Upper 64 bits are zero.
- //
- ulong low64 = UInt32x32To64(d1.Low, d2.Low);
- if (scale > DEC_SCALE_MAX)
- {
- // Result scale is too big. Divide result by power of 10 to reduce it.
- // If the amount to divide by is > 19 the result is guaranteed
- // less than 1/2. [max value in 64 bits = 1.84E19]
- //
- if (scale > DEC_SCALE_MAX + MaxInt64Scale)
- goto ReturnZero;
- scale -= DEC_SCALE_MAX + 1;
- ulong power = s_ulongPowers10[scale];
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- tmp = low64 / power;
- ulong remainder = low64 - tmp * power;
- low64 = tmp;
- // Round result. See if remainder >= 1/2 of divisor.
- // Divisor is a power of 10, so it is always even.
- //
- power >>= 1;
- if (remainder >= power && (remainder > power || ((uint)low64 & 1) > 0))
- low64++;
- scale = DEC_SCALE_MAX;
- }
- d1.Low64 = low64;
- d1.uflags = ((d2.uflags ^ d1.uflags) & SignMask) | ((uint)scale << ScaleShift);
- return;
- }
- else
- {
- // Left value is 32-bit, result fits in 4 uints
- tmp = UInt32x32To64(d1.Low, d2.Low);
- bufProd.U0 = (uint)tmp;
- tmp = UInt32x32To64(d1.Low, d2.Mid) + (tmp >> 32);
- bufProd.U1 = (uint)tmp;
- tmp >>= 32;
- if (d2.High != 0)
- {
- tmp += UInt32x32To64(d1.Low, d2.High);
- if (tmp > uint.MaxValue)
- {
- bufProd.Mid64 = tmp;
- hiProd = 3;
- goto SkipScan;
- }
- }
- bufProd.U2 = (uint)tmp;
- hiProd = 2;
- }
- }
- else if ((d2.High | d2.Mid) == 0)
- {
- // Right value is 32-bit, result fits in 4 uints
- tmp = UInt32x32To64(d2.Low, d1.Low);
- bufProd.U0 = (uint)tmp;
- tmp = UInt32x32To64(d2.Low, d1.Mid) + (tmp >> 32);
- bufProd.U1 = (uint)tmp;
- tmp >>= 32;
- if (d1.High != 0)
- {
- tmp += UInt32x32To64(d2.Low, d1.High);
- if (tmp > uint.MaxValue)
- {
- bufProd.Mid64 = tmp;
- hiProd = 3;
- goto SkipScan;
- }
- }
- bufProd.U2 = (uint)tmp;
- hiProd = 2;
- }
- else
- {
- // Both operands have bits set in the upper 64 bits.
- //
- // Compute and accumulate the 9 partial products into a
- // 192-bit (24-byte) result.
- //
- // [l-h][l-m][l-l] left high, middle, low
- // x [r-h][r-m][r-l] right high, middle, low
- // ------------------------------
- //
- // [0-h][0-l] l-l * r-l
- // [1ah][1al] l-l * r-m
- // [1bh][1bl] l-m * r-l
- // [2ah][2al] l-m * r-m
- // [2bh][2bl] l-l * r-h
- // [2ch][2cl] l-h * r-l
- // [3ah][3al] l-m * r-h
- // [3bh][3bl] l-h * r-m
- // [4-h][4-l] l-h * r-h
- // ------------------------------
- // [p-5][p-4][p-3][p-2][p-1][p-0] prod[] array
- //
- tmp = UInt32x32To64(d1.Low, d2.Low);
- bufProd.U0 = (uint)tmp;
- ulong tmp2 = UInt32x32To64(d1.Low, d2.Mid) + (tmp >> 32);
- tmp = UInt32x32To64(d1.Mid, d2.Low);
- tmp += tmp2; // this could generate carry
- bufProd.U1 = (uint)tmp;
- if (tmp < tmp2) // detect carry
- tmp2 = (tmp >> 32) | (1UL << 32);
- else
- tmp2 = tmp >> 32;
- tmp = UInt32x32To64(d1.Mid, d2.Mid) + tmp2;
- if ((d1.High | d2.High) > 0)
- {
- // Highest 32 bits is non-zero. Calculate 5 more partial products.
- //
- tmp2 = UInt32x32To64(d1.Low, d2.High);
- tmp += tmp2; // this could generate carry
- uint tmp3 = 0;
- if (tmp < tmp2) // detect carry
- tmp3 = 1;
- tmp2 = UInt32x32To64(d1.High, d2.Low);
- tmp += tmp2; // this could generate carry
- bufProd.U2 = (uint)tmp;
- if (tmp < tmp2) // detect carry
- tmp3++;
- tmp2 = ((ulong)tmp3 << 32) | (tmp >> 32);
- tmp = UInt32x32To64(d1.Mid, d2.High);
- tmp += tmp2; // this could generate carry
- tmp3 = 0;
- if (tmp < tmp2) // detect carry
- tmp3 = 1;
- tmp2 = UInt32x32To64(d1.High, d2.Mid);
- tmp += tmp2; // this could generate carry
- bufProd.U3 = (uint)tmp;
- if (tmp < tmp2) // detect carry
- tmp3++;
- tmp = ((ulong)tmp3 << 32) | (tmp >> 32);
- bufProd.High64 = UInt32x32To64(d1.High, d2.High) + tmp;
- hiProd = 5;
- }
- else
- {
- bufProd.Mid64 = tmp;
- hiProd = 3;
- }
- }
- // Check for leading zero uints on the product
- //
- uint* product = (uint*)&bufProd;
- while (product[(int)hiProd] == 0)
- {
- if (hiProd == 0)
- goto ReturnZero;
- hiProd--;
- }
- SkipScan:
- if (hiProd > 2 || scale > DEC_SCALE_MAX)
- {
- scale = ScaleResult(&bufProd, hiProd, scale);
- }
- d1.Low64 = bufProd.Low64;
- d1.High = bufProd.U2;
- d1.uflags = ((d2.uflags ^ d1.uflags) & SignMask) | ((uint)scale << ScaleShift);
- return;
- ReturnZero:
- d1 = default;
- }
- /// <summary>
- /// Convert float to Decimal
- /// </summary>
- internal static void VarDecFromR4(float input, out DecCalc result)
- {
- result = default;
- // The most we can scale by is 10^28, which is just slightly more
- // than 2^93. So a float with an exponent of -94 could just
- // barely reach 0.5, but smaller exponents will always round to zero.
- //
- const uint SNGBIAS = 126;
- int exp = (int)(GetExponent(input) - SNGBIAS);
- if (exp < -94)
- return; // result should be zeroed out
- if (exp > 96)
- Number.ThrowOverflowException(TypeCode.Decimal);
- uint flags = 0;
- if (input < 0)
- {
- input = -input;
- flags = SignMask;
- }
- // Round the input to a 7-digit integer. The R4 format has
- // only 7 digits of precision, and we want to keep garbage digits
- // out of the Decimal were making.
- //
- // Calculate max power of 10 input value could have by multiplying
- // the exponent by log10(2). Using scaled integer multiplcation,
- // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
- //
- double dbl = input;
- int power = 6 - ((exp * 19728) >> 16);
- // power is between -22 and 35
- if (power >= 0)
- {
- // We have less than 7 digits, scale input up.
- //
- if (power > DEC_SCALE_MAX)
- power = DEC_SCALE_MAX;
- dbl *= s_doublePowers10[power];
- }
- else
- {
- if (power != -1 || dbl >= 1E7)
- dbl /= s_doublePowers10[-power];
- else
- power = 0; // didn't scale it
- }
- Debug.Assert(dbl < 1E7);
- if (dbl < 1E6 && power < DEC_SCALE_MAX)
- {
- dbl *= 10;
- power++;
- Debug.Assert(dbl >= 1E6);
- }
- // Round to integer
- //
- uint mant;
- // with SSE4.1 support ROUNDSD can be used
- if (X86.Sse41.IsSupported)
- mant = (uint)(int)Math.Round(dbl);
- else
- {
- mant = (uint)(int)dbl;
- dbl -= (int)mant; // difference between input & integer
- if (dbl > 0.5 || dbl == 0.5 && (mant & 1) != 0)
- mant++;
- }
- if (mant == 0)
- return; // result should be zeroed out
- if (power < 0)
- {
- // Add -power factors of 10, -power <= (29 - 7) = 22.
- //
- power = -power;
- if (power < 10)
- {
- result.Low64 = UInt32x32To64(mant, s_powers10[power]);
- }
- else
- {
- // Have a big power of 10.
- //
- if (power > 18)
- {
- ulong low64 = UInt32x32To64(mant, s_powers10[power - 18]);
- UInt64x64To128(low64, TenToPowerEighteen, ref result);
- }
- else
- {
- ulong low64 = UInt32x32To64(mant, s_powers10[power - 9]);
- ulong hi64 = UInt32x32To64(TenToPowerNine, (uint)(low64 >> 32));
- low64 = UInt32x32To64(TenToPowerNine, (uint)low64);
- result.Low = (uint)low64;
- hi64 += low64 >> 32;
- result.Mid = (uint)hi64;
- hi64 >>= 32;
- result.High = (uint)hi64;
- }
- }
- }
- else
- {
- // Factor out powers of 10 to reduce the scale, if possible.
- // The maximum number we could factor out would be 6. This
- // comes from the fact we have a 7-digit number, and the
- // MSD must be non-zero -- but the lower 6 digits could be
- // zero. Note also the scale factor is never negative, so
- // we can't scale by any more than the power we used to
- // get the integer.
- //
- int lmax = power;
- if (lmax > 6)
- lmax = 6;
- if ((mant & 0xF) == 0 && lmax >= 4)
- {
- const uint den = 10000;
- uint div = mant / den;
- if (mant == div * den)
- {
- mant = div;
- power -= 4;
- lmax -= 4;
- }
- }
- if ((mant & 3) == 0 && lmax >= 2)
- {
- const uint den = 100;
- uint div = mant / den;
- if (mant == div * den)
- {
- mant = div;
- power -= 2;
- lmax -= 2;
- }
- }
- if ((mant & 1) == 0 && lmax >= 1)
- {
- const uint den = 10;
- uint div = mant / den;
- if (mant == div * den)
- {
- mant = div;
- power--;
- }
- }
- flags |= (uint)power << ScaleShift;
- result.Low = mant;
- }
- result.uflags = flags;
- }
- /// <summary>
- /// Convert double to Decimal
- /// </summary>
- internal static void VarDecFromR8(double input, out DecCalc result)
- {
- result = default;
- // The most we can scale by is 10^28, which is just slightly more
- // than 2^93. So a float with an exponent of -94 could just
- // barely reach 0.5, but smaller exponents will always round to zero.
- //
- const uint DBLBIAS = 1022;
- int exp = (int)(GetExponent(input) - DBLBIAS);
- if (exp < -94)
- return; // result should be zeroed out
- if (exp > 96)
- Number.ThrowOverflowException(TypeCode.Decimal);
- uint flags = 0;
- if (input < 0)
- {
- input = -input;
- flags = SignMask;
- }
- // Round the input to a 15-digit integer. The R8 format has
- // only 15 digits of precision, and we want to keep garbage digits
- // out of the Decimal were making.
- //
- // Calculate max power of 10 input value could have by multiplying
- // the exponent by log10(2). Using scaled integer multiplcation,
- // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
- //
- double dbl = input;
- int power = 14 - ((exp * 19728) >> 16);
- // power is between -14 and 43
- if (power >= 0)
- {
- // We have less than 15 digits, scale input up.
- //
- if (power > DEC_SCALE_MAX)
- power = DEC_SCALE_MAX;
- dbl *= s_doublePowers10[power];
- }
- else
- {
- if (power != -1 || dbl >= 1E15)
- dbl /= s_doublePowers10[-power];
- else
- power = 0; // didn't scale it
- }
- Debug.Assert(dbl < 1E15);
- if (dbl < 1E14 && power < DEC_SCALE_MAX)
- {
- dbl *= 10;
- power++;
- Debug.Assert(dbl >= 1E14);
- }
- // Round to int64
- //
- ulong mant;
- // with SSE4.1 support ROUNDSD can be used
- if (X86.Sse41.IsSupported)
- mant = (ulong)(long)Math.Round(dbl);
- else
- {
- mant = (ulong)(long)dbl;
- dbl -= (long)mant; // difference between input & integer
- if (dbl > 0.5 || dbl == 0.5 && (mant & 1) != 0)
- mant++;
- }
- if (mant == 0)
- return; // result should be zeroed out
- if (power < 0)
- {
- // Add -power factors of 10, -power <= (29 - 15) = 14.
- //
- power = -power;
- if (power < 10)
- {
- var pow10 = s_powers10[power];
- ulong low64 = UInt32x32To64((uint)mant, pow10);
- ulong hi64 = UInt32x32To64((uint)(mant >> 32), pow10);
- result.Low = (uint)low64;
- hi64 += low64 >> 32;
- result.Mid = (uint)hi64;
- hi64 >>= 32;
- result.High = (uint)hi64;
- }
- else
- {
- // Have a big power of 10.
- //
- Debug.Assert(power <= 14);
- UInt64x64To128(mant, s_ulongPowers10[power - 1], ref result);
- }
- }
- else
- {
- // Factor out powers of 10 to reduce the scale, if possible.
- // The maximum number we could factor out would be 14. This
- // comes from the fact we have a 15-digit number, and the
- // MSD must be non-zero -- but the lower 14 digits could be
- // zero. Note also the scale factor is never negative, so
- // we can't scale by any more than the power we used to
- // get the integer.
- //
- int lmax = power;
- if (lmax > 14)
- lmax = 14;
- if ((byte)mant == 0 && lmax >= 8)
- {
- const uint den = 100000000;
- ulong div = mant / den;
- if ((uint)mant == (uint)(div * den))
- {
- mant = div;
- power -= 8;
- lmax -= 8;
- }
- }
- if (((uint)mant & 0xF) == 0 && lmax >= 4)
- {
- const uint den = 10000;
- ulong div = mant / den;
- if ((uint)mant == (uint)(div * den))
- {
- mant = div;
- power -= 4;
- lmax -= 4;
- }
- }
- if (((uint)mant & 3) == 0 && lmax >= 2)
- {
- const uint den = 100;
- ulong div = mant / den;
- if ((uint)mant == (uint)(div * den))
- {
- mant = div;
- power -= 2;
- lmax -= 2;
- }
- }
- if (((uint)mant & 1) == 0 && lmax >= 1)
- {
- const uint den = 10;
- ulong div = mant / den;
- if ((uint)mant == (uint)(div * den))
- {
- mant = div;
- power--;
- }
- }
- flags |= (uint)power << ScaleShift;
- result.Low64 = mant;
- }
- result.uflags = flags;
- }
- /// <summary>
- /// Convert Decimal to float
- /// </summary>
- internal static float VarR4FromDec(in decimal value)
- {
- return (float)VarR8FromDec(in value);
- }
- /// <summary>
- /// Convert Decimal to double
- /// </summary>
- internal static double VarR8FromDec(in decimal value)
- {
- // Value taken via reverse engineering the double that corresponds to 2^64. (oleaut32 has ds2to64 = DEFDS(0, 0, DBLBIAS + 65, 0))
- const double ds2to64 = 1.8446744073709552e+019;
- double dbl = ((double)value.Low64 +
- (double)value.High * ds2to64) / s_doublePowers10[value.Scale];
- if (value.IsNegative)
- dbl = -dbl;
- return dbl;
- }
- internal static int GetHashCode(in decimal d)
- {
- if ((d.Low | d.Mid | d.High) == 0)
- return 0;
- uint flags = (uint)d.flags;
- if ((flags & ScaleMask) == 0 || (d.Low & 1) != 0)
- return (int)(flags ^ d.High ^ d.Mid ^ d.Low);
- int scale = (byte)(flags >> ScaleShift);
- uint low = d.Low;
- ulong high64 = ((ulong)d.High << 32) | d.Mid;
- Unscale(ref low, ref high64, ref scale);
- flags = ((flags) & ~ScaleMask) | (uint)scale << ScaleShift;
- return (int)(flags ^ (uint)(high64 >> 32) ^ (uint)high64 ^ low);
- }
- /// <summary>
- /// Divides two decimal values.
- /// On return, d1 contains the result of the operation.
- /// </summary>
- internal static unsafe void VarDecDiv(ref DecCalc d1, ref DecCalc d2)
- {
- Buf12 bufQuo;
- _ = &bufQuo; // workaround for CS0165
- DebugPoison(ref bufQuo);
- uint power;
- int curScale;
- int scale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift);
- bool unscale = false;
- uint tmp;
- if ((d2.High | d2.Mid) == 0)
- {
- // Divisor is only 32 bits. Easy divide.
- //
- uint den = d2.Low;
- if (den == 0)
- throw new DivideByZeroException();
- bufQuo.Low64 = d1.Low64;
- bufQuo.U2 = d1.High;
- uint remainder = Div96By32(ref bufQuo, den);
- for (;;)
- {
- if (remainder == 0)
- {
- if (scale < 0)
- {
- curScale = Math.Min(9, -scale);
- goto HaveScale;
- }
- break;
- }
- // We need to unscale if and only if we have a non-zero remainder
- unscale = true;
- // We have computed a quotient based on the natural scale
- // ( <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 bufQuo[2].bufQuo[1] >= 2^32 / 10^9
- // = 4.294 967 296. So the upper limit is bufQuo[2] == 4 and
- // bufQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+. Since
- // quotient bits in bufQuo[0] could be all 1's, then 1,266,874,888
- // is the largest value in bufQuo[1] (when bufQuo[2] == 4) that is
- // assured not to overflow.
- //
- if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0)
- {
- // No more scaling to be done, but remainder is non-zero.
- // Round quotient.
- //
- tmp = remainder << 1;
- if (tmp < remainder || tmp >= den && (tmp > den || (bufQuo.U0 & 1) != 0))
- goto RoundUp;
- break;
- }
- HaveScale:
- power = s_powers10[curScale];
- scale += curScale;
- if (IncreaseScale(ref bufQuo, power) != 0)
- goto ThrowOverflow;
- ulong num = UInt32x32To64(remainder, power);
- // TODO: https://github.com/dotnet/coreclr/issues/3439
- uint div = (uint)(num / den);
- remainder = (uint)num - div * den;
- if (!Add32To96(ref bufQuo, div))
- {
- scale = OverflowUnscale(ref bufQuo, scale, remainder != 0);
- break;
- }
- } // for (;;)
- }
- else
- {
- // Divisor has bits set in the upper 64 bits.
- //
- // Divisor must be fully normalized (shifted so bit 31 of the most
- // significant uint is 1). Locate the MSB so we know how much to
- // normalize by. The dividend will be shifted by the same amount so
- // the quotient is not changed.
- //
- tmp = d2.High;
- if (tmp == 0)
- tmp = d2.Mid;
- curScale = BitOperations.LeadingZeroCount(tmp);
- // Shift both dividend and divisor left by curScale.
- //
- Buf16 bufRem;
- _ = &bufRem; // workaround for CS0165
- DebugPoison(ref bufRem);
- bufRem.Low64 = d1.Low64 << curScale;
- bufRem.High64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - curScale);
- ulong divisor = d2.Low64 << curScale;
- if (d2.High == 0)
- {
- // Have a 64-bit divisor in sdlDivisor. The remainder
- // (currently 96 bits spread over 4 uints) will be < divisor.
- //
- bufQuo.U2 = 0;
- bufQuo.U1 = Div96By64(ref *(Buf12*)&bufRem.U1, divisor);
- bufQuo.U0 = Div96By64(ref *(Buf12*)&bufRem, divisor);
- for (;;)
- {
- if (bufRem.Low64 == 0)
- {
- if (scale < 0)
- {
- curScale = Math.Min(9, -scale);
- goto HaveScale64;
- }
- break;
- }
- // We need to unscale if and only if we have a non-zero remainder
- unscale = true;
- // Remainder is non-zero. Scale up quotient and remainder by
- // powers of 10 so we can compute more significant bits.
- //
- if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0)
- {
- // No more scaling to be done, but remainder is non-zero.
- // Round quotient.
- //
- ulong tmp64 = bufRem.Low64;
- if ((long)tmp64 < 0 || (tmp64 <<= 1) > divisor ||
- (tmp64 == divisor && (bufQuo.U0 & 1) != 0))
- goto RoundUp;
- break;
- }
- HaveScale64:
- power = s_powers10[curScale];
- scale += curScale;
- if (IncreaseScale(ref bufQuo, power) != 0)
- goto ThrowOverflow;
- IncreaseScale64(ref *(Buf12*)&bufRem, power);
- tmp = Div96By64(ref *(Buf12*)&bufRem, divisor);
- if (!Add32To96(ref bufQuo, tmp))
- {
- scale = OverflowUnscale(ref bufQuo, scale, bufRem.Low64 != 0);
- break;
- }
- } // for (;;)
- }
- else
- {
- // Have a 96-bit divisor in bufDivisor.
- //
- // Start by finishing the shift left by curScale.
- //
- Buf12 bufDivisor;
- _ = &bufDivisor; // workaround for CS0165
- DebugPoison(ref bufDivisor);
- bufDivisor.Low64 = divisor;
- bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - curScale));
- // The remainder (currently 96 bits spread over 4 uints) will be < divisor.
- //
- bufQuo.Low64 = Div128By96(ref bufRem, ref bufDivisor);
- bufQuo.U2 = 0;
- for (;;)
- {
- if ((bufRem.Low64 | bufRem.U2) == 0)
- {
- if (scale < 0)
- {
- curScale = Math.Min(9, -scale);
- goto HaveScale96;
- }
- break;
- }
- // We need to unscale if and only if we have a non-zero remainder
- unscale = true;
- // Remainder is non-zero. Scale up quotient and remainder by
- // powers of 10 so we can compute more significant bits.
- //
- if (scale == DEC_SCALE_MAX || (curScale = SearchScale(ref bufQuo, scale)) == 0)
- {
- // No more scaling to be done, but remainder is non-zero.
- // Round quotient.
- //
- if ((int)bufRem.U2 < 0)
- {
- goto RoundUp;
- }
- tmp = bufRem.U1 >> 31;
- bufRem.Low64 <<= 1;
- bufRem.U2 = (bufRem.U2 << 1) + tmp;
- if (bufRem.U2 > bufDivisor.U2 || bufRem.U2 == bufDivisor.U2 &&
- (bufRem.Low64 > bufDivisor.Low64 || bufRem.Low64 == bufDivisor.Low64 &&
- (bufQuo.U0 & 1) != 0))
- goto RoundUp;
- break;
- }
- HaveScale96:
- power = s_powers10[curScale];
- scale += curScale;
- if (IncreaseScale(ref bufQuo, power) != 0)
- goto ThrowOverflow;
- bufRem.U3 = IncreaseScale(ref *(Buf12*)&bufRem, power);
- tmp = Div128By96(ref bufRem, ref bufDivisor);
- if (!Add32To96(ref bufQuo, tmp))
- {
- scale = OverflowUnscale(ref bufQuo, scale, (bufRem.Low64 | bufRem.High64) != 0);
- break;
- }
- } // for (;;)
- }
- }
- Unscale:
- if (unscale)
- {
- uint low = bufQuo.U0;
- ulong high64 = bufQuo.High64;
- Unscale(ref low, ref high64, ref scale);
- d1.Low = low;
- d1.Mid = (uint)high64;
- d1.High = (uint)(high64 >> 32);
- }
- else
- {
- d1.Low64 = bufQuo.Low64;
- d1.High = bufQuo.U2;
- }
- d1.uflags = ((d1.uflags ^ d2.uflags) & SignMask) | ((uint)scale << ScaleShift);
- return;
- RoundUp:
- {
- if (++bufQuo.Low64 == 0 && ++bufQuo.U2 == 0)
- {
- scale = OverflowUnscale(ref bufQuo, scale, true);
- }
- goto Unscale;
- }
- ThrowOverflow:
- Number.ThrowOverflowException(TypeCode.Decimal);
- }
- /// <summary>
- /// Computes the remainder between two decimals.
- /// On return, d1 contains the result of the operation and d2 is trashed.
- /// </summary>
- 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;
- // The divisor is smaller than the dividend and both are non-zero. Calculate the integer remainder using the larger scaling factor.
- int scale = (sbyte)(d1.uflags - d2.uflags >> ScaleShift);
- if (scale > 0)
- {
- // Divisor scale can always be increased to dividend scale for remainder calculation.
- do
- {
- uint power = scale >= MaxInt32Scale ? TenToPowerNine : s_powers10[scale];
- ulong tmp = UInt32x32To64(d2.Low, power);
- d2.Low = (uint)tmp;
- tmp >>= 32;
- tmp += (d2.Mid + ((ulong)d2.High << 32)) * power;
- d2.Mid = (uint)tmp;
- d2.High = (uint)(tmp >> 32);
- } while ((scale -= MaxInt32Scale) > 0);
- scale = 0;
- }
- do
- {
- if (scale < 0)
- {
- d1.uflags = d2.uflags;
- // Try to scale up dividend to match divisor.
- Buf12 bufQuo;
- unsafe { _ = &bufQuo; } // workaround for CS0165
- DebugPoison(ref bufQuo);
- bufQuo.Low64 = d1.Low64;
- bufQuo.U2 = d1.High;
- do
- {
- int iCurScale = SearchScale(ref bufQuo, DEC_SCALE_MAX + scale);
- if (iCurScale == 0)
- break;
- uint power = iCurScale >= MaxInt32Scale ? TenToPowerNine : s_powers10[iCurScale];
- scale += iCurScale;
- ulong tmp = UInt32x32To64(bufQuo.U0, power);
- bufQuo.U0 = (uint)tmp;
- tmp >>= 32;
- bufQuo.High64 = tmp + bufQuo.High64 * power;
- if (power != TenToPowerNine)
- break;
- }
- while (scale < 0);
- d1.Low64 = bufQuo.Low64;
- d1.High = bufQuo.U2;
- }
- if (d1.High == 0)
- {
- Debug.Assert(d2.High == 0);
- Debug.Assert(scale == 0);
- d1.Low64 %= d2.Low64;
- return;
- }
- else if ((d2.High | d2.Mid) == 0)
- {
- uint den = d2.Low;
- ulong tmp = ((ulong)d1.High << 32) | d1.Mid;
- tmp = ((tmp % den) << 32) | d1.Low;
- d1.Low64 = tmp % den;
- d1.High = 0;
- }
- else
- {
- VarDecModFull(ref d1, ref d2, scale);
- return;
- }
- } while (scale < 0);
- }
- private static unsafe void VarDecModFull(ref DecCalc d1, ref DecCalc d2, int scale)
- {
- // Divisor has bits set in the upper 64 bits.
- //
- // Divisor must be fully normalized (shifted so bit 31 of the most significant uint is 1).
- // Locate the MSB so we know how much to normalize by.
- // The dividend will be shifted by the same amount so the quotient is not changed.
- //
- uint tmp = d2.High;
- if (tmp == 0)
- tmp = d2.Mid;
- int shift = BitOperations.LeadingZeroCount(tmp);
- Buf28 b;
- _ = &b; // workaround for CS0165
- DebugPoison(ref b);
- b.Buf24.Low64 = d1.Low64 << shift;
- b.Buf24.Mid64 = (d1.Mid + ((ulong)d1.High << 32)) >> (32 - shift);
- // The dividend might need to be scaled up to 221 significant bits.
- // Maximum scaling is required when the divisor is 2^64 with scale 28 and is left shifted 31 bits
- // and the dividend is decimal.MaxValue: (2^96 - 1) * 10^28 << 31 = 221 bits.
- uint high = 3;
- while (scale < 0)
- {
- uint power = scale <= -MaxInt32Scale ? TenToPowerNine : s_powers10[-scale];
- uint* buf = (uint*)&b;
- ulong tmp64 = UInt32x32To64(b.Buf24.U0, power);
- b.Buf24.U0 = (uint)tmp64;
- for (int i = 1; i <= high; i++)
- {
- tmp64 >>= 32;
- tmp64 += UInt32x32To64(buf[i], power);
- buf[i] = (uint)tmp64;
- }
- // The high bit of the dividend must not be set.
- if (tmp64 > int.MaxValue)
- {
- Debug.Assert(high + 1 < b.Length);
- buf[++high] = (uint)(tmp64 >> 32);
- }
- scale += MaxInt32Scale;
- }
- if (d2.High == 0)
- {
- ulong divisor = d2.Low64 << shift;
- switch (high)
- {
- case 6:
- Div96By64(ref *(Buf12*)&b.Buf24.U4, divisor);
- goto case 5;
- case 5:
- Div96By64(ref *(Buf12*)&b.Buf24.U3, divisor);
- goto case 4;
- case 4:
- Div96By64(ref *(Buf12*)&b.Buf24.U2, divisor);
- break;
- }
- Div96By64(ref *(Buf12*)&b.Buf24.U1, divisor);
- Div96By64(ref *(Buf12*)&b, divisor);
- d1.Low64 = b.Buf24.Low64 >> shift;
- d1.High = 0;
- }
- else
- {
- Buf12 bufDivisor;
- _ = &bufDivisor; // workaround for CS0165
- DebugPoison(ref bufDivisor);
- bufDivisor.Low64 = d2.Low64 << shift;
- bufDivisor.U2 = (uint)((d2.Mid + ((ulong)d2.High << 32)) >> (32 - shift));
- switch (high)
- {
- case 6:
- Div128By96(ref *(Buf16*)&b.Buf24.U3, ref bufDivisor);
- goto case 5;
- case 5:
- Div128By96(ref *(Buf16*)&b.Buf24.U2, ref bufDivisor);
- goto case 4;
- case 4:
- Div128By96(ref *(Buf16*)&b.Buf24.U1, ref bufDivisor);
- break;
- }
- Div128By96(ref *(Buf16*)&b, ref bufDivisor);
- d1.Low64 = (b.Buf24.Low64 >> shift) + ((ulong)b.Buf24.U2 << (32 - shift) << 32);
- d1.High = b.Buf24.U2 >> shift;
- }
- }
- // Does an in-place round by the specified scale
- internal static void InternalRound(ref DecCalc d, uint scale, MidpointRounding 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 <= MidpointRounding.ToZero)
- 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 == MidpointRounding.ToZero)
- goto done;
- else if (mode == MidpointRounding.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 == MidpointRounding.AwayFromZero)
- {
- // Round away from zero at the mid point.
- remainder <<= 1;
- if (power > remainder)
- goto done;
- }
- else if (mode == MidpointRounding.ToNegativeInfinity)
- {
- // 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 == MidpointRounding.ToPositiveInfinity);
- // Round toward infinity if we have chopped off a non-zero amount from a positive value.
- if ((remainder | sticky) == 0 || d.IsNegative)
- goto done;
- }
- if (++d.Low64 == 0)
- d.uhi++;
- done:
- return;
- }
- internal static uint DecDivMod1E9(ref DecCalc value)
- {
- ulong high64 = ((ulong)value.uhi << 32) + value.umid;
- ulong div64 = high64 / TenToPowerNine;
- value.uhi = (uint)(div64 >> 32);
- value.umid = (uint)div64;
- ulong num = ((high64 - (uint)div64 * TenToPowerNine) << 32) + value.ulo;
- uint div = (uint)(num / TenToPowerNine);
- value.ulo = div;
- return (uint)num - div * TenToPowerNine;
- }
- struct PowerOvfl
- {
- public readonly uint Hi;
- public readonly ulong MidLo;
- public PowerOvfl(uint hi, uint mid, uint lo)
- {
- Hi = hi;
- MidLo = ((ulong)mid << 32) + lo;
- }
- }
- static readonly PowerOvfl[] PowerOvflValues = new[]
- {
- // This is a table of the largest values that can be in the upper two
- // uints of a 96-bit number that will not overflow when multiplied
- // by a given power. For the upper word, this is a table of
- // 2^32 / 10^n for 1 <= n <= 8. For the lower word, this is the
- // remaining fraction part * 2^32. 2^32 = 4294967296.
- //
- new PowerOvfl(429496729, 2576980377, 2576980377), // 10^1 remainder 0.6
- new PowerOvfl(42949672, 4123168604, 687194767), // 10^2 remainder 0.16
- new PowerOvfl(4294967, 1271310319, 2645699854), // 10^3 remainder 0.616
- new PowerOvfl(429496, 3133608139, 694066715), // 10^4 remainder 0.1616
- new PowerOvfl(42949, 2890341191, 2216890319), // 10^5 remainder 0.51616
- new PowerOvfl(4294, 4154504685, 2369172679), // 10^6 remainder 0.551616
- new PowerOvfl(429, 2133437386, 4102387834), // 10^7 remainder 0.9551616
- new PowerOvfl(42, 4078814305, 410238783), // 10^8 remainder 0.09991616
- };
- [StructLayout(LayoutKind.Explicit)]
- private struct Buf12
- {
- [FieldOffset(0 * 4)]
- public uint U0;
- [FieldOffset(1 * 4)]
- public uint U1;
- [FieldOffset(2 * 4)]
- public uint U2;
- [FieldOffset(0)]
- private ulong ulo64LE;
- [FieldOffset(4)]
- private ulong uhigh64LE;
- public ulong Low64
- {
- #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;
- }
- private struct Buf28
- {
- public Buf24 Buf24;
- public uint U6;
- public int Length => 7;
- }
- }
- }
- }
|