1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924 |
- using System;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
-
- namespace Unity.Burst
- {
- #if BURST_COMPILER_SHARED
- internal static partial class BurstStringInternal
- #else
- internal static partial class BurstString
- #endif
- {
- // This file provides an implementation for formatting floating point numbers that is compatible
- // with Burst
-
- // ------------------------------------------------------------------------------
- // Part of code translated to C# from http://www.ryanjuckett.com/programming/printing-floating-point-numbers
- // with the following license:
- /******************************************************************************
- Copyright (c) 2014 Ryan Juckett
- http://www.ryanjuckett.com/
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
- ******************************************************************************/
-
- //******************************************************************************
- // Get the log base 2 of a 32-bit unsigned integer.
- // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
- //******************************************************************************
- private static readonly byte[] logTable = new byte[256]
- {
- 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
- };
-
- private static uint LogBase2(uint val)
- {
- uint temp;
- temp = val >> 24;
- if (temp != 0)
- return (uint)(24 + logTable[(int)temp]);
-
- temp = val >> 16;
- if (temp != 0)
- return (uint)(16 + logTable[temp]);
-
- temp = val >> 8;
- if (temp != 0)
- return (uint)(8 + logTable[temp]);
-
- return logTable[val];
- }
-
- //******************************************************************************
- // This structure stores a high precision unsigned integer. It uses a buffer
- // of 32 bit integer blocks along with a length. The lowest bits of the integer
- // are stored at the start of the buffer and the length is set to the minimum
- // value that contains the integer. Thus, there are never any zero blocks at the
- // end of the buffer.
- //******************************************************************************
- public unsafe struct tBigInt
- {
- //******************************************************************************
- // Maximum number of 32 bit blocks needed in high precision arithmetic
- // to print out 64 bit IEEE floating point values.
- //******************************************************************************
- const int c_BigInt_MaxBlocks = 35;
-
- //// Copy integer
- //tBigInt & operator=(tBigInt &rhs)
- //{
- // uint length = rhs.m_length;
- // uint* pLhsCur = m_blocks;
- // for (uint* pRhsCur = rhs.m_blocks, *pRhsEnd = pRhsCur + length;
- // pRhsCur != pRhsEnd;
- // ++pLhsCur, ++pRhsCur)
- // {
- // *pLhsCur = *pRhsCur;
- // }
- // m_length = length;
- // return *this;
- //}
-
- // Data accessors
- public int GetLength() { return m_length; }
- public uint GetBlock(int idx) { return m_blocks[idx]; }
-
- // Zero helper functions
- public void SetZero() { m_length = 0; }
- public bool IsZero() { return m_length == 0; }
-
- // Basic type accessors
- public void SetU64(ulong val)
- {
- if (val > 0xFFFFFFFF)
- {
- m_blocks[0] = (uint)(val & 0xFFFFFFFF);
- m_blocks[1] = (uint)(val >> 32 & 0xFFFFFFFF);
- m_length = 2;
- }
- else if (val != 0)
- {
- m_blocks[0] = (uint)(val & 0xFFFFFFFF);
- m_length = 1;
- }
- else
- {
- m_length = 0;
- }
- }
-
- public void SetU32(uint val)
- {
- if (val != 0)
- {
- m_blocks[0] = val;
- m_length = (val != 0) ? 1 : 0;
- }
- else
- {
- m_length = 0;
- }
- }
-
- public uint GetU32() { return (m_length == 0) ? 0 : m_blocks[0]; }
-
- // Member data
- public int m_length;
- public fixed uint m_blocks[c_BigInt_MaxBlocks];
- }
-
- //******************************************************************************
- // Returns 0 if (lhs = rhs), negative if (lhs < rhs), positive if (lhs > rhs)
- //******************************************************************************
- private static unsafe int BigInt_Compare(in tBigInt lhs, in tBigInt rhs)
- {
- // A bigger length implies a bigger number.
- int lengthDiff = lhs.m_length - rhs.m_length;
- if (lengthDiff != 0)
- return lengthDiff;
-
- // Compare blocks one by one from high to low.
- for (int i = (int)lhs.m_length - 1; i >= 0; --i)
- {
- if (lhs.m_blocks[i] == rhs.m_blocks[i])
- continue;
- else if (lhs.m_blocks[i] > rhs.m_blocks[i])
- return 1;
- else
- return -1;
- }
-
- // no blocks differed
- return 0;
- }
-
- //******************************************************************************
- // result = lhs + rhs
- //******************************************************************************
- private static unsafe void BigInt_Add(out tBigInt pResult, in tBigInt lhs, in tBigInt rhs)
- {
- if (lhs.m_length < rhs.m_length)
- {
- BigInt_Add_internal(out pResult, rhs, lhs);
- }
- else
- {
- BigInt_Add_internal(out pResult, lhs, rhs);
- }
- }
- private static unsafe void BigInt_Add_internal(out tBigInt pResult, in tBigInt pLarge, in tBigInt pSmall)
- {
- int largeLen = pLarge.m_length;
- int smallLen = pSmall.m_length;
-
- // The output will be at least as long as the largest input
- pResult.m_length = largeLen;
-
- // Add each block and add carry the overflow to the next block
- ulong carry = 0;
- fixed (uint * pLargeCur1 = pLarge.m_blocks)
- fixed (uint * pSmallCur1 = pSmall.m_blocks)
- fixed (uint * pResultCur1 = pResult.m_blocks)
- {
- uint* pLargeCur = pLargeCur1;
- uint* pSmallCur = pSmallCur1;
- uint* pResultCur = pResultCur1;
- uint* pLargeEnd = pLargeCur + largeLen;
- uint* pSmallEnd = pSmallCur + smallLen;
-
- while (pSmallCur != pSmallEnd)
- {
- ulong sum = carry + (ulong) (*pLargeCur) + (ulong) (*pSmallCur);
- carry = sum >> 32;
- (*pResultCur) = (uint)(sum & 0xFFFFFFFF);
- ++pLargeCur;
- ++pSmallCur;
- ++pResultCur;
- }
-
- // Add the carry to any blocks that only exist in the large operand
- while (pLargeCur != pLargeEnd)
- {
- ulong sum = carry + (ulong) (*pLargeCur);
- carry = sum >> 32;
- (*pResultCur) = (uint)(sum & 0xFFFFFFFF);
- ++pLargeCur;
- ++pResultCur;
- }
-
- // If there's still a carry, append a new block
- if (carry != 0)
- {
- //RJ_ASSERT(carry == 1);
- //RJ_ASSERT((uint)(pResultCur - pResult.m_blocks) == largeLen && (largeLen < c_BigInt_MaxBlocks));
- *pResultCur = 1;
- pResult.m_length = largeLen + 1;
- }
- else
- {
- pResult.m_length = largeLen;
- }
- }
- }
-
- //******************************************************************************
- // result = lhs * rhs
- //******************************************************************************
- private static unsafe void BigInt_Multiply(out tBigInt pResult, in tBigInt lhs, in tBigInt rhs)
- {
- if (lhs.m_length < rhs.m_length)
- {
- BigInt_Multiply_internal(out pResult, rhs, lhs);
- }
- else
- {
- BigInt_Multiply_internal(out pResult, lhs, rhs);
- }
- }
-
- private static unsafe void BigInt_Multiply_internal(out tBigInt pResult, in tBigInt pLarge, in tBigInt pSmall)
- {
- // set the maximum possible result length
- int maxResultLen = pLarge.m_length + pSmall.m_length;
- // RJ_ASSERT( maxResultLen <= c_BigInt_MaxBlocks );
-
- // clear the result data
- // uint * pCur = pResult.m_blocks, *pEnd = pCur + maxResultLen; pCur != pEnd; ++pCur
- for (int i = 0; i < maxResultLen; i++)
- pResult.m_blocks[i] = 0;
-
- // perform standard long multiplication
- fixed (uint *pLargeBeg1 = pLarge.m_blocks)
- {
- uint* pLargeBeg = pLargeBeg1;
- uint* pLargeEnd = pLargeBeg + pLarge.m_length;
-
- // for each small block
- fixed (uint* pResultStart1 = pResult.m_blocks)
- fixed (uint* pSmallCur1 = pSmall.m_blocks)
- {
- uint* pSmallCur = pSmallCur1;
- uint* pSmallEnd = pSmallCur + pSmall.m_length;
- uint* pResultStart = pResultStart1;
- for (; pSmallCur != pSmallEnd; ++pSmallCur, ++pResultStart)
- {
- // if non-zero, multiply against all the large blocks and add into the result
- uint multiplier = *pSmallCur;
- if (multiplier != 0)
- {
- uint* pLargeCur = pLargeBeg;
- uint* pResultCur = pResultStart;
- ulong carry = 0;
- do
- {
- ulong product = (*pResultCur) + (*pLargeCur) * (ulong) multiplier + carry;
- carry = product >> 32;
- *pResultCur = (uint)(product & 0xFFFFFFFF);
- ++pLargeCur;
- ++pResultCur;
- } while (pLargeCur != pLargeEnd);
-
- //RJ_ASSERT(pResultCur < pResult.m_blocks + maxResultLen);
- *pResultCur = (uint) (carry & 0xFFFFFFFF);
- }
- }
-
- // check if the terminating block has no set bits
- if (maxResultLen > 0 && pResult.m_blocks[maxResultLen - 1] == 0)
- pResult.m_length = maxResultLen - 1;
- else
- pResult.m_length = maxResultLen;
- }
- }
- }
-
- //******************************************************************************
- // result = lhs * rhs
- //******************************************************************************
- private static unsafe void BigInt_Multiply(out tBigInt pResult, in tBigInt lhs, uint rhs)
- {
- // perform long multiplication
- uint carry = 0;
- fixed (uint* pResultCur1 = pResult.m_blocks)
- fixed (uint* pLhsCur1 = lhs.m_blocks)
- {
- uint* pResultCur = pResultCur1;
- uint* pLhsCur = pLhsCur1;
- uint* pLhsEnd = pLhsCur + lhs.m_length;
- for (; pLhsCur != pLhsEnd; ++pLhsCur, ++pResultCur)
- {
- ulong product = (ulong) (*pLhsCur) * rhs + carry;
- *pResultCur = (uint) (product & 0xFFFFFFFF);
- carry = (uint)(product >> 32);
- }
-
- // if there is a remaining carry, grow the array
- if (carry != 0)
- {
- // grow the array
- //RJ_ASSERT(lhs.m_length + 1 <= c_BigInt_MaxBlocks);
- *pResultCur = (uint) carry;
- pResult.m_length = lhs.m_length + 1;
- }
- else
- {
- pResult.m_length = lhs.m_length;
- }
- }
- }
-
- //******************************************************************************
- // result = in * 2
- //******************************************************************************
- private static unsafe void BigInt_Multiply2(out tBigInt pResult, in tBigInt input)
- {
- // shift all the blocks by one
- uint carry = 0;
-
- fixed (uint* pResultCur1 = pResult.m_blocks)
- fixed (uint* pLhsCur1 = input.m_blocks)
- {
- uint* pResultCur = pResultCur1;
- uint* pLhsCur = pLhsCur1;
- uint* pLhsEnd = pLhsCur + input.m_length;
- for (; pLhsCur != pLhsEnd; ++pLhsCur, ++pResultCur)
- {
- uint cur = *pLhsCur;
- *pResultCur = (cur << 1) | carry;
- carry = cur >> 31;
- }
-
- if (carry != 0)
- {
- // grow the array
- // RJ_ASSERT(input.m_length + 1 <= c_BigInt_MaxBlocks);
- *pResultCur = carry;
- pResult.m_length = input.m_length + 1;
- }
- else
- {
- pResult.m_length = input.m_length;
- }
- }
- }
-
- //******************************************************************************
- // result = result * 2
- //******************************************************************************
- private static unsafe void BigInt_Multiply2(ref tBigInt pResult)
- {
- // shift all the blocks by one
- uint carry = 0;
-
- fixed (uint* pCur1 = pResult.m_blocks)
- {
- uint* pCur = pCur1;
- uint* pEnd = pCur + pResult.m_length;
- for (; pCur != pEnd; ++pCur)
- {
- uint cur = *pCur;
- *pCur = (cur << 1) | carry;
- carry = cur >> 31;
- }
-
- if (carry != 0)
- {
- // grow the array
- // RJ_ASSERT(pResult.m_length + 1 <= c_BigInt_MaxBlocks);
- *pCur = carry;
- ++pResult.m_length;
- }
- }
- }
-
- //******************************************************************************
- // result = result * 10
- //******************************************************************************
- private static unsafe void BigInt_Multiply10(ref tBigInt pResult)
- {
- // multiply all the blocks
- ulong carry = 0;
-
- fixed (uint* pCur1 = pResult.m_blocks)
- {
- uint* pCur = pCur1;
- uint* pEnd = pCur + pResult.m_length;
- for (; pCur != pEnd; ++pCur)
- {
- ulong product = (ulong) (*pCur) * 10 + carry;
- (*pCur) = (uint) (product & 0xFFFFFFFF);
- carry = product >> 32;
- }
-
- if (carry != 0)
- {
- // grow the array
- //RJ_ASSERT(pResult.m_length + 1 <= c_BigInt_MaxBlocks);
- *pCur = (uint) carry;
- ++pResult.m_length;
- }
- }
- }
-
- //******************************************************************************
- //******************************************************************************
- private static readonly uint[] g_PowerOf10_U32 = new uint[]
- {
- 1, // 10 ^ 0
- 10, // 10 ^ 1
- 100, // 10 ^ 2
- 1000, // 10 ^ 3
- 10000, // 10 ^ 4
- 100000, // 10 ^ 5
- 1000000, // 10 ^ 6
- 10000000, // 10 ^ 7
- };
-
- //******************************************************************************
- // Note: This has a lot of wasted space in the big integer structures of the
- // early table entries. It wouldn't be terribly hard to make the multiply
- // function work on integer pointers with an array length instead of
- // the tBigInt struct which would allow us to store a minimal amount of
- // data here.
- //******************************************************************************
- private static unsafe tBigInt g_PowerOf10_Big(int i)
- {
- tBigInt result;
- // 10 ^ 8
- if (i == 0)
- {
- // { 1, { 100000000 } },
- result.m_length = 1;
- result.m_blocks[0] = 100000000;
- }
- else if (i == 1)
- {
- // 10 ^ 16
- // { 2, { 0x6fc10000, 0x002386f2 } },
- result.m_length = 2;
- result.m_blocks[0] = 0x6fc10000;
- result.m_blocks[1] = 0x002386f2;
- }
- else if (i == 2)
- {
- // 10 ^ 32
- // { 4, { 0x00000000, 0x85acef81, 0x2d6d415b, 0x000004ee, } },
- result.m_length = 4;
- result.m_blocks[0] = 0x00000000;
- result.m_blocks[1] = 0x85acef81;
- result.m_blocks[2] = 0x2d6d415b;
- result.m_blocks[3] = 0x000004ee;
- }
- else if (i == 3)
- {
- // 10 ^ 64
- // { 7, { 0x00000000, 0x00000000, 0xbf6a1f01, 0x6e38ed64, 0xdaa797ed, 0xe93ff9f4, 0x00184f03, } },
- result.m_length = 7;
- result.m_blocks[0] = 0x00000000;
- result.m_blocks[1] = 0x00000000;
- result.m_blocks[2] = 0xbf6a1f01;
- result.m_blocks[3] = 0x6e38ed64;
- result.m_blocks[4] = 0xdaa797ed;
- result.m_blocks[5] = 0xe93ff9f4;
- result.m_blocks[6] = 0x00184f03;
- }
- else if (i == 4)
- {
- // 10 ^ 128
- //{
- // 14, {
- // 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x2e953e01, 0x03df9909, 0x0f1538fd,
- // 0x2374e42f, 0xd3cff5ec, 0xc404dc08, 0xbccdb0da, 0xa6337f19, 0xe91f2603, 0x0000024e, }
- //},
- result.m_length = 14;
- result.m_blocks[0] = 0x00000000;
- result.m_blocks[1] = 0x00000000;
- result.m_blocks[2] = 0x00000000;
- result.m_blocks[3] = 0x00000000;
- result.m_blocks[4] = 0x2e953e01;
- result.m_blocks[5] = 0x03df9909;
- result.m_blocks[6] = 0x0f1538fd;
- result.m_blocks[7] = 0x2374e42f;
- result.m_blocks[8] = 0xd3cff5ec;
- result.m_blocks[9] = 0xc404dc08;
- result.m_blocks[10] = 0xbccdb0da;
- result.m_blocks[11] = 0xa6337f19;
- result.m_blocks[12] = 0xe91f2603;
- result.m_blocks[13] = 0x0000024e;
-
- }
- else
- {
- // 10 ^ 256
- //{
- // 27, {
- // 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- // 0x00000000, 0x982e7c01, 0xbed3875b, 0xd8d99f72, 0x12152f87, 0x6bde50c6, 0xcf4a6e70,
- // 0xd595d80f, 0x26b2716e, 0xadc666b0, 0x1d153624, 0x3c42d35a, 0x63ff540e, 0xcc5573c0,
- // 0x65f9ef17, 0x55bc28f2, 0x80dcc7f7, 0xf46eeddc, 0x5fdcefce, 0x000553f7,
- // }
- //}
- result.m_length = 27;
- result.m_blocks[0] = 0x00000000;
- result.m_blocks[1] = 0x00000000;
- result.m_blocks[2] = 0x00000000;
- result.m_blocks[3] = 0x00000000;
- result.m_blocks[4] = 0x00000000;
- result.m_blocks[5] = 0x00000000;
- result.m_blocks[6] = 0x00000000;
- result.m_blocks[7] = 0x00000000;
- result.m_blocks[8] = 0x982e7c01;
- result.m_blocks[9] = 0xbed3875b;
- result.m_blocks[10] = 0xd8d99f72;
- result.m_blocks[11] = 0x12152f87;
- result.m_blocks[12] = 0x6bde50c6;
- result.m_blocks[13] = 0xcf4a6e70;
- result.m_blocks[14] = 0xd595d80f;
- result.m_blocks[15] = 0x26b2716e;
- result.m_blocks[16] = 0xadc666b0;
- result.m_blocks[17] = 0x1d153624;
- result.m_blocks[18] = 0x3c42d35a;
- result.m_blocks[19] = 0x63ff540e;
- result.m_blocks[20] = 0xcc5573c0;
- result.m_blocks[21] = 0x65f9ef17;
- result.m_blocks[22] = 0x55bc28f2;
- result.m_blocks[23] = 0x80dcc7f7;
- result.m_blocks[24] = 0xf46eeddc;
- result.m_blocks[25] = 0x5fdcefce;
- result.m_blocks[26] = 0x000553f7;
- }
-
- return result;
- }
-
- //******************************************************************************
- // result = 10^exponent
- //******************************************************************************
- private static void BigInt_Pow10(out tBigInt pResult, uint exponent)
- {
- // make sure the exponent is within the bounds of the lookup table data
- // RJ_ASSERT(exponent < 512);
-
- // create two temporary values to reduce large integer copy operations
- tBigInt temp1 = default;
- tBigInt temp2 = default;
- ref tBigInt pCurTemp = ref temp1;
- ref tBigInt pNextTemp = ref temp2;
-
- // initialize the result by looking up a 32-bit power of 10 corresponding to the first 3 bits
- uint smallExponent = exponent & 0x7;
- pCurTemp.SetU32(g_PowerOf10_U32[smallExponent]);
-
- // remove the low bits that we used for the 32-bit lookup table
- exponent >>= 3;
- int tableIdx = 0;
- // while there are remaining bits in the exponent to be processed
- while (exponent != 0)
- {
- // if the current bit is set, multiply it with the corresponding power of 10
- if ((exponent & 1) != 0)
- {
- // multiply into the next temporary
- BigInt_Multiply(out pNextTemp, pCurTemp, g_PowerOf10_Big(tableIdx));
-
- // swap to the next temporary
- ref tBigInt pSwap = ref pCurTemp;
- pCurTemp = pNextTemp;
- pNextTemp = pSwap;
- }
-
- // advance to the next bit
- ++tableIdx;
- exponent >>= 1;
- }
-
- // output the result
- pResult = pCurTemp;
- }
-
-
- //******************************************************************************
- // result = in * 10^exponent
- //******************************************************************************
- private static unsafe void BigInt_MultiplyPow10(out tBigInt pResult, in tBigInt input, uint exponent)
- {
- // make sure the exponent is within the bounds of the lookup table data
- // RJ_ASSERT(exponent < 512);
-
- // create two temporary values to reduce large integer copy operations
- tBigInt temp1 = default;
- tBigInt temp2 = default;
- ref tBigInt pCurTemp = ref temp1;
- ref tBigInt pNextTemp = ref temp2;
-
- // initialize the result by looking up a 32-bit power of 10 corresponding to the first 3 bits
- uint smallExponent = exponent & 0x7;
- if (smallExponent != 0)
- {
- BigInt_Multiply(out pCurTemp, input, g_PowerOf10_U32[smallExponent]);
- }
- else
- {
- pCurTemp = input;
- }
-
- // remove the low bits that we used for the 32-bit lookup table
- exponent >>= 3;
- int tableIdx = 0;
-
- // while there are remaining bits in the exponent to be processed
- while (exponent != 0)
- {
- // if the current bit is set, multiply it with the corresponding power of 10
- if((exponent & 1) != 0)
- {
- // multiply into the next temporary
- BigInt_Multiply( out pNextTemp, pCurTemp, g_PowerOf10_Big(tableIdx) );
-
- // swap to the next temporary
- ref tBigInt pSwap = ref pCurTemp;
- pCurTemp = pNextTemp;
- pNextTemp = pSwap;
- }
-
- // advance to the next bit
- ++tableIdx;
- exponent >>= 1;
- }
-
- // output the result
- pResult = pCurTemp;
- }
-
- //******************************************************************************
- // result = 2^exponent
- //******************************************************************************
- private static unsafe void BigInt_Pow2(out tBigInt pResult, uint exponent)
- {
- int blockIdx = (int)exponent / 32;
- //RJ_ASSERT(blockIdx < c_BigInt_MaxBlocks);
-
- for (uint i = 0; i <= blockIdx; ++i)
- pResult.m_blocks[i] = 0;
-
- pResult.m_length = blockIdx + 1;
-
- int bitIdx = ((int)exponent % 32);
- pResult.m_blocks[blockIdx] |= (uint)(1 << bitIdx);
- }
-
- //******************************************************************************
- // This function will divide two large numbers under the assumption that the
- // result is within the range [0,10) and the input numbers have been shifted
- // to satisfy:
- // - The highest block of the divisor is greater than or equal to 8 such that
- // there is enough precision to make an accurate first guess at the quotient.
- // - The highest block of the divisor is less than the maximum value on an
- // unsigned 32-bit integer such that we can safely increment without overflow.
- // - The dividend does not contain more blocks than the divisor such that we
- // can estimate the quotient by dividing the equivalently placed high blocks.
- //
- // quotient = floor(dividend / divisor)
- // remainder = dividend - quotient*divisor
- //
- // pDividend is updated to be the remainder and the quotient is returned.
- //******************************************************************************
- private static unsafe uint BigInt_DivideWithRemainder_MaxQuotient9(ref tBigInt pDividend, in tBigInt divisor)
- {
- // Check that the divisor has been correctly shifted into range and that it is not
- // smaller than the dividend in length.
- //RJ_ASSERT( !divisor.IsZero() &&
- // divisor.m_blocks[divisor.m_length-1] >= 8 &&
- // divisor.m_blocks[divisor.m_length-1] < 0xFFFFFFFF &&
- // pDividend->m_length <= divisor.m_length );
-
- // If the dividend is smaller than the divisor, the quotient is zero and the divisor is already
- // the remainder.
- int length = divisor.m_length;
- if (pDividend.m_length < divisor.m_length)
- return 0;
-
- fixed (uint* pDivisorCur1 = divisor.m_blocks)
- fixed (uint* pDividendCur1 = pDividend.m_blocks)
- {
- uint* pDivisorCur = pDivisorCur1;
- uint* pDividendCur = pDividendCur1;
-
- uint* pFinalDivisorBlock = pDivisorCur + length - 1;
- uint* pFinalDividendBlock = pDividendCur + length - 1;
-
- // Compute an estimated quotient based on the high block value. This will either match the actual quotient or
- // undershoot by one.
- uint quotient = *pFinalDividendBlock / (*pFinalDivisorBlock + 1);
- //RJ_ASSERT(quotient <= 9);
-
- // Divide out the estimated quotient
- if (quotient != 0)
- {
- // dividend = dividend - divisor*quotient
- ulong borrow = 0;
- ulong carry = 0;
- do
- {
- ulong product = (ulong) *pDivisorCur * (ulong) quotient + carry;
- carry = product >> 32;
-
- ulong difference = (ulong) *pDividendCur - (product & 0xFFFFFFFF) - borrow;
- borrow = (difference >> 32) & 1;
-
- *pDividendCur = (uint) (difference & 0xFFFFFFFF);
-
- ++pDivisorCur;
- ++pDividendCur;
- } while (pDivisorCur <= pFinalDivisorBlock);
-
- // remove all leading zero blocks from dividend
- while (length > 0 && pDividend.m_blocks[length - 1] == 0)
- --length;
-
- pDividend.m_length = length;
- }
-
- // If the dividend is still larger than the divisor, we overshot our estimate quotient. To correct,
- // we increment the quotient and subtract one more divisor from the dividend.
- if (BigInt_Compare(pDividend, divisor) >= 0)
- {
- ++quotient;
-
- // dividend = dividend - divisor
- pDivisorCur = pDivisorCur1;
- pDividendCur = pDividendCur1;
-
- ulong borrow = 0;
- do
- {
- ulong difference = (ulong) *pDividendCur - (ulong) *pDivisorCur - borrow;
- borrow = (difference >> 32) & 1;
-
- *pDividendCur = (uint)(difference & 0xFFFFFFFF);
-
- ++pDivisorCur;
- ++pDividendCur;
- } while (pDivisorCur <= pFinalDivisorBlock);
-
- // remove all leading zero blocks from dividend
- while (length > 0 && pDividend.m_blocks[length - 1] == 0)
- --length;
-
- pDividend.m_length = length;
- }
-
- return quotient;
- }
- }
-
-
- //******************************************************************************
- // result = result << shift
- //******************************************************************************
- private static unsafe void BigInt_ShiftLeft(ref tBigInt pResult, uint shift)
- {
- // RJ_ASSERT( shift != 0 );
-
- int shiftBlocks = (int)shift / 32;
- int shiftBits = (int)shift % 32;
-
- int inLength = pResult.m_length;
- // RJ_ASSERT( inLength + shiftBlocks <= c_BigInt_MaxBlocks );
-
- // check if the shift is block aligned
- if (shiftBits == 0)
- {
- // process blocks high to low so that we can safely process in place
- fixed (uint* pInBlocks1 = pResult.m_blocks)
- {
- uint* pInBlocks = pInBlocks1;
- uint* pInCur = pInBlocks + inLength - 1;
- uint* pOutCur = pInCur + shiftBlocks;
-
- // copy blocks from high to low
- for (; pInCur >= pInBlocks; --pInCur, --pOutCur)
- {
- *pOutCur = *pInCur;
- }
- }
-
- // zero the remaining low blocks
- for ( uint i = 0; i < shiftBlocks; ++i)
- pResult.m_blocks[i] = 0;
-
- pResult.m_length += shiftBlocks;
- }
- // else we need to shift partial blocks
- else
- {
- int inBlockIdx = inLength - 1;
- int outBlockIdx = inLength + shiftBlocks;
-
- // set the length to hold the shifted blocks
- //RJ_ASSERT( outBlockIdx < c_BigInt_MaxBlocks );
- pResult.m_length = outBlockIdx + 1;
-
- // output the initial blocks
- int lowBitsShift = (32 - shiftBits);
- uint highBits = 0;
- uint block = pResult.m_blocks[inBlockIdx];
- uint lowBits = block >> lowBitsShift;
- while ( inBlockIdx > 0 )
- {
- pResult.m_blocks[outBlockIdx] = highBits | lowBits;
- highBits = block << shiftBits;
-
- --inBlockIdx;
- --outBlockIdx;
-
- block = pResult.m_blocks[inBlockIdx];
- lowBits = block >> lowBitsShift;
- }
-
- // output the final blocks
- // RJ_ASSERT( outBlockIdx == shiftBlocks + 1 );
- pResult.m_blocks[outBlockIdx] = highBits | lowBits;
- pResult.m_blocks[outBlockIdx-1] = block << shiftBits;
-
- // zero the remaining low blocks
- for ( uint i = 0; i < shiftBlocks; ++i)
- pResult.m_blocks[i] = 0;
-
- // check if the terminating block has no set bits
- if (pResult.m_blocks[pResult.m_length - 1] == 0)
- --pResult.m_length;
- }
- }
-
- //******************************************************************************
- // Different modes for terminating digit output
- //******************************************************************************
- public enum CutoffMode
- {
- Unique, // as many digits as necessary to print a uniquely identifiable number
- TotalLength, // up to cutoffNumber significant digits
- FractionLength, // up to cutoffNumber significant digits past the decimal point
- };
-
- //******************************************************************************
- // This is an implementation the Dragon4 algorithm to convert a binary number
- // in floating point format to a decimal number in string format. The function
- // returns the number of digits written to the output buffer and the output is
- // not NUL terminated.
- //
- // The floating point input value is (mantissa * 2^exponent).
- //
- // See the following papers for more information on the algorithm:
- // "How to Print Floating-Point Numbers Accurately"
- // Steele and White
- // http://kurtstephens.com/files/p372-steele.pdf
- // "Printing Floating-Point Numbers Quickly and Accurately"
- // Burger and Dybvig
- // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf
- //******************************************************************************
- private static unsafe uint Dragon4
- (
- ulong mantissa, // value significand
- int exponent, // value exponent in base 2
- uint mantissaHighBitIdx, // index of the highest set mantissa bit
- bool hasUnequalMargins, // is the high margin twice as large as the low margin
- CutoffMode cutoffMode, // how to determine output length
- uint cutoffNumber, // parameter to the selected cutoffMode
- byte* pOutBuffer, // buffer to output into
- uint bufferSize, // maximum characters that can be printed to pOutBuffer
- out int pOutExponent // the base 10 exponent of the first digit
- )
- {
- byte* pCurDigit = pOutBuffer;
-
- // RJ_ASSERT( bufferSize > 0 );
-
- // if the mantissa is zero, the value is zero regardless of the exponent
- if (mantissa == 0)
- {
- *pCurDigit = (byte)'0';
- pOutExponent = 0;
- return 1;
- }
-
- // compute the initial state in integral form such that
- // value = scaledValue / scale
- // marginLow = scaledMarginLow / scale
- tBigInt scale = default; // positive scale applied to value and margin such that they can be
- // represented as whole numbers
- tBigInt scaledValue = default; // scale * mantissa
- tBigInt scaledMarginLow = default; // scale * 0.5 * (distance between this floating-point number and its
- // immediate lower value)
-
- // For normalized IEEE floating point values, each time the exponent is incremented the margin also
- // doubles. That creates a subset of transition numbers where the high margin is twice the size of
- // the low margin.
- tBigInt * pScaledMarginHigh;
- tBigInt optionalMarginHigh = default;
-
- if ( hasUnequalMargins )
- {
- // if we have no fractional component
- if (exponent > 0)
- {
- // 1) Expand the input value by multiplying out the mantissa and exponent. This represents
- // the input value in its whole number representation.
- // 2) Apply an additional scale of 2 such that later comparisons against the margin values
- // are simplified.
- // 3) Set the margin value to the lowest mantissa bit's scale.
-
- // scaledValue = 2 * 2 * mantissa*2^exponent
- scaledValue.SetU64( 4 * mantissa );
- BigInt_ShiftLeft(ref scaledValue, (uint)exponent);
-
- // scale = 2 * 2 * 1
- scale.SetU32( 4 );
-
- // scaledMarginLow = 2 * 2^(exponent-1)
- BigInt_Pow2( out scaledMarginLow, (uint)exponent );
-
- // scaledMarginHigh = 2 * 2 * 2^(exponent-1)
- BigInt_Pow2( out optionalMarginHigh, (uint)(exponent + 1));
- }
- // else we have a fractional exponent
- else
- {
- // In order to track the mantissa data as an integer, we store it as is with a large scale
-
- // scaledValue = 2 * 2 * mantissa
- scaledValue.SetU64( 4 * mantissa );
-
- // scale = 2 * 2 * 2^(-exponent)
- BigInt_Pow2(out scale, (uint)(-exponent + 2));
-
- // scaledMarginLow = 2 * 2^(-1)
- scaledMarginLow.SetU32( 1 );
-
- // scaledMarginHigh = 2 * 2 * 2^(-1)
- optionalMarginHigh.SetU32( 2 );
- }
-
- // the high and low margins are different
- pScaledMarginHigh = &optionalMarginHigh;
- }
- else
- {
- // if we have no fractional component
- if (exponent > 0)
- {
- // 1) Expand the input value by multiplying out the mantissa and exponent. This represents
- // the input value in its whole number representation.
- // 2) Apply an additional scale of 2 such that later comparisons against the margin values
- // are simplified.
- // 3) Set the margin value to the lowest mantissa bit's scale.
-
- // scaledValue = 2 * mantissa*2^exponent
- scaledValue.SetU64( 2 * mantissa );
- BigInt_ShiftLeft(ref scaledValue, (uint)exponent);
-
- // scale = 2 * 1
- scale.SetU32( 2 );
-
- // scaledMarginLow = 2 * 2^(exponent-1)
- BigInt_Pow2(out scaledMarginLow, (uint)exponent );
- }
- // else we have a fractional exponent
- else
- {
- // In order to track the mantissa data as an integer, we store it as is with a large scale
-
- // scaledValue = 2 * mantissa
- scaledValue.SetU64( 2 * mantissa );
-
- // scale = 2 * 2^(-exponent)
- BigInt_Pow2(out scale, (uint)(-exponent + 1));
-
- // scaledMarginLow = 2 * 2^(-1)
- scaledMarginLow.SetU32( 1 );
- }
-
- // the high and low margins are equal
- pScaledMarginHigh = &scaledMarginLow;
- }
-
- // Compute an estimate for digitExponent that will be correct or undershoot by one.
- // This optimization is based on the paper "Printing Floating-Point Numbers Quickly and Accurately"
- // by Burger and Dybvig http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.4656&rep=rep1&type=pdf
- // We perform an additional subtraction of 0.69 to increase the frequency of a failed estimate
- // because that lets us take a faster branch in the code. 0.69 is chosen because 0.69 + log10(2) is
- // less than one by a reasonable epsilon that will account for any floating point error.
- //
- // We want to set digitExponent to floor(log10(v)) + 1
- // v = mantissa*2^exponent
- // log2(v) = log2(mantissa) + exponent;
- // log10(v) = log2(v) * log10(2)
- // floor(log2(v)) = mantissaHighBitIdx + exponent;
- // log10(v) - log10(2) < (mantissaHighBitIdx + exponent) * log10(2) <= log10(v)
- // log10(v) < (mantissaHighBitIdx + exponent) * log10(2) + log10(2) <= log10(v) + log10(2)
- // floor( log10(v) ) < ceil( (mantissaHighBitIdx + exponent) * log10(2) ) <= floor( log10(v) ) + 1
- const double log10_2 = 0.30102999566398119521373889472449;
- var digitExponentDoubleValue = (double) ((int) mantissaHighBitIdx + exponent) * log10_2 - 0.69;
- digitExponentDoubleValue = Math.Ceiling(digitExponentDoubleValue);
- int digitExponent = (int)digitExponentDoubleValue;
-
- // if the digit exponent is smaller than the smallest desired digit for fractional cutoff,
- // pull the digit back into legal range at which point we will round to the appropriate value.
- // Note that while our value for digitExponent is still an estimate, this is safe because it
- // only increases the number. This will either correct digitExponent to an accurate value or it
- // will clamp it above the accurate value.
- if (cutoffMode == CutoffMode.FractionLength && digitExponent <= -(int)cutoffNumber)
- {
- digitExponent = -(int)cutoffNumber + 1;
- }
-
- // Divide value by 10^digitExponent.
- if (digitExponent > 0)
- {
- // The exponent is positive creating a division so we multiply up the scale.
- tBigInt temp;
- BigInt_MultiplyPow10( out temp, scale, (uint)digitExponent );
- scale = temp;
- }
- else if (digitExponent < 0)
- {
- // The exponent is negative creating a multiplication so we multiply up the scaledValue,
- // scaledMarginLow and scaledMarginHigh.
- tBigInt pow10;
- BigInt_Pow10(out pow10, (uint)(-digitExponent));
-
- tBigInt temp;
- BigInt_Multiply( out temp, scaledValue, pow10);
- scaledValue = temp;
-
- BigInt_Multiply( out temp, scaledMarginLow, pow10);
- scaledMarginLow = temp;
-
- if (pScaledMarginHigh != &scaledMarginLow)
- BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow );
- }
-
- // If (value >= 1), our estimate for digitExponent was too low
- if( BigInt_Compare(scaledValue,scale) >= 0 )
- {
- // The exponent estimate was incorrect.
- // Increment the exponent and don't perform the premultiply needed
- // for the first loop iteration.
- digitExponent = digitExponent + 1;
- }
- else
- {
- // The exponent estimate was correct.
- // Multiply larger by the output base to prepare for the first loop iteration.
- BigInt_Multiply10( ref scaledValue );
- BigInt_Multiply10( ref scaledMarginLow );
- if (pScaledMarginHigh != &scaledMarginLow)
- BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow );
- }
-
- // Compute the cutoff exponent (the exponent of the final digit to print).
- // Default to the maximum size of the output buffer.
- int cutoffExponent = digitExponent - (int)bufferSize;
- switch (cutoffMode)
- {
- // print digits until we pass the accuracy margin limits or buffer size
- case CutoffMode.Unique:
- break;
-
- // print cutoffNumber of digits or until we reach the buffer size
- case CutoffMode.TotalLength:
- {
- int desiredCutoffExponent = digitExponent - (int) cutoffNumber;
- if (desiredCutoffExponent > cutoffExponent)
- cutoffExponent = desiredCutoffExponent;
- }
- break;
-
- // print cutoffNumber digits past the decimal point or until we reach the buffer size
- case CutoffMode.FractionLength:
- {
- int desiredCutoffExponent = -(int) cutoffNumber;
- if (desiredCutoffExponent > cutoffExponent)
- cutoffExponent = desiredCutoffExponent;
- }
- break;
- }
-
- // Output the exponent of the first digit we will print
- pOutExponent = digitExponent-1;
-
- // In preparation for calling BigInt_DivideWithRemainder_MaxQuotient9(),
- // we need to scale up our values such that the highest block of the denominator
- // is greater than or equal to 8. We also need to guarantee that the numerator
- // can never have a length greater than the denominator after each loop iteration.
- // This requires the highest block of the denominator to be less than or equal to
- // 429496729 which is the highest number that can be multiplied by 10 without
- // overflowing to a new block.
- // RJ_ASSERT( scale.GetLength() > 0 );
- uint hiBlock = scale.GetBlock( scale.GetLength() - 1 );
- if (hiBlock < 8 || hiBlock > 429496729)
- {
- // Perform a bit shift on all values to get the highest block of the denominator into
- // the range [8,429496729]. We are more likely to make accurate quotient estimations
- // in BigInt_DivideWithRemainder_MaxQuotient9() with higher denominator values so
- // we shift the denominator to place the highest bit at index 27 of the highest block.
- // This is safe because (2^28 - 1) = 268435455 which is less than 429496729. This means
- // that all values with a highest bit at index 27 are within range.
- uint hiBlockLog2 = LogBase2(hiBlock);
- // RJ_ASSERT(hiBlockLog2 < 3 || hiBlockLog2 > 27);
- uint shift = (32 + 27 - hiBlockLog2) % 32;
-
- BigInt_ShiftLeft( ref scale, shift );
- BigInt_ShiftLeft( ref scaledValue, shift);
- BigInt_ShiftLeft( ref scaledMarginLow, shift);
- if (pScaledMarginHigh != &scaledMarginLow)
- BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow );
- }
-
- // These values are used to inspect why the print loop terminated so we can properly
- // round the final digit.
- bool low; // did the value get within marginLow distance from zero
- bool high; // did the value get within marginHigh distance from one
- uint outputDigit; // current digit being output
-
- if (cutoffMode == CutoffMode.Unique)
- {
- // For the unique cutoff mode, we will try to print until we have reached a level of
- // precision that uniquely distinguishes this value from its neighbors. If we run
- // out of space in the output buffer, we terminate early.
- for (;;)
- {
- digitExponent = digitExponent-1;
-
- // divide out the scale to extract the digit
- outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(ref scaledValue, scale);
- //RJ_ASSERT( outputDigit < 10 );
-
- // update the high end of the value
- tBigInt scaledValueHigh;
- BigInt_Add( out scaledValueHigh, scaledValue, *pScaledMarginHigh );
-
- // stop looping if we are far enough away from our neighboring values
- // or if we have reached the cutoff digit
- low = BigInt_Compare(scaledValue, scaledMarginLow) < 0;
- high = BigInt_Compare(scaledValueHigh, scale) > 0;
- if (low | high | (digitExponent == cutoffExponent))
- break;
-
- // store the output digit
- *pCurDigit = (byte)('0' + outputDigit);
- ++pCurDigit;
-
- // multiply larger by the output base
- BigInt_Multiply10( ref scaledValue );
- BigInt_Multiply10( ref scaledMarginLow );
- if (pScaledMarginHigh != &scaledMarginLow)
- BigInt_Multiply2( out *pScaledMarginHigh, scaledMarginLow );
- }
- }
- else
- {
- // For length based cutoff modes, we will try to print until we
- // have exhausted all precision (i.e. all remaining digits are zeros) or
- // until we reach the desired cutoff digit.
- low = false;
- high = false;
-
- for (;;)
- {
- digitExponent = digitExponent-1;
-
- // divide out the scale to extract the digit
- outputDigit = BigInt_DivideWithRemainder_MaxQuotient9(ref scaledValue, scale);
- //RJ_ASSERT( outputDigit < 10 );
-
- if ( scaledValue.IsZero() | (digitExponent == cutoffExponent) )
- break;
-
- // store the output digit
- *pCurDigit = (byte)('0' + outputDigit);
- ++pCurDigit;
-
- // multiply larger by the output base
- BigInt_Multiply10(ref scaledValue);
- }
- }
-
- // round off the final digit
- // default to rounding down if value got too close to 0
- bool roundDown = low;
-
- // if it is legal to round up and down
- if (low == high)
- {
- // round to the closest digit by comparing value with 0.5. To do this we need to convert
- // the inequality to large integer values.
- // compare( value, 0.5 )
- // compare( scale * value, scale * 0.5 )
- // compare( 2 * scale * value, scale )
- BigInt_Multiply2(ref scaledValue);
- int compare = BigInt_Compare(scaledValue, scale);
- roundDown = compare < 0;
-
- // if we are directly in the middle, round towards the even digit (i.e. IEEE rouding rules)
- if (compare == 0)
- roundDown = (outputDigit & 1) == 0;
- }
-
- // print the rounded digit
- if (roundDown)
- {
- *pCurDigit = (byte)('0' + outputDigit);
- ++pCurDigit;
- }
- else
- {
- // handle rounding up
- if (outputDigit == 9)
- {
- // find the first non-nine prior digit
- for (;;)
- {
- // if we are at the first digit
- if (pCurDigit == pOutBuffer)
- {
- // output 1 at the next highest exponent
- *pCurDigit = (byte)'1';
- ++pCurDigit;
- pOutExponent += 1;
- break;
- }
-
- --pCurDigit;
- if (*pCurDigit != (byte)'9')
- {
- // increment the digit
- *pCurDigit += 1;
- ++pCurDigit;
- break;
- }
- }
- }
- else
- {
- // values in the range [0,8] can perform a simple round up
- *pCurDigit = (byte)((byte)'0' + outputDigit + 1);
- ++pCurDigit;
- }
- }
-
- // return the number of digits output
- uint outputLen = (uint)(pCurDigit - pOutBuffer);
- // RJ_ASSERT(outputLen <= bufferSize);
- return outputLen;
- }
-
- //******************************************************************************
- //******************************************************************************
- public enum PrintFloatFormat
- {
- Positional, // [-]ddddd.dddd
- Scientific, // [-]d.dddde[sign]ddd
- }
-
- //******************************************************************************\
- // Helper union to decompose a 32-bit IEEE float.
- // sign: 1 bit
- // exponent: 8 bits
- // mantissa: 23 bits
- //******************************************************************************
- [StructLayout(LayoutKind.Explicit)]
- public struct tFloatUnion32
- {
- public bool IsNegative() { return (m_integer >> 31) != 0; }
- public uint GetExponent() { return (m_integer >> 23) & 0xFF; }
- public uint GetMantissa() { return m_integer & 0x7FFFFF; }
-
- [FieldOffset(0)]
- public float m_floatingPoint;
-
- [FieldOffset(0)]
- public uint m_integer;
- };
-
- //******************************************************************************
- // Helper union to decompose a 64-bit IEEE float.
- // sign: 1 bit
- // exponent: 11 bits
- // mantissa: 52 bits
- //******************************************************************************
- [StructLayout(LayoutKind.Explicit)]
- public struct tFloatUnion64
- {
- public bool IsNegative() { return (m_integer >> 63) != 0; }
- public uint GetExponent() { return (uint)((m_integer >> 52) & 0x7FF); }
- public ulong GetMantissa() { return m_integer & 0xFFFFFFFFFFFFFUL; }
-
- [FieldOffset(0)]
- public double m_floatingPoint;
-
- [FieldOffset(0)]
- public ulong m_integer;
- };
-
-
- //******************************************************************************
- // Outputs the positive number with positional notation: ddddd.dddd
- // The output is always NUL terminated and the output length (not including the
- // NUL) is returned.
- //******************************************************************************
- private static unsafe int FormatPositional
- (
- byte* pOutBuffer, // buffer to output into
- uint bufferSize, // maximum characters that can be printed to pOutBuffer
- ulong mantissa, // value significand
- int exponent, // value exponent in base 2
- uint mantissaHighBitIdx, // index of the highest set mantissa bit
- bool hasUnequalMargins, // is the high margin twice as large as the low margin
- int precision // Negative prints as many digits as are needed for a unique
- // number. Positive specifies the maximum number of
- // significant digits to print past the decimal point.
- )
- {
- //RJ_ASSERT(bufferSize > 0);
-
- int printExponent;
- uint numPrintDigits;
-
- uint maxPrintLen = bufferSize - 1;
-
- if (precision < 0)
- {
- numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.Unique,
- 0,
- pOutBuffer,
- maxPrintLen,
- out printExponent);
- }
- else
- {
- numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.FractionLength,
- (uint)precision,
- pOutBuffer,
- maxPrintLen,
- out printExponent);
- }
-
- //RJ_ASSERT(numPrintDigits > 0);
- //RJ_ASSERT(numPrintDigits <= bufferSize);
-
- // track the number of digits past the decimal point that have been printed
- uint numFractionDigits = 0;
-
- // if output has a whole number
- if (printExponent >= 0)
- {
- // leave the whole number at the start of the buffer
- uint numWholeDigits = (uint)(printExponent + 1);
- if (numPrintDigits < numWholeDigits)
- {
- // don't overflow the buffer
- if (numWholeDigits > maxPrintLen)
- numWholeDigits = maxPrintLen;
-
- // add trailing zeros up to the decimal point
- for (; numPrintDigits < numWholeDigits; ++numPrintDigits)
- pOutBuffer[numPrintDigits] = (byte)'0';
- }
- // insert the decimal point prior to the fraction
- else if (numPrintDigits > (uint)numWholeDigits)
- {
- numFractionDigits = numPrintDigits - numWholeDigits;
- uint maxFractionDigits = maxPrintLen - numWholeDigits - 1;
- if (numFractionDigits > maxFractionDigits)
- numFractionDigits = maxFractionDigits;
- Unsafe.CopyBlock(pOutBuffer + numWholeDigits + 1, pOutBuffer + numWholeDigits, numFractionDigits);
- pOutBuffer[numWholeDigits] = (byte)'.';
- numPrintDigits = numWholeDigits + 1 + numFractionDigits;
- }
- }
- else
- {
- // shift out the fraction to make room for the leading zeros
- if (maxPrintLen > 2)
- {
- uint numFractionZeros = (uint)( - printExponent - 1);
- uint maxFractionZeros = maxPrintLen - 2;
- if (numFractionZeros > maxFractionZeros)
- numFractionZeros = maxFractionZeros;
-
- uint digitsStartIdx = 2 + numFractionZeros;
-
- // shift the significant digits right such that there is room for leading zeros
- numFractionDigits = numPrintDigits;
- uint maxFractionDigits = maxPrintLen - digitsStartIdx;
- if (numFractionDigits > maxFractionDigits)
- numFractionDigits = maxFractionDigits;
- Unsafe.CopyBlock(pOutBuffer + digitsStartIdx, pOutBuffer, numFractionDigits);
-
- // insert the leading zeros
- for (uint i = 2; i < digitsStartIdx; ++i)
- pOutBuffer[i] = (byte)'0';
-
- // update the counts
- numFractionDigits += numFractionZeros;
- numPrintDigits = numFractionDigits;
- }
-
- // add the decimal point
- if (maxPrintLen > 1)
- {
- pOutBuffer[1] = (byte)'.';
- numPrintDigits += 1;
- }
-
- // add the initial zero
- if (maxPrintLen > 0)
- {
- pOutBuffer[0] = (byte)'0';
- numPrintDigits += 1;
- }
- }
-
- // add trailing zeros up to precision length
- if (precision > (int)numFractionDigits && numPrintDigits < maxPrintLen)
- {
- // add a decimal point if this is the first fractional digit we are printing
- if (numFractionDigits == 0)
- {
- pOutBuffer[numPrintDigits++] = (byte)'.';
- }
-
- // compute the number of trailing zeros needed
- uint totalDigits = (uint)(numPrintDigits + (precision - (int)numFractionDigits));
- if (totalDigits > maxPrintLen)
- totalDigits = maxPrintLen;
-
- for (; numPrintDigits < totalDigits; ++numPrintDigits)
- pOutBuffer[numPrintDigits] = (byte)'0';
- }
-
- // terminate the buffer
- //RJ_ASSERT(numPrintDigits <= maxPrintLen);
- //pOutBuffer[numPrintDigits] = '\0';
-
- return (int)numPrintDigits;
- }
-
-
-
- //******************************************************************************
- // Outputs the positive number with scientific notation: d.dddde[sign]ddd
- // The output is always NUL terminated and the output length (not including the
- // NUL) is returned.
- //******************************************************************************
- private static unsafe int FormatScientific
- (
- byte* pOutBuffer, // buffer to output into
- uint bufferSize, // maximum characters that can be printed to pOutBuffer
- ulong mantissa, // value significand
- int exponent, // value exponent in base 2
- uint mantissaHighBitIdx, // index of the highest set mantissa bit
- bool hasUnequalMargins, // is the high margin twice as large as the low margin
- int precision // Negative prints as many digits as are needed for a unique
- // number. Positive specifies the maximum number of
- // significant digits to print past the decimal point.
- )
- {
- //RJ_ASSERT(bufferSize > 0);
-
- int printExponent;
- uint numPrintDigits;
-
- if (precision < 0)
- {
- numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.Unique,
- 0,
- pOutBuffer,
- bufferSize,
- out printExponent);
- }
- else
- {
- numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.TotalLength,
- (uint)(precision + 1),
- pOutBuffer,
- bufferSize,
- out printExponent);
- }
-
- //RJ_ASSERT(numPrintDigits > 0);
- //RJ_ASSERT(numPrintDigits <= bufferSize);
-
- byte* pCurOut = pOutBuffer;
-
- // keep the whole number as the first digit
- if (bufferSize > 1)
- {
- pCurOut += 1;
- bufferSize -= 1;
- }
-
- // insert the decimal point prior to the fractional number
- uint numFractionDigits = numPrintDigits - 1;
- if (numFractionDigits > 0 && bufferSize > 1)
- {
- uint maxFractionDigits = bufferSize - 2;
- if (numFractionDigits > maxFractionDigits)
- numFractionDigits = maxFractionDigits;
- Unsafe.CopyBlock(pCurOut + 1, pCurOut, numFractionDigits);
- pCurOut[0] = (byte)'.';
- pCurOut += (1 + numFractionDigits);
- bufferSize -= (1 + numFractionDigits);
- }
-
- // add trailing zeros up to precision length
- if (precision > (int)numFractionDigits && bufferSize > 1)
- {
- // add a decimal point if this is the first fractional digit we are printing
- if (numFractionDigits == 0)
- {
- *pCurOut = (byte)'.';
- ++pCurOut;
- --bufferSize;
- }
-
- // compute the number of trailing zeros needed
- uint numZeros = (uint)(precision - numFractionDigits);
- if (numZeros > bufferSize - 1)
- numZeros = bufferSize - 1;
-
- for (byte* pEnd = pCurOut + numZeros; pCurOut < pEnd; ++pCurOut)
- *pCurOut = (byte)'0';
- }
-
- // print the exponent into a local buffer and copy into output buffer
- if (bufferSize > 1)
- {
- var exponentBuffer = stackalloc byte[5];
- exponentBuffer[0] = (byte)'e';
- if (printExponent >= 0)
- {
- exponentBuffer[1] = (byte)'+';
- }
- else
- {
- exponentBuffer[1] = (byte)'-';
- printExponent = -printExponent;
- }
-
- //RJ_ASSERT(printExponent < 1000);
- uint hundredsPlace = (uint)(printExponent / 100);
- uint tensPlace = (uint)((printExponent - hundredsPlace * 100) / 10);
- uint onesPlace = (uint)((printExponent - hundredsPlace * 100 - tensPlace * 10));
-
- exponentBuffer[2] = (byte)('0' + hundredsPlace);
- exponentBuffer[3] = (byte)('0' + tensPlace);
- exponentBuffer[4] = (byte)('0' + onesPlace);
-
- // copy the exponent buffer into the output
- uint maxExponentSize = bufferSize - 1;
- uint exponentSize = (5 < maxExponentSize) ? 5 : maxExponentSize;
- Unsafe.CopyBlock(pCurOut, exponentBuffer, exponentSize);
- pCurOut += exponentSize;
- bufferSize -= exponentSize;
- }
-
- //RJ_ASSERT(bufferSize > 0);
- //pCurOut[0] = '\0';
-
- return (int)(pCurOut - pOutBuffer);
- }
-
- //******************************************************************************
- // Print special case values for infinities and NaNs.
- // The output string is always NUL terminated and the string length (not
- // including the NUL) is returned.
- //******************************************************************************
- private static readonly byte[] InfinityString = new byte[]
- {
- (byte) 'I',
- (byte) 'n',
- (byte) 'f',
- (byte) 'i',
- (byte) 'n',
- (byte) 'i',
- (byte) 't',
- (byte) 'y',
- };
-
- private static readonly byte[] NanString = new byte[]
- {
- (byte) 'N',
- (byte) 'a',
- (byte) 'N',
- };
-
- private static unsafe void FormatInfinityNaN(byte* dest, ref int destIndex, int destLength, ulong mantissa, bool isNegative, FormatOptions formatOptions)
- {
- //RJ_ASSERT(bufferSize > 0);
- int length = mantissa == 0 ? 8 + (isNegative ? 1 : 0) : 3;
- int align = formatOptions.AlignAndSize;
-
- // left align
- if (AlignLeft(dest, ref destIndex, destLength, align, length)) return;
-
- // Check for infinity
- if (mantissa == 0)
- {
- if (isNegative)
- {
- if (destIndex >= destLength) return;
- dest[destIndex++] = (byte)'-';
- }
-
- for (int i = 0; i < 8; i++)
- {
- if (destIndex >= destLength) return;
- dest[destIndex++] = InfinityString[i];
- }
- }
- else
- {
- for (int i = 0; i < 3; i++)
- {
- if (destIndex >= destLength) return;
- dest[destIndex++] = NanString[i];
- }
- }
-
- // right align
- AlignRight(dest, ref destIndex, destLength, align, length);
- }
-
- // ------------------------------------------------------------------------------
- // Part of the following code is taking some constants and code from
- // https://github.com/dotnet/runtime/blob/75036ffec9473dd1d948c052c041fdedd7784ac9/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the https://github.com/dotnet/runtime/blob/master/LICENSE.TXT file for more information.
- // ------------------------------------------------------------------------------
-
- // SinglePrecision and DoublePrecision represent the maximum number of digits required
- // to guarantee that any given Single or Double can roundtrip. Some numbers may require
- // less, but none will require more.
- private const int SinglePrecision = 9;
- private const int DoublePrecision = 17;
-
- internal const int SingleNumberBufferLength = SinglePrecision + 1; // + zero
- internal const int DoubleNumberBufferLength = DoublePrecision + 1; // + zero
-
- // SinglePrecisionCustomFormat and DoublePrecisionCustomFormat are used to ensure that
- // custom format strings return the same string as in previous releases when the format
- // would return x digits or less (where x is the value of the corresponding constant).
- // In order to support more digits, we would need to update ParseFormatSpecifier to pre-parse
- // the format and determine exactly how many digits are being requested and whether they
- // represent "significant digits" or "digits after the decimal point".
- private const int SinglePrecisionCustomFormat = 7;
- private const int DoublePrecisionCustomFormat = 15;
-
- /// <summary>
- /// Format a float 32-bit to a general format to the specified destination buffer.
- /// </summary>
- /// <param name="dest">Destination buffer.</param>
- /// <param name="destIndex">Current index in destination buffer.</param>
- /// <param name="destLength">Maximum length of destination buffer.</param>
- /// <param name="value">The float 32 value to format.</param>
- /// <param name="formatOptions">Formatting options.</param>
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static unsafe void ConvertFloatToString(byte* dest, ref int destIndex, int destLength, float value, FormatOptions formatOptions)
- {
- // deconstruct the floating point value
- tFloatUnion32 floatUnion = default;
- floatUnion.m_floatingPoint = value;
- uint floatExponent = floatUnion.GetExponent();
- uint floatMantissa = floatUnion.GetMantissa();
-
- // if this is a special value
- if (floatExponent == 0xFF)
- {
- FormatInfinityNaN(dest, ref destIndex, destLength, floatMantissa, floatUnion.IsNegative(), formatOptions);
- }
- // else this is a number
- else
- {
- // factor the value into its parts
- uint mantissa;
- int exponent;
- uint mantissaHighBitIdx;
- bool hasUnequalMargins;
- if (floatExponent != 0)
- {
- // normalized
- // The floating point equation is:
- // value = (1 + mantissa/2^23) * 2 ^ (exponent-127)
- // We convert the integer equation by factoring a 2^23 out of the exponent
- // value = (1 + mantissa/2^23) * 2^23 * 2 ^ (exponent-127-23)
- // value = (2^23 + mantissa) * 2 ^ (exponent-127-23)
- // Because of the implied 1 in front of the mantissa we have 24 bits of precision.
- // m = (2^23 + mantissa)
- // e = (exponent-127-23)
- mantissa = (uint)((1UL << 23) | floatMantissa);
- exponent = (int)(floatExponent - 127 - 23);
- mantissaHighBitIdx = 23;
- hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0);
- }
- else
- {
- // denormalized
- // The floating point equation is:
- // value = (mantissa/2^23) * 2 ^ (1-127)
- // We convert the integer equation by factoring a 2^23 out of the exponent
- // value = (mantissa/2^23) * 2^23 * 2 ^ (1-127-23)
- // value = mantissa * 2 ^ (1-127-23)
- // We have up to 23 bits of precision.
- // m = (mantissa)
- // e = (1-127-23)
- mantissa = floatMantissa;
- exponent = 1 - 127 - 23;
- mantissaHighBitIdx = LogBase2(mantissa);
- hasUnequalMargins = false;
- }
-
- var precision = formatOptions.Specifier == 0 ? -1 : formatOptions.Specifier;
- var bufferSize = Math.Max(SingleNumberBufferLength, precision + 1);
-
- var pOutBuffer = stackalloc byte[bufferSize];
- if (precision < 0)
- {
- precision = SinglePrecisionCustomFormat;
- }
-
- int printExponent;
- uint numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.TotalLength,
- (uint)precision,
- pOutBuffer,
- (uint)(bufferSize - 1),
- out printExponent);
-
- pOutBuffer[numPrintDigits] = 0;
-
- // Negative 0 are displayed as 0
- bool isNegative = floatUnion.IsNegative();
- if (floatUnion.m_integer == ((uint)1 << 31))
- {
- isNegative = false;
- }
-
- var number = new NumberBuffer(NumberBufferKind.Float, pOutBuffer, (int)numPrintDigits, printExponent + 1, isNegative);
- FormatNumber(dest, ref destIndex, destLength, ref number, precision, formatOptions);
- }
- }
-
- /// <summary>
- /// Format a float 64-bit to a general format to the specified destination buffer.
- /// </summary>
- /// <param name="dest">Destination buffer.</param>
- /// <param name="destIndex">Current index in destination buffer.</param>
- /// <param name="destLength">Maximum length of destination buffer.</param>
- /// <param name="value">The float 64 value to format.</param>
- /// <param name="formatOptions">Formatting options.</param>
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static unsafe void ConvertDoubleToString(byte* dest, ref int destIndex, int destLength, double value, FormatOptions formatOptions)
- {
- // deconstruct the floating point value
- tFloatUnion64 floatUnion = default;
- floatUnion.m_floatingPoint = value;
- uint floatExponent = floatUnion.GetExponent();
- ulong floatMantissa = floatUnion.GetMantissa();
-
- // if this is a special value
- if (floatExponent == 0x7FF)
- {
- FormatInfinityNaN(dest, ref destIndex, destLength, floatMantissa, floatUnion.IsNegative(), formatOptions);
- }
- // else this is a number
- else
- {
- // factor the value into its parts
- ulong mantissa;
- int exponent;
- uint mantissaHighBitIdx;
- bool hasUnequalMargins;
-
- if (floatExponent != 0)
- {
- // normal
- // The floating point equation is:
- // value = (1 + mantissa/2^52) * 2 ^ (exponent-1023)
- // We convert the integer equation by factoring a 2^52 out of the exponent
- // value = (1 + mantissa/2^52) * 2^52 * 2 ^ (exponent-1023-52)
- // value = (2^52 + mantissa) * 2 ^ (exponent-1023-52)
- // Because of the implied 1 in front of the mantissa we have 53 bits of precision.
- // m = (2^52 + mantissa)
- // e = (exponent-1023+1-53)
- mantissa = (1UL << 52) | floatMantissa;
- exponent = (int)(floatExponent - 1023 - 52);
- mantissaHighBitIdx = 52;
- hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0);
- }
- else
- {
- // subnormal
- // The floating point equation is:
- // value = (mantissa/2^52) * 2 ^ (1-1023)
- // We convert the integer equation by factoring a 2^52 out of the exponent
- // value = (mantissa/2^52) * 2^52 * 2 ^ (1-1023-52)
- // value = mantissa * 2 ^ (1-1023-52)
- // We have up to 52 bits of precision.
- // m = (mantissa)
- // e = (1-1023-52)
- mantissa = floatMantissa;
- exponent = 1 - 1023 - 52;
- mantissaHighBitIdx = LogBase2((uint)mantissa);
- hasUnequalMargins = false;
- }
-
- var precision = formatOptions.Specifier == 0 ? -1 : formatOptions.Specifier;
- var bufferSize = Math.Max(DoubleNumberBufferLength, precision + 1);
-
- var pOutBuffer = stackalloc byte[bufferSize];
- if (precision < 0)
- {
- precision = DoublePrecisionCustomFormat;
- }
-
- int printExponent;
- uint numPrintDigits = Dragon4(mantissa,
- exponent,
- mantissaHighBitIdx,
- hasUnequalMargins,
- CutoffMode.TotalLength,
- (uint)precision,
- pOutBuffer,
- (uint)(bufferSize - 1),
- out printExponent);
-
- pOutBuffer[numPrintDigits] = 0;
-
- // Negative 0 are displayed as 0
- bool isNegative = floatUnion.IsNegative();
- if (floatUnion.m_integer == ((ulong)1 << 63))
- {
- isNegative = false;
- }
-
- var number = new NumberBuffer(NumberBufferKind.Float, pOutBuffer, (int)numPrintDigits, printExponent + 1, isNegative);
- FormatNumber(dest, ref destIndex, destLength, ref number, precision, formatOptions);
- }
- }
- }
- }
|