|
|
//---------------------------------------------------------------------------
// Package Title ratpak
// File conv.c
// Author Timothy David Corrie Jr. ([email protected])
// Copyright (C) 1995-97 Microsoft
// Date 01-16-95
//
//
// Description
//
// Contains conversion, input and output routines for numbers rationals
// and longs.
//
//
//
//---------------------------------------------------------------------------
#include <stdio.h>
#include <tchar.h> // TCHAR version of sprintf
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#if defined( DOS )
#include <dosstub.h>
#else
#include <windows.h>
#endif
#include <ratpak.h>
BOOL fparserror=FALSE; BOOL gbinexact=FALSE;
// digits 0..64 used by bases 2 .. 64
TCHAR digits[65]=TEXT("0123456789") TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZ") TEXT("abcdefghijklmnopqrstuvwxyz_@");
// ratio of internal 'digits' to output 'digits'
// Calculated elsewhere as part of initialization and when base is changed
long ratio; // int(log(2L^BASEXPWR)/log(nRadix))
// Used to strip trailing zeroes, and prevent combinatorial explosions
BOOL stripzeroesnum( PNUMBER pnum, long starting );
// returns int(lognRadix(x)) quickly.
long longlognRadix( long x );
//----------------------------------------------------------------------------
//
// FUNCTION: fail
//
// ARGUMENTS: pointer to an error message.
//
// RETURN: None
//
// DESCRIPTION: fail dumps the error message then throws an exception
//
//----------------------------------------------------------------------------
void fail( IN long errmsg )
{ #ifdef DEBUG
fprintf( stderr, "%s\n", TEXT("Out of Memory") ); #endif
throw( CALC_E_OUTOFMEMORY ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: _destroynum
//
// ARGUMENTS: pointer to a number
//
// RETURN: None
//
// DESCRIPTION: Deletes the number and associated allocation
//
//-----------------------------------------------------------------------------
void _destroynum( IN PNUMBER pnum )
{ if ( pnum != NULL ) { zfree( pnum ); } }
//-----------------------------------------------------------------------------
//
// FUNCTION: _destroyrat
//
// ARGUMENTS: pointer to a rational
//
// RETURN: None
//
// DESCRIPTION: Deletes the rational and associated
// allocations.
//
//-----------------------------------------------------------------------------
void _destroyrat( IN PRAT prat )
{ if ( prat != NULL ) { destroynum( prat->pp ); destroynum( prat->pq ); zfree( prat ); } }
//-----------------------------------------------------------------------------
//
// FUNCTION: _createnum
//
// ARGUMENTS: size of number in 'digits'
//
// RETURN: pointer to a number
//
// DESCRIPTION: allocates and zeroes out number type.
//
//-----------------------------------------------------------------------------
PNUMBER _createnum( IN long size )
{ PNUMBER pnumret=NULL;
// sizeof( MANTTYPE ) is the size of a 'digit'
pnumret = (PNUMBER)zmalloc( (int)(size+1) * sizeof( MANTTYPE ) + sizeof( NUMBER ) ); if ( pnumret == NULL ) { fail( CALC_E_OUTOFMEMORY ); } 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=NULL;
prat = (PRAT)zmalloc( sizeof( RAT ) );
if ( prat == NULL ) { fail( CALC_E_OUTOFMEMORY ); } prat->pp = NULL; prat->pq = NULL; return( prat ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: numtorat
//
// ARGUMENTS: pointer to a number, nRadix 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, IN unsigned long nRadix )
{ PRAT pout=NULL; PNUMBER pnRadixn=NULL; PNUMBER qnRadixn=NULL;
DUPNUM( pnRadixn, pin );
qnRadixn=longtonum( 1, nRadix );
// Ensure p and q start out as integers.
if ( pnRadixn->exp < 0 ) { qnRadixn->exp -= pnRadixn->exp; pnRadixn->exp = 0; }
createrat(pout);
// There is probably a better way to do this.
pout->pp = numtonRadixx( pnRadixn, nRadix, ratio ); pout->pq = numtonRadixx( qnRadixn, nRadix, ratio );
destroynum( pnRadixn ); destroynum( qnRadixn );
return( pout ); }
//----------------------------------------------------------------------------
//
// FUNCTION: nRadixxtonum
//
// ARGUMENTS: pointer to a number, base requested.
//
// RETURN: number representation in nRadix 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, IN unsigned long nRadix )
{ PNUMBER sum=NULL; PNUMBER powofnRadix=NULL; unsigned long bitmask; unsigned long cdigits; MANTTYPE *ptr;
sum = longtonum( 0, nRadix ); powofnRadix = longtonum( BASEX, nRadix );
// 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 = maxout + 1; if ( cdigits > (unsigned long)a->cdigit ) { cdigits = (unsigned long)a->cdigit; }
// scale by the internal base to the internal exponent offset of the LSD
numpowlong( &powofnRadix, a->exp + (a->cdigit - cdigits), nRadix );
// Loop over all the relative digits from MSD to LSD
for ( ptr = &(MANT(a)[a->cdigit-1]); cdigits > 0 && !fhalt; ptr--, cdigits-- ) { // Loop over all the bits from MSB to LSB
for ( bitmask = BASEX/2; bitmask > 0; bitmask /= 2 ) { addnum( &sum, sum, nRadix ); if ( *ptr & bitmask ) { sum->mant[0] |= 1; } } }
// Scale answer by power of internal exponent.
mulnum( &sum, powofnRadix, nRadix );
destroynum( powofnRadix ); sum->sign = a->sign; return( sum ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: numtonRadixx
//
// ARGUMENTS: pointer to a number, nRadix of that number.
// previously calculated ratio
//
// RETURN: number representation in internal nRadix.
//
// DESCRIPTION: Does a nRadix conversion on a number from
// specified nRadix to requested nRadix. Assumes the nRadix
// specified is the nRadix of the number passed in.
//
//-----------------------------------------------------------------------------
PNUMBER numtonRadixx( IN PNUMBER a, IN unsigned long nRadix, IN long ratio )
{ PNUMBER pnumret = NULL; // pnumret is the number in internal form.
PNUMBER thisdigit = NULL; // thisdigit holds the current digit of a
// being summed into result.
PNUMBER powofnRadix = NULL; // offset of external base exponent.
MANTTYPE *ptrdigit; // pointer to digit being worked on.
long idigit; // idigit is the iterate of digits in a.
pnumret = longtonum( 0, BASEX );
ptrdigit = MANT(a);
// Digits are in reverse order, back over them LSD first.
ptrdigit += a->cdigit-1;
for ( idigit = 0; idigit < a->cdigit; idigit++ ) { mulnumx( &pnumret, num_nRadix ); // 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 = longtonum( *ptrdigit--, BASEX ); addnum( &pnumret, thisdigit, BASEX ); destroynum( thisdigit ); } DUPNUM( powofnRadix, num_nRadix );
// Calculate the exponent of the external base for scaling.
numpowlongx( &powofnRadix, a->exp );
// ... and scale the result.
mulnumx( &pnumret, powofnRadix );
destroynum( powofnRadix );
// And propagate the sign.
pnumret->sign = a->sign;
return( pnumret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: inrat
//
// ARGUMENTS:
// fMantIsNeg true if mantissa is less than zero
// pszMant a string representation of a number
// fExpIsNeg true if exponent is less than zero
// pszExp a string representation of a number
//
// RETURN: prat representation of string input.
// Or NULL if no number scanned.
//
// EXPLANATION: This is for calc.
//
//
//-----------------------------------------------------------------------------
PRAT inrat( IN BOOL fMantIsNeg, IN LPTSTR pszMant, IN BOOL fExpIsNeg, IN LPTSTR pszExp )
{ PNUMBER pnummant=NULL; // holds mantissa in number form.
PNUMBER pnumexp=NULL; // holds exponent in number form.
PRAT pratexp=NULL; // holds exponent in rational form.
PRAT prat=NULL; // holds exponent in rational form.
long expt; // holds exponent
// Deal with Mantissa
if ( ( pszMant == NULL ) || ( *pszMant == TEXT('\0') ) ) { // Preset value if no mantissa
if ( ( pszExp == NULL ) || ( *pszExp == TEXT('\0') ) ) { // Exponent not specified, preset value to zero
DUPRAT(prat,rat_zero); } else { // Exponent specified, preset value to one
DUPRAT(prat,rat_one); } } else { // Mantissa specified, convert to number form.
pnummant = innum( pszMant ); if ( pnummant == NULL ) { return( NULL ); } prat = numtorat( pnummant, nRadix ); // convert to rational form, and cleanup.
destroynum(pnummant); }
if ( ( pszExp == NULL ) || ( *pszExp == TEXT('\0') ) ) { // Exponent not specified, preset value to zero
expt=0; } else { // Exponent specified, convert to number form.
// Don't use native stuff, as it is restricted in the bases it can
// handle.
pnumexp = innum( pszExp ); if ( pnumexp == NULL ) { return( NULL ); }
// Convert exponent number form to native integral form, and cleanup.
expt = numtolong( pnumexp, nRadix ); destroynum( pnumexp ); }
// Convert native integral exponent form to rational multiplier form.
pnumexp=longtonum( nRadix, BASEX ); numpowlongx(&(pnumexp),abs(expt)); createrat(pratexp); DUPNUM( pratexp->pp, pnumexp ); pratexp->pq = longtonum( 1, BASEX ); destroynum(pnumexp);
if ( fExpIsNeg ) { // multiplier is less than 1, this means divide.
divrat( &prat, pratexp ); } else { if ( expt > 0 ) { // multiplier is greater than 1, this means divide.
mulrat(&prat, pratexp); } // multiplier can be 1, in which case it'd be a waste of time to
// multiply.
}
if ( fMantIsNeg ) { // A negative number was used, adjust the sign.
prat->pp->sign *= -1; } return( prat ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: innum
//
// ARGUMENTS:
// TCHAR *buffer
//
// RETURN: pnumber representation of string input.
// Or NULL 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 nRadix 10, ^ for all other nRadixs.
//
//-----------------------------------------------------------------------------
#define DP 0
#define ZR 1
#define NZ 2
#define SG 3
#define EX 4
#define START 0
#define MANTS 1
#define LZ 2
#define LZDP 3
#define LD 4
#define DZ 5
#define DD 6
#define DDP 7
#define EXPB 8
#define EXPS 9
#define EXPD 10
#define EXPBZ 11
#define EXPSZ 12
#define EXPDZ 13
#define 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]
char 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 } };
PNUMBER innum( IN TCHAR *buffer )
{ int c; // c is character being worked on currently.
int state; // state is the state of the input state machine.
long exps = 1L; // exps is exponent sign ( +/- 1 )
long expt = 0L; // expt is exponent mantissa, should be unsigned
long length = 0L; // length is the length of the input string.
MANTTYPE *pmant; //
PNUMBER pnumret=NULL; //
length = _tcslen(buffer); createnum( pnumret, length ); pnumret->sign = 1L; pnumret->cdigit = 0; pnumret->exp = 0; pmant = MANT(pnumret)+length-1; state = START; fparserror=FALSE; // clear global flag for parse error initially.
while ( ( c = *buffer ) && c != TEXT('\n') ) { int dp; dp = 0; // Added code to deal with international decimal point.
while ( szDec[dp] && ( szDec[dp] == *buffer ) ) { dp++; buffer++; } if ( dp ) { if ( szDec[dp] == TEXT('\0') ) { // OK pretend that was a decimal point for the state machine
c = TEXT('.'); buffer--; } else { // Backup that was no decimal point
buffer -= (dp-1); c = *buffer++; } } switch ( c ) { case TEXT('-'): case TEXT('+'): state=machine[state][SG]; break; case TEXT('.'): state=machine[state][DP]; break; case TEXT('0'): state=machine[state][ZR]; break; case TEXT('^'): case TEXT('e'): if ( ( c == TEXT('^') ) || ( nRadix == 10 ) ) { state=machine[state][EX]; break; } // WARNING tricky dropthrough in the TEXT('e') as a digit case!!!
default: state=machine[state][NZ]; break; } switch ( state ) { case MANTS: pnumret->sign = ( ( c == TEXT('-') ) ? -1 : 1); break; case EXPSZ: case EXPS: exps = ( ( c == TEXT('-') ) ? -1 : 1); break; case EXPDZ: case EXPD: { TCHAR *ptr; // offset into digit table.
if ( ( nRadix <= 36 ) && ( nRadix > 10 ) ) { c = toupper( c ); } ptr = _tcschr( digits, (TCHAR)c ); if ( ptr != NULL ) { expt *= nRadix; expt += (long)(ptr - digits); } else { state=ERR; } } break; case LD: pnumret->exp++; case DD: { TCHAR *ptr; // offset into digit table.
if ( ( nRadix <= 36 ) && ( nRadix > 10 ) ) { // Allow upper and lower case letters as equivalent, base
// is in the range where this is not ambiguous.
c = toupper( c ); } ptr = _tcschr( digits, (TCHAR)c ); if ( ptr != NULL && ( (ptr - digits) < nRadix ) ) { *pmant-- = (MANTTYPE)(ptr - digits); pnumret->exp--; pnumret->cdigit++; } else { state=ERR; // set global flag for parse error just in case anyone cares.
fparserror=TRUE; } } break; case DZ: pnumret->exp--; break; case LZ: case LZDP: case DDP: break; } buffer++; } if ( state == DZ || state == EXPDZ ) { pnumret->cdigit = 1; pnumret->exp=0; pnumret->sign=1; } else { while ( pnumret->cdigit < length ) { pnumret->cdigit++; pnumret->exp--; } pnumret->exp += exps*expt; }
if ( pnumret->cdigit == 0 ) { destroynum( pnumret ); pnumret = NULL; } stripzeroesnum( pnumret, maxout ); return( pnumret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: longtorat
//
// ARGUMENTS: long
//
// RETURN: Rational representation of long input.
//
// DESCRIPTION: Converts long input to rational (p over q)
// form, where q is 1 and p is the long.
//
//-----------------------------------------------------------------------------
PRAT longtorat( IN long inlong )
{ PRAT pratret=NULL; createrat( pratret ); pratret->pp = longtonum(inlong, BASEX ); pratret->pq = longtonum(1L, BASEX ); return( pratret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: realtorat
//
// ARGUMENTS: double real value.
//
// RETURN: Rational representation of the double
//
// DESCRIPTION: returns the rational (p over q)
// representation of the double.
//
//-----------------------------------------------------------------------------
PRAT realtorat( IN double real )
{ #if !defined( CLEVER )
// get clever later, right now hack something to work
TCHAR *ptr; PNUMBER pnum=NULL; PRAT prat=NULL; if ( ( ptr = (TCHAR*)zmalloc( 60 * sizeof(TCHAR) ) ) != NULL ) { _stprintf( ptr, TEXT("%20.20le"), real ); pnum=innum( ptr ); prat = numtorat( pnum, nRadix ); destroynum( pnum ); zfree( ptr ); return( prat ); } else { return( NULL ); } #else
int i; union { double real; BYTE split[8]; } unpack; long expt; long ratio; MANTTYPE *pmant; PNUMBER pnumret = NULL; PRAT pratret = NULL;
createrat( pratret );
if ( real == 0.0 ) { pnumret=longtonum( 0L, 2L ); } else { unpack.real=real;
expt=unpack.split[7]*0x100+(unpack.split[6]>>4)-1023; createnum( pnumret, 52 ); pmant = MANT(pnumret); for ( i = 63; i > 10; i-- ) { *pmant++ = (MANTTYPE)((unpack.split[i/8]&(1<<(i%8)))!=0); } pnumret->exp=expt-52; pnumret->cdigit=52; }
ratio = 1; while ( ratio > BASEX ) { ratio *= 2; }
pratret->pp = numtonRadixx( pnumret, 2, ratio ); destroynum( pnumret );
pratret->pq=longtonum( 1L, BASEX );
if ( pratret->pp->exp < 0 ) { pratret->pq->exp -= pratret->pp->exp; pratret->pp->exp = 0; }
return( pratret ); #endif
}
//-----------------------------------------------------------------------------
//
// FUNCTION: longtonum
//
// ARGUMENTS: long input and nRadix requested.
//
// RETURN: number
//
// DESCRIPTION: Returns a number representation in the
// base requested of the long value passed in.
//
//-----------------------------------------------------------------------------
PNUMBER longtonum( IN long inlong, IN unsigned long nRadix )
{ MANTTYPE *pmant; PNUMBER pnumret=NULL;
createnum( pnumret, MAX_LONG_SIZE ); pmant = MANT(pnumret); pnumret->cdigit = 0; pnumret->exp = 0; if ( inlong < 0 ) { pnumret->sign = -1; inlong *= -1; } else { pnumret->sign = 1; }
do { *pmant++ = (MANTTYPE)(inlong % nRadix); inlong /= nRadix; pnumret->cdigit++; } while ( inlong );
return( pnumret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: rattolong
//
// ARGUMENTS: rational number in internal base.
//
// RETURN: long
//
// DESCRIPTION: returns the long representation of the
// number input. Assumes that the number is in the internal
// base.
//
//-----------------------------------------------------------------------------
long rattolong( IN PRAT prat )
{ long lret; PRAT pint = NULL;
if ( rat_gt( prat, rat_dword ) || rat_lt( prat, rat_min_long ) ) { // Don't attempt rattolong of anything too big or small
throw( CALC_E_DOMAIN ); }
DUPRAT(pint,prat);
intrat( &pint ); divnumx( &(pint->pp), pint->pq ); DUPNUM( pint->pq, num_one );
lret = numtolong( pint->pp, BASEX );
destroyrat(pint);
return( lret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: numtolong
//
// ARGUMENTS: number input and base of that number.
//
// RETURN: long
//
// DESCRIPTION: returns the long representation of the
// number input. Assumes that the number is really in the
// base claimed.
//
//-----------------------------------------------------------------------------
long numtolong( IN PNUMBER pnum, IN unsigned long nRadix )
{ long lret; long expt; long length; MANTTYPE *pmant;
lret = 0; pmant = MANT( pnum ); pmant += pnum->cdigit - 1;
expt = pnum->exp; length = pnum->cdigit; while ( length > 0 && length + expt > 0 ) { lret *= nRadix; lret += *(pmant--); length--; } while ( expt-- > 0 ) { lret *= (long)nRadix; } 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 zeroes.
//
//-----------------------------------------------------------------------------
BOOL stripzeroesnum( IN OUT PNUMBER pnum, long starting )
{ MANTTYPE *pmant; long cdigits; BOOL fstrip = FALSE;
// point pmant to the LeastCalculatedDigit
pmant=MANT(pnum); 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 zeroes.
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 zeroes to remove.
if ( fstrip ) { // Remove them.
memcpy( MANT(pnum), pmant, (int)(cdigits*sizeof(MANTTYPE)) ); // And adjust exponent and digit count accordingly.
pnum->exp += ( pnum->cdigit - cdigits ); pnum->cdigit = cdigits; } return( fstrip ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: putnum
//
// ARGUMENTS: number representation
// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or
// FMT_ENGINEERING
//
// RETURN: String representation of number.
//
// DESCRIPTION: Converts a number to it's string
// representation. Returns a string that should be
// zfree'd after use.
//
//-----------------------------------------------------------------------------
TCHAR *putnum( IN PNUMBER *ppnum, IN int fmt )
{ TCHAR *psz; TCHAR *pret; long expt; // Actual number of digits to the left of decimal
long eout; // Displayed exponent.
long cexp; // the size of the exponent needed.
long elen; long length; MANTTYPE *pmant; int fsciform=0; // If true scientific form is called for.
PNUMBER pnum; PNUMBER round=NULL; long oldfmt = fmt;
pnum=*ppnum; stripzeroesnum( pnum, maxout+2 ); length = pnum->cdigit; expt = pnum->exp+length; if ( ( expt > maxout ) && ( fmt == FMT_FLOAT ) ) { // Force scientific mode to prevent user from assuming 33rd digit is
// exact.
fmt = FMT_SCIENTIFIC; }
// Make length small enough to fit in pret.
if ( length > maxout ) { length = maxout; }
eout=expt-1; cexp = longlognRadix( expt );
// 2 for signs, 1 for 'e'(or leading zero), 1 for dp, 1 for null and
// 10 for maximum exponent size.
pret = (TCHAR*)zmalloc( (maxout + 16) * sizeof(TCHAR) ); psz = pret;
if (!psz) { fail( CALC_E_OUTOFMEMORY ); }
// If there is a chance a round has to occour, round.
if ( // if number is zero no rounding.
!zernum( pnum ) && // if number of digits is less than the maximum output no rounding.
pnum->cdigit >= maxout ) { // Otherwise round.
round=longtonum( nRadix, nRadix ); divnum(&round, num_two, nRadix );
// Make round number exponent one below the LSD for the number.
round->exp = pnum->exp + pnum->cdigit - round->cdigit - maxout; round->sign = pnum->sign; }
if ( fmt == FMT_FLOAT ) { // cexp will now contain the size required by exponential.
// Figure out if the exponent will fill more space than the nonexponent field.
if ( ( length - expt > maxout + 2 ) || ( expt > maxout + 3 ) ) { // Case where too many zeroes are to the right or left of the
// decimal pt. And we are forced to switch to scientific form.
fmt = FMT_SCIENTIFIC; } else { // Minimum loss of precision occours with listing leading zeros
// if we need to make room for zeroes sacrifice some digits.
if ( length + abs(expt) < maxout ) { if ( round ) { round->exp -= expt; } } } } if ( round != NULL ) { BOOL fstrip=FALSE; long offset; addnum( ppnum, round, nRadix ); pnum=*ppnum; offset=(pnum->cdigit+pnum->exp) - (round->cdigit+round->exp); fstrip = stripzeroesnum( pnum, offset ); destroynum( round ); if ( fstrip ) { // WARNING: nesting/recursion, too much has been changed, need to
// refigure format.
return( putnum( &pnum, oldfmt ) ); } } else { stripzeroesnum( pnum, maxout ); }
// Set up all the post rounding stuff.
pmant = MANT(pnum)+pnum->cdigit-1;
if ( // Case where too many digits are to the left of the decimal or
// FMT_SCIENTIFIC or FMT_ENGINEERING was specified.
( fmt == FMT_SCIENTIFIC ) || ( fmt == FMT_ENGINEERING ) )
{ fsciform=1; if ( eout != 0 ) {
if ( fmt == FMT_ENGINEERING ) { expt = (eout % 3); eout -= expt; expt++;
// Fix the case where 0.02e-3 should really be 2.e-6 etc.
if ( expt < 0 ) { expt += 3; eout -= 3; }
} else { expt = 1; } } } else { fsciform=0; eout=0; }
// Make sure negative zeroes aren't allowed.
if ( ( pnum->sign == -1 ) && ( length > 0 ) ) { *psz++ = TEXT('-'); }
if ( ( expt <= 0 ) && ( fsciform == 0 ) ) { *psz++ = TEXT('0'); *psz++ = szDec[0]; // Used up a digit unaccounted for.
} while ( expt < 0 ) { *psz++ = TEXT('0'); expt++; }
while ( length > 0 ) { expt--; *psz++ = digits[ *pmant-- ]; length--; // Be more regular in using a decimal point.
if ( expt == 0 ) { *psz++ = szDec[0]; } }
while ( expt > 0 ) { *psz++ = TEXT('0'); expt--; // Be more regular in using a decimal point.
if ( expt == 0 ) { *psz++ = szDec[0]; } }
if ( fsciform ) { if ( nRadix == 10 ) { *psz++ = TEXT('e'); } else { *psz++ = TEXT('^'); } *psz++ = ( eout < 0 ? TEXT('-') : TEXT('+') ); eout = abs( eout ); elen=0; do { // should this be eout % nRadix? or is that insane?
*psz++ = digits[ eout % nRadix ]; elen++; eout /= nRadix; } while ( eout > 0 ); *psz = TEXT('\0'); _tcsrev( &(psz[-elen]) ); } *psz = TEXT('\0'); return( pret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: putrat
//
// ARGUMENTS:
// PRAT *representation of a number.
// long representation of base to dump to screen.
// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or FMT_ENGINEERING
//
// RETURN: string
//
// DESCRIPTION: returns a string representation of rational number passed
// in, at least to the maxout digits. String returned should be zfree'd
// after use.
//
// 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.
//
//-----------------------------------------------------------------------------
TCHAR *putrat( IN OUT PRAT *pa, IN unsigned long nRadix, IN int fmt )
{ TCHAR *psz; PNUMBER p=NULL; PNUMBER q=NULL; long scaleby=0;
// Convert p and q of rational form from internal base to requested base.
// Scale by largest power of BASEX possible.
scaleby=min((*pa)->pp->exp,(*pa)->pq->exp); if ( scaleby < 0 ) { scaleby = 0; } (*pa)->pp->exp -= scaleby; (*pa)->pq->exp -= scaleby;
p = nRadixxtonum( (*pa)->pp, nRadix );
q = nRadixxtonum( (*pa)->pq, nRadix );
// finally take the time hit to actually divide.
divnum( &p, q, nRadix );
psz = putnum( &p, fmt ); destroynum( p ); destroynum( q ); return( psz ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: gcd
//
// ARGUMENTS:
// PNUMBER representation of a number.
// PNUMBER representation of a number.
//
// 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 GregSte and TimC proved 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=NULL; PNUMBER tmpa=NULL; PNUMBER tmpb=NULL;
if ( lessnum( a, b ) ) { DUPNUM(tmpa,b); if ( zernum(a) ) { return(tmpa); } DUPNUM(tmpb,a); } else { DUPNUM(tmpa,a); if ( zernum(b) ) { return(tmpa); } DUPNUM(tmpb,b); }
remnum( &tmpa, tmpb, nRadix ); while ( !zernum( tmpa ) ) { // swap tmpa and tmpb
r = tmpa; tmpa = tmpb; tmpb = r; remnum( &tmpa, tmpb, nRadix ); } destroynum( tmpa ); return( tmpb );
}
//-----------------------------------------------------------------------------
//
// FUNCTION: longfactnum
//
// ARGUMENTS:
// long integer to factorialize.
// long integer representing base of answer.
//
// RETURN: Factorial of input in nRadix PNUMBER form.
//
// NOTE: Not currently used.
//
//-----------------------------------------------------------------------------
PNUMBER longfactnum( IN long inlong, IN unsigned long nRadix )
{ PNUMBER lret=NULL; PNUMBER tmp=NULL; PNUMBER tmp1=NULL;
lret = longtonum( 1, nRadix );
while ( inlong > 0 ) { tmp = longtonum( inlong--, nRadix ); mulnum( &lret, tmp, nRadix ); destroynum( tmp ); } return( lret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: longprodnum
//
// ARGUMENTS:
// long integer to factorialize.
// long integer representing base of answer.
//
// RETURN: Factorial of input in base PNUMBER form.
//
//-----------------------------------------------------------------------------
PNUMBER longprodnum( IN long start, IN long stop, IN unsigned long nRadix )
{ PNUMBER lret=NULL; PNUMBER tmp=NULL;
lret = longtonum( 1, nRadix );
while ( start <= stop ) { if ( start ) { tmp = longtonum( start, nRadix ); mulnum( &lret, tmp, nRadix ); destroynum( tmp ); } start++; } return( lret ); }
//-----------------------------------------------------------------------------
//
// FUNCTION: numpowlong
//
// ARGUMENTS: root as number power as long and nRadix of
// number.
//
// RETURN: None root is changed.
//
// DESCRIPTION: changes numeric representation of root to
// root ** power. Assumes nRadix is the nRadix of root.
//
//-----------------------------------------------------------------------------
void numpowlong( IN OUT PNUMBER *proot, IN long power, IN unsigned long nRadix )
{ PNUMBER lret=NULL;
lret = longtonum( 1, nRadix );
while ( power > 0 ) { if ( power & 1 ) { mulnum( &lret, *proot, nRadix ); } mulnum( proot, *proot, nRadix ); TRIMNUM(*proot); power >>= 1; } destroynum( *proot ); *proot=lret;
}
//-----------------------------------------------------------------------------
//
// FUNCTION: ratpowlong
//
// ARGUMENTS: root as rational, power as long.
//
// RETURN: None root is changed.
//
// DESCRIPTION: changes rational representation of root to
// root ** power.
//
//-----------------------------------------------------------------------------
void ratpowlong( IN OUT PRAT *proot, IN long power )
{ if ( power < 0 ) { // Take the positive power and invert answer.
PNUMBER pnumtemp = NULL; ratpowlong( proot, -power ); pnumtemp = (*proot)->pp; (*proot)->pp = (*proot)->pq; (*proot)->pq = pnumtemp; } else { PRAT lret=NULL;
lret = longtorat( 1 );
while ( power > 0 ) { if ( power & 1 ) { mulnumx( &(lret->pp), (*proot)->pp ); mulnumx( &(lret->pq), (*proot)->pq ); } mulrat( proot, *proot ); trimit(&lret); trimit(proot); power >>= 1; } destroyrat( *proot ); *proot=lret; } }
//-----------------------------------------------------------------------------
//
// FUNCTION: longlog10
//
// ARGUMENTS: number as long.
//
// RETURN: returns int(log10(abs(number)+1)), useful in formatting output
//
//-----------------------------------------------------------------------------
long longlognRadix( long x )
{ long ret = 0; x--; if ( x < 0 ) { x = -x; } while ( x ) { ret++; x /= nRadix; } return( ret ); }
|