calculator/src/CalcManager/Ratpack/conv.cpp
Michał Janiszewski f6a6aae6e6 Add additional defines for MSVC ARM and ARM64 to conv.cpp (#399)
Fixes ARM(64) regression introduced with #212
2019-03-28 11:17:06 -07:00

1581 lines
42 KiB
C++

// 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<uint32_t>(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<int32_t>(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<size_t>(radix))
{
*pmant-- = static_cast<MANTTYPE>(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<int32_t>(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<int32_t>(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;
}
}