// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //--------------------------------------------------------------------------- // Package Title ratpak // File conv.c // Copyright (C) 1995-97 Microsoft // Date 01-16-95 // // // Description // // Contains conversion, input and output routines for numbers rationals // and i32s. // // // //--------------------------------------------------------------------------- #include "pch.h" #include "ratpak.h" using namespace std; static constexpr int MAX_ZEROS_AFTER_DECIMAL = 2; // digits 0..64 used by bases 2 .. 64 static constexpr wstring_view DIGITS = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_@"; // ratio of internal 'digits' to output 'digits' // Calculated elsewhere as part of initialization and when base is changed int32_t g_ratio; // int(log(2L^BASEXPWR)/log(radix)) // Default decimal separator wchar_t g_decimalSeparator = L'.'; // The following defines and Calc_ULong* functions were taken from // https://github.com/dotnet/coreclr/blob/8b1595b74c943b33fa794e63e440e6f4c9679478/src/pal/inc/rt/intsafe.h // under MIT License // See also // * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros // * https://sourceforge.net/p/predef/wiki/Architectures/ #if defined(MIDL_PASS) || defined(RC_INVOKED) || defined(_M_CEE_PURE) \ || defined(_M_AMD64) || defined(__ARM_ARCH) || defined(__x86_64__) || defined(_M_ARM64) #ifndef Calc_UInt32x32To64 #define Calc_UInt32x32To64(a, b) ((uint64_t)((uint32_t)(a)) * (uint64_t)((uint32_t)(b))) #endif #elif defined(_M_IX86) || defined(__i386__) || defined(_M_ARM) #ifndef Calc_UInt32x32To64 #define Calc_UInt32x32To64(a, b) (uint64_t)((uint64_t)(uint32_t)(a) * (uint32_t)(b)) #endif #else #error Must define a target architecture. #endif #define CALC_INTSAFE_E_ARITHMETIC_OVERFLOW ((int32_t)0x80070216L) // 0x216 = 534 = ERROR_ARITHMETIC_OVERFLOW #define CALC_ULONG_ERROR ((uint32_t)0xffffffffU) namespace { int32_t Calc_ULongAdd( _In_ uint32_t ulAugend, _In_ uint32_t ulAddend, _Out_ uint32_t* pulResult) { int32_t hr = CALC_INTSAFE_E_ARITHMETIC_OVERFLOW; *pulResult = CALC_ULONG_ERROR; if ((ulAugend + ulAddend) >= ulAugend) { *pulResult = (ulAugend + ulAddend); hr = S_OK; } return hr; } int32_t Calc_ULongLongToULong( _In_ uint64_t ullOperand, _Out_ uint32_t* pulResult) { int32_t hr = CALC_INTSAFE_E_ARITHMETIC_OVERFLOW; *pulResult = CALC_ULONG_ERROR; if (ullOperand <= UINT32_MAX) { *pulResult = (uint32_t)ullOperand; hr = S_OK; } return hr; } int32_t Calc_ULongMult( _In_ uint32_t ulMultiplicand, _In_ uint32_t ulMultiplier, _Out_ uint32_t* pulResult) { uint64_t ull64Result = Calc_UInt32x32To64(ulMultiplicand, ulMultiplier); return Calc_ULongLongToULong(ull64Result, pulResult); } } // Used to strip trailing zeros, and prevent combinatorial explosions bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting); void SetDecimalSeparator(wchar_t decimalSeparator) { g_decimalSeparator = decimalSeparator; } // // Windows heap allocation // void* zmalloc(size_t a) { return calloc(a, sizeof(unsigned char)); } //----------------------------------------------------------------------------- // // FUNCTION: _dupnum // // ARGUMENTS: pointer to a number, pointer to a number // // RETURN: None // // DESCRIPTION: Copies the source to the destination // //----------------------------------------------------------------------------- void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER * const src) { memcpy(dest, src, (int)(sizeof(NUMBER) + ((src)->cdigit)*(sizeof(MANTTYPE)))); } //----------------------------------------------------------------------------- // // FUNCTION: _destroynum // // ARGUMENTS: pointer to a number // // RETURN: None // // DESCRIPTION: Deletes the number and associated allocation // //----------------------------------------------------------------------------- void _destroynum( _In_ PNUMBER pnum ) { if ( pnum != nullptr) { free( pnum ); } } //----------------------------------------------------------------------------- // // FUNCTION: _destroyrat // // ARGUMENTS: pointer to a rational // // RETURN: None // // DESCRIPTION: Deletes the rational and associated // allocations. // //----------------------------------------------------------------------------- void _destroyrat( _In_ PRAT prat ) { if ( prat != nullptr) { destroynum( prat->pp ); destroynum( prat->pq ); free( prat ); } } //----------------------------------------------------------------------------- // // FUNCTION: _createnum // // ARGUMENTS: size of number in 'digits' // // RETURN: pointer to a number // // DESCRIPTION: allocates and zeros out number type. // //----------------------------------------------------------------------------- PNUMBER _createnum( _In_ uint32_t size ) { PNUMBER pnumret= nullptr; uint32_t cbAlloc; // sizeof( MANTTYPE ) is the size of a 'digit' if (SUCCEEDED(Calc_ULongAdd(size, 1, &cbAlloc)) && SUCCEEDED(Calc_ULongMult(cbAlloc, sizeof(MANTTYPE), &cbAlloc)) && SUCCEEDED(Calc_ULongAdd(cbAlloc, sizeof(NUMBER), &cbAlloc))) { pnumret = (PNUMBER)zmalloc( cbAlloc ); if ( pnumret == nullptr) { throw( CALC_E_OUTOFMEMORY ); } } else { throw( CALC_E_INVALIDRANGE ); } return( pnumret ); } //----------------------------------------------------------------------------- // // FUNCTION: _createrat // // ARGUMENTS: none // // RETURN: pointer to a rational // // DESCRIPTION: allocates a rational structure but does not // allocate the numbers that make up the rational p over q // form. These number pointers are left pointing to null. // //----------------------------------------------------------------------------- PRAT _createrat( void ) { PRAT prat= nullptr; prat = (PRAT)zmalloc( sizeof( RAT ) ); if ( prat == nullptr) { throw( CALC_E_OUTOFMEMORY ); } prat->pp = nullptr; prat->pq = nullptr; return( prat ); } //----------------------------------------------------------------------------- // // FUNCTION: numtorat // // ARGUMENTS: pointer to a number, radix number is in. // // RETURN: Rational representation of number. // // DESCRIPTION: The rational representation of the number // is guaranteed to be in the form p (number with internal // base representation) over q (number with internal base // representation) Where p and q are integers. // //----------------------------------------------------------------------------- PRAT numtorat( _In_ PNUMBER pin, uint32_t radix) { PNUMBER pnRadixn= nullptr; DUPNUM( pnRadixn, pin ); PNUMBER qnRadixn=i32tonum( 1, radix); // Ensure p and q start out as integers. if ( pnRadixn->exp < 0 ) { qnRadixn->exp -= pnRadixn->exp; pnRadixn->exp = 0; } PRAT pout= nullptr; createrat(pout); // There is probably a better way to do this. pout->pp = numtonRadixx(pnRadixn, radix); pout->pq = numtonRadixx(qnRadixn, radix); destroynum( pnRadixn ); destroynum( qnRadixn ); return( pout ); } //---------------------------------------------------------------------------- // // FUNCTION: nRadixxtonum // // ARGUMENTS: pointer to a number, base requested. // // RETURN: number representation in radix requested. // // DESCRIPTION: Does a base conversion on a number from // internal to requested base. Assumes number being passed // in is really in internal base form. // //---------------------------------------------------------------------------- PNUMBER nRadixxtonum( _In_ PNUMBER a, uint32_t radix, int32_t precision) { uint32_t bitmask; uint32_t cdigits; MANTTYPE *ptr; PNUMBER sum = i32tonum( 0, radix ); PNUMBER powofnRadix = i32tonum( BASEX, radix ); // A large penalty is paid for conversion of digits no one will see anyway. // limit the digits to the minimum of the existing precision or the // requested precision. cdigits = precision + 1; if ( cdigits > (uint32_t)a->cdigit ) { cdigits = (uint32_t)a->cdigit; } // scale by the internal base to the internal exponent offset of the LSD numpowi32( &powofnRadix, a->exp + (a->cdigit - cdigits), radix, precision); // Loop over all the relative digits from MSD to LSD for ( ptr = &(a->mant[a->cdigit-1]); cdigits > 0; ptr--, cdigits-- ) { // Loop over all the bits from MSB to LSB for ( bitmask = BASEX/2; bitmask > 0; bitmask /= 2 ) { addnum( &sum, sum, radix ); if ( *ptr & bitmask ) { sum->mant[0] |= 1; } } } // Scale answer by power of internal exponent. mulnum( &sum, powofnRadix, radix ); destroynum( powofnRadix ); sum->sign = a->sign; return( sum ); } //----------------------------------------------------------------------------- // // FUNCTION: numtonRadixx // // ARGUMENTS: pointer to a number, radix of that number. // // RETURN: number representation in internal radix. // // DESCRIPTION: Does a radix conversion on a number from // specified radix to requested radix. Assumes the radix // specified is the radix of the number passed in. // //----------------------------------------------------------------------------- PNUMBER numtonRadixx(_In_ PNUMBER a, uint32_t radix) { PNUMBER pnumret = i32tonum(0, BASEX); // pnumret is the number in internal form. PNUMBER num_radix = i32tonum(radix, BASEX); MANTTYPE *ptrdigit = a->mant; // pointer to digit being worked on. // Digits are in reverse order, back over them LSD first. ptrdigit += a->cdigit-1; PNUMBER thisdigit = nullptr; // thisdigit holds the current digit of a // being summed into result. int32_t idigit; // idigit is the iterate of digits in a. for ( idigit = 0; idigit < a->cdigit; idigit++ ) { mulnumx( &pnumret, num_radix); // WARNING: // This should just smack in each digit into a 'special' thisdigit. // and not do the overhead of recreating the number type each time. thisdigit = i32tonum( *ptrdigit--, BASEX ); addnum( &pnumret, thisdigit, BASEX ); destroynum( thisdigit ); } // Calculate the exponent of the external base for scaling. numpowi32x( &num_radix, a->exp ); // ... and scale the result. mulnumx( &pnumret, num_radix); destroynum(num_radix); // And propagate the sign. pnumret->sign = a->sign; return( pnumret ); } //----------------------------------------------------------------------------- // // FUNCTION: StringToRat // // ARGUMENTS: // mantissaIsNegative true if mantissa is less than zero // mantissa a string representation of a number // exponentIsNegative true if exponent is less than zero // exponent a string representation of a number // radix is the number base used in the source string // // RETURN: PRAT representation of string input. // Or nullptr if no number scanned. // // EXPLANATION: This is for calc. // // //----------------------------------------------------------------------------- PRAT StringToRat(bool mantissaIsNegative, wstring_view mantissa, bool exponentIsNegative, wstring_view exponent, uint32_t radix, int32_t precision) { PRAT resultRat = nullptr; // holds exponent in rational form. // Deal with mantissa if (mantissa.empty()) { // Preset value if no mantissa if (exponent.empty()) { // Exponent not specified, preset value to zero DUPRAT(resultRat, rat_zero); } else { // Exponent specified, preset value to one DUPRAT(resultRat, rat_one); } } else { // Mantissa specified, convert to number form. PNUMBER pnummant = StringToNumber(mantissa, radix, precision); if (pnummant == nullptr) { return nullptr; } resultRat = numtorat(pnummant, radix); // convert to rational form, and cleanup. destroynum(pnummant); } // Deal with exponent int32_t expt = 0; if (!exponent.empty()) { // Exponent specified, convert to number form. // Don't use native stuff, as it is restricted in the bases it can // handle. PNUMBER numExp = StringToNumber(exponent, radix, precision); if (numExp == nullptr) { return nullptr; } // Convert exponent number form to native integral form, and cleanup. expt = numtoi32(numExp, radix); destroynum(numExp); } // Convert native integral exponent form to rational multiplier form. PNUMBER pnumexp = i32tonum(radix, BASEX); numpowi32x(&pnumexp, abs(expt)); PRAT pratexp = nullptr; createrat(pratexp); DUPNUM(pratexp->pp, pnumexp); pratexp->pq = i32tonum(1, BASEX); destroynum(pnumexp); if (exponentIsNegative) { // multiplier is less than 1, this means divide. divrat(&resultRat, pratexp, precision); } else if (expt > 0) { // multiplier is greater than 1, this means multiply. mulrat(&resultRat, pratexp, precision); } // multiplier can be 1, in which case it'd be a waste of time to multiply. destroyrat(pratexp); if (mantissaIsNegative) { // A negative number was used, adjust the sign. resultRat->pp->sign *= -1; } return resultRat; } //----------------------------------------------------------------------------- // // FUNCTION: StringToNumber // // ARGUMENTS: // wstring_view numberString // int radix // int32_t precision // // RETURN: pnumber representation of string input. // Or nullptr if no number scanned. // // EXPLANATION: This is a state machine, // // State Description Example, ^shows just read position. // which caused the transition // // START Start state ^1.0 // MANTS Mantissa sign -^1.0 // LZ Leading Zero 0^1.0 // LZDP Post LZ dec. pt. 000.^1 // LD Leading digit 1^.0 // DZ Post LZDP Zero 000.0^1 // DD Post Decimal digit .01^2 // DDP Leading Digit dec. pt. 1.^2 // EXPB Exponent Begins 1.0e^2 // EXPS Exponent sign 1.0e+^5 // EXPD Exponent digit 1.0e1^2 or even 1.0e0^1 // EXPBZ Exponent begin post 0 0.000e^+1 // EXPSZ Exponent sign post 0 0.000e+^1 // EXPDZ Exponent digit post 0 0.000e+1^2 // ERR Error case 0.0.^ // // Terminal Description // // DP '.' // ZR '0' // NZ '1'..'9' 'A'..'Z' 'a'..'z' '@' '_' // SG '+' '-' // EX 'e' '^' e is used for radix 10, ^ for all other radixes. // //----------------------------------------------------------------------------- static constexpr uint8_t DP = 0; static constexpr uint8_t ZR = 1; static constexpr uint8_t NZ = 2; static constexpr uint8_t SG = 3; static constexpr uint8_t EX = 4; static constexpr uint8_t START = 0; static constexpr uint8_t MANTS = 1; static constexpr uint8_t LZ = 2; static constexpr uint8_t LZDP = 3; static constexpr uint8_t LD = 4; static constexpr uint8_t DZ = 5; static constexpr uint8_t DD = 6; static constexpr uint8_t DDP = 7; static constexpr uint8_t EXPB = 8; static constexpr uint8_t EXPS = 9; static constexpr uint8_t EXPD = 10; static constexpr uint8_t EXPBZ = 11; static constexpr uint8_t EXPSZ = 12; static constexpr uint8_t EXPDZ = 13; static constexpr uint8_t ERR = 14; #if defined( DEBUG ) char *statestr[] = { "START", "MANTS", "LZ", "LZDP", "LD", "DZ", "DD", "DDP", "EXPB", "EXPS", "EXPD", "EXPBZ", "EXPSZ", "EXPDZ", "ERR", }; #endif // New state is machine[state][terminal] static constexpr uint8_t machine[ERR+1][EX+1]= { // DP, ZR, NZ, SG, EX // START { LZDP, LZ, LD, MANTS, ERR }, // MANTS { LZDP, LZ, LD, ERR, ERR }, // LZ { LZDP, LZ, LD, ERR, EXPBZ }, // LZDP { ERR, DZ, DD, ERR, EXPB }, // LD { DDP, LD, LD, ERR, EXPB }, // DZ { ERR, DZ, DD, ERR, EXPBZ }, // DD { ERR, DD, DD, ERR, EXPB }, // DDP { ERR, DD, DD, ERR, EXPB }, // EXPB { ERR, EXPD, EXPD, EXPS, ERR }, // EXPS { ERR, EXPD, EXPD, ERR, ERR }, // EXPD { ERR, EXPD, EXPD, ERR, ERR }, // EXPBZ { ERR, EXPDZ, EXPDZ, EXPSZ, ERR }, // EXPSZ { ERR, EXPDZ, EXPDZ, ERR, ERR }, // EXPDZ { ERR, EXPDZ, EXPDZ, ERR, ERR }, // ERR { ERR, ERR, ERR, ERR, ERR } }; wchar_t NormalizeCharDigit(wchar_t c, uint32_t radix) { // Allow upper and lower case letters as equivalent, base // is in the range where this is not ambiguous. if (size_t{ radix } >= DIGITS.find(L'A') && size_t { radix } <= DIGITS.find(L'Z')) { return towupper(c); } return c; } PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precision) { int32_t expSign = 1L; // expSign is exponent sign ( +/- 1 ) int32_t expValue = 0L; // expValue is exponent mantissa, should be unsigned PNUMBER pnumret = nullptr; createnum(pnumret, static_cast(numberString.length())); pnumret->sign = 1L; pnumret->cdigit = 0; pnumret->exp = 0; MANTTYPE *pmant = pnumret->mant + numberString.length() - 1; uint8_t state = START; // state is the state of the input state machine. wchar_t curChar; for (const auto& c : numberString) { // If the character is the decimal separator, use L'.' for the purposes of the state machine. curChar = (c == g_decimalSeparator ? L'.' : c); // Switch states based on the character we encountered switch (curChar) { case L'-': case L'+': state = machine[state][SG]; break; case L'.': state = machine[state][DP]; break; case L'0': state = machine[state][ZR]; break; case L'^': case L'e': if (curChar == L'^' || radix == 10) { state = machine[state][EX]; break; } // Drop through in the 'e'-as-a-digit case [[fallthrough]]; default: state = machine[state][NZ]; break; } // Now update our result value based on the state we are in switch (state) { case MANTS: pnumret->sign = (curChar == L'-') ? -1 : 1; break; case EXPSZ: case EXPS: expSign = (curChar == L'-') ? -1 : 1; break; case EXPDZ: case EXPD: { curChar = NormalizeCharDigit(curChar, radix); size_t pos = DIGITS.find(curChar); if (pos != wstring_view::npos) { expValue *= radix; expValue += static_cast(pos); } else { state = ERR; } } break; case LD: pnumret->exp++; [[fallthrough]]; case DD: { curChar = NormalizeCharDigit(curChar, radix); size_t pos = DIGITS.find(curChar); if (pos != wstring_view::npos && pos < static_cast(radix)) { *pmant-- = static_cast(pos); pnumret->exp--; pnumret->cdigit++; } else { state = ERR; } } break; case DZ: pnumret->exp--; break; case LZ: case LZDP: case DDP: break; } } if (state == DZ || state == EXPDZ) { pnumret->cdigit = 1; pnumret->exp = 0; pnumret->sign = 1; } else { while (pnumret->cdigit < static_cast(numberString.length())) { pnumret->cdigit++; pnumret->exp--; } pnumret->exp += expSign * expValue; } // If we don't have a number, clear our result. if (pnumret->cdigit == 0) { destroynum(pnumret); pnumret = nullptr; } stripzeroesnum(pnumret, precision); return pnumret; } //----------------------------------------------------------------------------- // // FUNCTION: i32torat // // ARGUMENTS: int32_t // // RETURN: Rational representation of int32_t input. // // DESCRIPTION: Converts int32_t input to rational (p over q) // form, where q is 1 and p is the int32_t. // //----------------------------------------------------------------------------- PRAT i32torat( _In_ int32_t ini32 ) { PRAT pratret= nullptr; createrat( pratret ); pratret->pp = i32tonum(ini32, BASEX ); pratret->pq = i32tonum(1L, BASEX ); return( pratret ); } //----------------------------------------------------------------------------- // // FUNCTION: Ui32torat // // ARGUMENTS: ui32 // // RETURN: Rational representation of uint32_t input. // // DESCRIPTION: Converts uint32_t input to rational (p over q) // form, where q is 1 and p is the uint32_t. Being unsigned cant take negative // numbers, but the full range of unsigned numbers // //----------------------------------------------------------------------------- PRAT Ui32torat( _In_ uint32_t inui32 ) { PRAT pratret= nullptr; createrat( pratret ); pratret->pp = Ui32tonum(inui32, BASEX ); pratret->pq = i32tonum(1L, BASEX ); return( pratret ); } //----------------------------------------------------------------------------- // // FUNCTION: i32tonum // // ARGUMENTS: int32_t input and radix requested. // // RETURN: number // // DESCRIPTION: Returns a number representation in the // base requested of the int32_t value passed in. // //----------------------------------------------------------------------------- PNUMBER i32tonum( int32_t ini32, uint32_t radix) { MANTTYPE *pmant; PNUMBER pnumret= nullptr; createnum( pnumret, MAX_LONG_SIZE ); pmant = pnumret->mant; pnumret->cdigit = 0; pnumret->exp = 0; if ( ini32 < 0 ) { pnumret->sign = -1; ini32 *= -1; } else { pnumret->sign = 1; } do { *pmant++ = (MANTTYPE)(ini32 % radix); ini32 /= radix; pnumret->cdigit++; } while ( ini32 ); return( pnumret ); } //----------------------------------------------------------------------------- // // FUNCTION: Ui32tonum // // ARGUMENTS: uint32_t input and radix requested. // // RETURN: number // // DESCRIPTION: Returns a number representation in the // base requested of the uint32_t value passed in. Being unsigned number it has no // negative number and takes the full range of unsigned number // //----------------------------------------------------------------------------- PNUMBER Ui32tonum(uint32_t ini32, uint32_t radix) { MANTTYPE *pmant; PNUMBER pnumret= nullptr; createnum( pnumret, MAX_LONG_SIZE ); pmant = pnumret->mant; pnumret->cdigit = 0; pnumret->exp = 0; pnumret->sign = 1; do { *pmant++ = (MANTTYPE)(ini32 % radix); ini32 /= radix; pnumret->cdigit++; } while ( ini32 ); return( pnumret ); } //----------------------------------------------------------------------------- // // FUNCTION: rattoi32 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision. // // RETURN: int32_t // // DESCRIPTION: returns the int32_t representation of the // number input. Assumes that the number is in the internal // base. // //----------------------------------------------------------------------------- int32_t rattoi32( _In_ PRAT prat , uint32_t radix, int32_t precision) { if ( rat_gt( prat, rat_max_i32, precision) || rat_lt( prat, rat_min_i32, precision) ) { // Don't attempt rattoi32 of anything too big or small throw( CALC_E_DOMAIN ); } PRAT pint = nullptr; DUPRAT(pint,prat); intrat( &pint, radix, precision); divnumx( &(pint->pp), pint->pq, precision); DUPNUM( pint->pq, num_one ); int32_t lret = numtoi32( pint->pp, BASEX ); destroyrat(pint); return( lret ); } //----------------------------------------------------------------------------- // // FUNCTION: rattoUi32 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision. // // RETURN: Ui32 // // DESCRIPTION: returns the Ui32 representation of the // number input. Assumes that the number is in the internal // base. // //----------------------------------------------------------------------------- uint32_t rattoUi32( _In_ PRAT prat, uint32_t radix, int32_t precision) { if ( rat_gt( prat, rat_dword, precision) || rat_lt( prat, rat_zero, precision) ) { // Don't attempt rattoui32 of anything too big or small throw( CALC_E_DOMAIN ); } PRAT pint = nullptr; DUPRAT(pint,prat); intrat( &pint, radix, precision); divnumx( &(pint->pp), pint->pq, precision); DUPNUM( pint->pq, num_one ); uint32_t lret = numtoi32( pint->pp, BASEX ); // This happens to work even if it is only signed destroyrat(pint); return( lret ); } //----------------------------------------------------------------------------- // // FUNCTION: rattoUi64 // // ARGUMENTS: rational number in internal base, integer radix and int32_t precision // // RETURN: Ui64 // // DESCRIPTION: returns the 64 bit (irrespective of which processor this is running in) representation of the // number input. Assumes that the number is in the internal // base. Can throw exception if the number exceeds 2^64 // Implementation by getting the HI & LO 32 bit words and concatenating them, as the // internal base chosen happens to be 2^32, this is easier. //----------------------------------------------------------------------------- uint64_t rattoUi64( _In_ PRAT prat, uint32_t radix, int32_t precision) { PRAT pint = nullptr; // first get the LO 32 bit word DUPRAT(pint, prat); andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) uint32_t lo = rattoUi32(pint, radix, precision); // wont throw exception because already hi-dword chopped off DUPRAT(pint, prat); // previous pint will get freed by this as well PRAT prat32 = i32torat(32); rshrat(&pint, prat32, radix, precision); intrat( &pint, radix, precision); andrat(&pint, rat_dword, radix, precision); // & 0xFFFFFFFF (2 ^ 32 -1) uint32_t hi = rattoUi32(pint, radix, precision); destroyrat(prat32); destroyrat(pint); return (((uint64_t)hi << 32) | lo); } //----------------------------------------------------------------------------- // // FUNCTION: numtoi32 // // ARGUMENTS: number input and base of that number. // // RETURN: int32_t // // DESCRIPTION: returns the int32_t representation of the // number input. Assumes that the number is really in the // base claimed. // //----------------------------------------------------------------------------- int32_t numtoi32( _In_ PNUMBER pnum, uint32_t radix ) { int32_t lret = 0; MANTTYPE *pmant = pnum->mant; pmant += pnum->cdigit - 1; int32_t expt = pnum->exp; for (int32_t length = pnum->cdigit; length > 0 && length + expt > 0; length--) { lret *= radix; lret += *(pmant--); } while (expt-- > 0) { lret *= radix; } lret *= pnum->sign; return lret; } //----------------------------------------------------------------------------- // // FUNCTION: bool stripzeroesnum // // ARGUMENTS: a number representation // // RETURN: true if stripping done, modifies number in place. // // DESCRIPTION: Strips off trailing zeros. // //----------------------------------------------------------------------------- bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting) { MANTTYPE *pmant; int32_t cdigits; bool fstrip = false; // point pmant to the LeastCalculatedDigit pmant=pnum->mant; cdigits=pnum->cdigit; // point pmant to the LSD if ( cdigits > starting ) { pmant += cdigits - starting; cdigits = starting; } // Check we haven't gone too far, and we are still looking at zeros. while ( ( cdigits > 0 ) && !(*pmant) ) { // move to next significant digit and keep track of digits we can // ignore later. pmant++; cdigits--; fstrip = true; } // If there are zeros to remove. if ( fstrip ) { // Remove them. memmove( pnum->mant, pmant, (int)(cdigits*sizeof(MANTTYPE)) ); // And adjust exponent and digit count accordingly. pnum->exp += ( pnum->cdigit - cdigits ); pnum->cdigit = cdigits; } return( fstrip ); } //----------------------------------------------------------------------------- // // FUNCTION: NumberToString // // ARGUMENTS: number representation // fmt, one of FMT_FLOAT FMT_SCIENTIFIC or // FMT_ENGINEERING // integer radix and int32_t precision value // // RETURN: String representation of number. // // DESCRIPTION: Converts a number to it's string // representation. // //----------------------------------------------------------------------------- wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision) { stripzeroesnum(pnum, precision + 2); int32_t length = pnum->cdigit; int32_t exponent = pnum->exp + length; // Actual number of digits to the left of decimal int32_t oldFormat = format; if (exponent > precision && format == FMT_FLOAT) { // Force scientific mode to prevent user from assuming 33rd digit is exact. format = FMT_SCIENTIFIC; } // Make length small enough to fit in pret. if (length > precision) { length = precision; } // If there is a chance a round has to occur, round. // - if number is zero no rounding // - if number of digits is less than the maximum output no rounding PNUMBER round = nullptr; if (!zernum(pnum) && (pnum->cdigit >= precision || (length - exponent > precision && exponent >= -MAX_ZEROS_AFTER_DECIMAL))) { // Otherwise round. round = i32tonum(radix, radix); divnum(&round, num_two, radix, precision); // Make round number exponent one below the LSD for the number. if (exponent > 0 || format == FMT_FLOAT) { round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision; } else { round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision - exponent; length = precision + exponent; } round->sign = pnum->sign; } if (format == FMT_FLOAT) { // Figure out if the exponent will fill more space than the non-exponent field. if ((length - exponent > precision) || (exponent > precision + 3)) { if (exponent >= -MAX_ZEROS_AFTER_DECIMAL) { round->exp -= exponent; length = precision + exponent; } else { // Case where too many zeros are to the right or left of the // decimal pt. And we are forced to switch to scientific form. format = FMT_SCIENTIFIC; } } else if (length + abs(exponent) < precision && round) { // Minimum loss of precision occurs with listing leading zeros // if we need to make room for zeros sacrifice some digits. round->exp -= exponent; } } if (round != nullptr) { addnum(&pnum, round, radix); int32_t offset = (pnum->cdigit + pnum->exp) - (round->cdigit + round->exp); destroynum(round); if (stripzeroesnum(pnum, offset)) { // WARNING: nesting/recursion, too much has been changed, need to // re-figure format. return NumberToString(pnum, oldFormat, radix, precision); } } else { stripzeroesnum(pnum, precision); } // Set up all the post rounding stuff. bool useSciForm = false; int32_t eout = exponent - 1; // Displayed exponent. MANTTYPE *pmant = pnum->mant + pnum->cdigit - 1; // Case where too many digits are to the left of the decimal or // FMT_SCIENTIFIC or FMT_ENGINEERING was specified. if ((format == FMT_SCIENTIFIC) || (format == FMT_ENGINEERING)) { useSciForm = true; if (eout != 0) { if (format == FMT_ENGINEERING) { exponent = (eout % 3); eout -= exponent; exponent++; // Fix the case where 0.02e-3 should really be 2.e-6 etc. if (exponent < 0) { exponent += 3; eout -= 3; } } else { exponent = 1; } } } else { eout = 0; } // Begin building the result string wstringstream resultStream{}; // Make sure negative zeros aren't allowed. if ((pnum->sign == -1) && (length > 0)) { resultStream << L'-'; } if (exponent <= 0 && !useSciForm) { resultStream << L'0'; resultStream << g_decimalSeparator; // Used up a digit unaccounted for. } while (exponent < 0) { resultStream << L'0'; exponent++; } while (length > 0) { exponent--; resultStream << DIGITS[*pmant--]; length--; // Be more regular in using a decimal point. if (exponent == 0) { resultStream << g_decimalSeparator; } } while (exponent > 0) { resultStream << L'0'; exponent--; // Be more regular in using a decimal point. if (exponent == 0) { resultStream << g_decimalSeparator; } } if (useSciForm) { resultStream << (radix == 10 ? L'e' : L'^'); resultStream << (eout < 0 ? L'-' : L'+'); eout = abs(eout); wstringstream exponentStream{}; do { exponentStream << DIGITS[eout % radix]; eout /= radix; } while (eout > 0); auto expString = exponentStream.str(); for (auto ritr = expString.rbegin(); ritr != expString.rend(); ritr++) { resultStream << *ritr; } } // Remove trailing decimal auto resultString = resultStream.str(); if (!resultString.empty() && resultString.back() == g_decimalSeparator) { resultString.pop_back(); } return resultString; } //----------------------------------------------------------------------------- // // FUNCTION: RatToString // // ARGUMENTS: // PRAT *representation of a number. // i32 representation of base to dump to screen. // fmt, one of FMT_FLOAT FMT_SCIENTIFIC or FMT_ENGINEERING // precision uint32_t // // RETURN: string // // DESCRIPTION: returns a string representation of rational number passed // in, at least to the precision digits. // // NOTE: It may be that doing a GCD() could shorten the rational form // And it may eventually be worthwhile to keep the result. That is // why a pointer to the rational is passed in. // //----------------------------------------------------------------------------- wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision) { PNUMBER p = RatToNumber(prat, radix, precision); wstring result = NumberToString(p, format, radix, precision); destroynum(p); return result; } PNUMBER RatToNumber(_In_ PRAT prat, uint32_t radix, int32_t precision) { PRAT temprat = nullptr; DUPRAT(temprat, prat); // Convert p and q of rational form from internal base to requested base. // Scale by largest power of BASEX possible. int32_t scaleby = min(temprat->pp->exp, temprat->pq->exp); scaleby = max(scaleby, 0); temprat->pp->exp -= scaleby; temprat->pq->exp -= scaleby; PNUMBER p = nRadixxtonum(temprat->pp, radix, precision); PNUMBER q = nRadixxtonum(temprat->pq, radix, precision); destroyrat(temprat); // finally take the time hit to actually divide. divnum(&p, q, radix, precision); destroynum(q); return p; } // Converts a PRAT to a PNUMBER and back to a PRAT, flattening/simplifying the rational in the process void flatrat(_Inout_ PRAT& prat, uint32_t radix, int32_t precision) { PNUMBER pnum = RatToNumber(prat, radix, precision); destroyrat(prat); prat = numtorat(pnum, radix); destroynum(pnum); } //----------------------------------------------------------------------------- // // FUNCTION: gcd // // ARGUMENTS: // PNUMBER representation of a number. // PNUMBER representation of a number. // int for Radix // // RETURN: Greatest common divisor in internal BASEX PNUMBER form. // // DESCRIPTION: gcd uses remainders to find the greatest common divisor. // // ASSUMPTIONS: gcd assumes inputs are integers. // // NOTE: Before it was found that the TRIM macro actually kept the // size down cheaper than GCD, this routine was used extensively. // now it is not used but might be later. // //----------------------------------------------------------------------------- PNUMBER gcd( _In_ PNUMBER a, _In_ PNUMBER b) { PNUMBER r= nullptr; PNUMBER larger= nullptr; PNUMBER smaller= nullptr; if (zernum(a)) { return b; } else if(zernum(b)) { return a; } if (lessnum(a, b)) { DUPNUM(larger, b); DUPNUM(smaller, a); } else { DUPNUM(larger, a); DUPNUM(smaller, b); } while (!zernum(smaller)) { remnum(&larger, smaller, BASEX); // swap larger and smaller r = larger; larger = smaller; smaller = r; } destroynum(smaller); return larger; } //----------------------------------------------------------------------------- // // FUNCTION: i32factnum // // ARGUMENTS: // int32_t integer to factorialize. // int32_t integer representing base of answer. // uint32_t integer for radix // // RETURN: Factorial of input in radix PNUMBER form. // // NOTE: Not currently used. // //----------------------------------------------------------------------------- PNUMBER i32factnum(int32_t ini32, uint32_t radix) { PNUMBER lret= nullptr; PNUMBER tmp= nullptr; lret = i32tonum( 1, radix); while ( ini32 > 0 ) { tmp = i32tonum( ini32--, radix); mulnum( &lret, tmp, radix); destroynum( tmp ); } return( lret ); } //----------------------------------------------------------------------------- // // FUNCTION: i32prodnum // // ARGUMENTS: // int32_t integer to factorialize. // int32_t integer representing base of answer. // uint32_t integer for radix // // RETURN: Factorial of input in base PNUMBER form. // //----------------------------------------------------------------------------- PNUMBER i32prodnum(int32_t start, int32_t stop, uint32_t radix) { PNUMBER lret= nullptr; PNUMBER tmp= nullptr; lret = i32tonum( 1, radix); while ( start <= stop ) { if ( start ) { tmp = i32tonum( start, radix); mulnum( &lret, tmp, radix); destroynum( tmp ); } start++; } return( lret ); } //----------------------------------------------------------------------------- // // FUNCTION: numpowi32 // // ARGUMENTS: root as number power as int32_t and radix of // number along with the precision value in int32_t. // // RETURN: None root is changed. // // DESCRIPTION: changes numeric representation of root to // root ** power. Assumes radix is the radix of root. // //----------------------------------------------------------------------------- void numpowi32( _Inout_ PNUMBER *proot, int32_t power, uint32_t radix, int32_t precision) { PNUMBER lret = i32tonum( 1, radix ); while ( power > 0 ) { if ( power & 1 ) { mulnum( &lret, *proot, radix ); } mulnum( proot, *proot, radix ); TRIMNUM(*proot, precision); power >>= 1; } destroynum( *proot ); *proot=lret; } //----------------------------------------------------------------------------- // // FUNCTION: ratpowi32 // // ARGUMENTS: root as rational, power as int32_t and precision as int32_t. // // RETURN: None root is changed. // // DESCRIPTION: changes rational representation of root to // root ** power. // //----------------------------------------------------------------------------- void ratpowi32( _Inout_ PRAT *proot, int32_t power, int32_t precision) { if ( power < 0 ) { // Take the positive power and invert answer. PNUMBER pnumtemp = nullptr; ratpowi32( proot, -power, precision); pnumtemp = (*proot)->pp; (*proot)->pp = (*proot)->pq; (*proot)->pq = pnumtemp; } else { PRAT lret= nullptr; lret = i32torat( 1 ); while ( power > 0 ) { if ( power & 1 ) { mulnumx( &(lret->pp), (*proot)->pp ); mulnumx( &(lret->pq), (*proot)->pq ); } mulrat( proot, *proot, precision); trimit(&lret, precision); trimit(proot, precision); power >>= 1; } destroyrat( *proot ); *proot=lret; } }