// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. //----------------------------------------------------------------------------- // Package Title ratpak // File basex.c // Copyright (C) 1995-97 Microsoft // Date 03-14-97 // // // Description // // Contains number routines for internal base computations, these assume // internal base is a power of 2. // //----------------------------------------------------------------------------- #include "pch.h" #include "ratpak.h" void _mulnumx( PNUMBER *pa, PNUMBER b ); //---------------------------------------------------------------------------- // // FUNCTION: mulnumx // // ARGUMENTS: pointer to a number and a second number, the // base is always BASEX. // // RETURN: None, changes first pointer. // // DESCRIPTION: Does the number equivalent of *pa *= b. // This is a stub which prevents multiplication by 1, this is a big speed // improvement. // //---------------------------------------------------------------------------- void __inline mulnumx( PNUMBER *pa, PNUMBER b ) { if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) { // If b is not one we multiply if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 ) { // pa and b are both non-one. _mulnumx( pa, b ); } else { // if pa is one and b isn't just copy b. and adjust the sign. long sign = (*pa)->sign; DUPNUM(*pa,b); (*pa)->sign *= sign; } } else { // B is +/- 1, But we do have to set the sign. (*pa)->sign *= b->sign; } } //---------------------------------------------------------------------------- // // FUNCTION: _mulnumx // // ARGUMENTS: pointer to a number and a second number, the // base is always BASEX. // // RETURN: None, changes first pointer. // // DESCRIPTION: Does the number equivalent of *pa *= b. // Assumes the base is BASEX of both numbers. This algorithm is the // same one you learned in grade school, except the base isn't 10 it's // BASEX. // //---------------------------------------------------------------------------- void _mulnumx( PNUMBER *pa, PNUMBER b ) { PNUMBER c= nullptr; // c will contain the result. PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa MANTTYPE *ptra; // ptra is a pointer to the mantissa of a. MANTTYPE *ptrb; // ptrb is a pointer to the mantissa of b. MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c. MANTTYPE *ptrcoffset; // ptrcoffset, is the anchor location of the next // single digit multiply partial result. long iadigit=0; // Index of digit being used in the first number. long ibdigit=0; // Index of digit being used in the second number. MANTTYPE da=0; // da is the digit from the fist number. TWO_MANTTYPE cy=0; // cy is the carry resulting from the addition of // a multiplied row into the result. TWO_MANTTYPE mcy=0; // mcy is the resultant from a single // multiply, AND the carry of that multiply. long icdigit=0; // Index of digit being calculated in final result. a=*pa; ibdigit = a->cdigit + b->cdigit - 1; createnum( c, ibdigit + 1 ); c->cdigit = ibdigit; c->sign = a->sign * b->sign; c->exp = a->exp + b->exp; ptra = a->mant; ptrcoffset = c->mant; for ( iadigit = a->cdigit; iadigit > 0; iadigit-- ) { da = *ptra++; ptrb = b->mant; // Shift ptrc, and ptrcoffset, one for each digit ptrc = ptrcoffset++; for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- ) { cy = 0; mcy = (DWORDLONG)da * (*ptrb); if ( mcy ) { icdigit = 0; if ( ibdigit == 1 && iadigit == 1 ) { c->cdigit++; } } // If result is nonzero, or while result of carry is nonzero... while ( mcy || cy ) { // update carry from addition(s) and multiply. cy += (TWO_MANTTYPE)ptrc[icdigit]+((DWORD)mcy&((DWORD)~BASEX)); // update result digit from ptrc[icdigit++]=(MANTTYPE)((DWORD)cy&((DWORD)~BASEX)); // update carries from mcy >>= BASEXPWR; cy >>= BASEXPWR; } ptrb++; ptrc++; } } // prevent different kinds of zeros, by stripping leading duplicate zeros. // digits are in order of increasing significance. while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 ) { c->cdigit--; } destroynum( *pa ); *pa=c; } //----------------------------------------------------------------------------- // // FUNCTION: numpowlongx // // ARGUMENTS: root as number power as long // number. // // RETURN: None root is changed. // // DESCRIPTION: changes numeric representation of root to // root ** power. Assumes base BASEX // decomposes the exponent into it's sums of powers of 2, so on average // it will take n+n/2 multiplies where n is the highest on bit. // //----------------------------------------------------------------------------- void numpowlongx( _Inout_ PNUMBER *proot, _In_ long power ) { PNUMBER lret = longtonum( 1, BASEX ); // Once the power remaining is zero we are done. while ( power > 0 ) { // If this bit in the power decomposition is on, multiply the result // by the root number. if ( power & 1 ) { mulnumx( &lret, *proot ); } // multiply the root number by itself to scale for the next bit (i.e. // square it. mulnumx( proot, *proot ); // move the next bit of the power into place. power >>= 1; } destroynum( *proot ); *proot=lret; } void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision); //---------------------------------------------------------------------------- // // FUNCTION: divnumx // // ARGUMENTS: pointer to a number, a second number and precision. // // RETURN: None, changes first pointer. // // DESCRIPTION: Does the number equivalent of *pa /= b. // Assumes radix is the internal radix representation. // This is a stub which prevents division by 1, this is a big speed // improvement. // //---------------------------------------------------------------------------- void __inline divnumx( PNUMBER *pa, PNUMBER b, int32_t precision) { if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 ) { // b is not one. if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 ) { // pa and b are both not one. _divnumx( pa, b, precision); } else { // if pa is one and b is not one, just copy b, and adjust the sign. long sign = (*pa)->sign; DUPNUM(*pa,b); (*pa)->sign *= sign; } } else { // b is one so don't divide, but set the sign. (*pa)->sign *= b->sign; } } //---------------------------------------------------------------------------- // // FUNCTION: _divnumx // // ARGUMENTS: pointer to a number, a second number and precision. // // RETURN: None, changes first pointer. // // DESCRIPTION: Does the number equivalent of *pa /= b. // Assumes radix is the internal radix representation. // //---------------------------------------------------------------------------- void _divnumx( PNUMBER *pa, PNUMBER b, int32_t precision) { PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa PNUMBER c= nullptr; // c will contain the result. PNUMBER lasttmp = nullptr; // lasttmp allows a backup when the algorithm // guesses one bit too far. PNUMBER tmp = nullptr; // current guess being worked on for divide. PNUMBER rem = nullptr; // remainder after applying guess. long cdigits; // count of digits for answer. MANTTYPE *ptrc; // ptrc is a pointer to the mantissa of c. long thismax = precision + g_ratio; // set a maximum number of internal digits // to shoot for in the divide. a=*pa; if ( thismax < a->cdigit ) { // a has more digits than precision specified, bump up digits to shoot // for. thismax = a->cdigit; } if ( thismax < b->cdigit ) { // b has more digits than precision specified, bump up digits to shoot // for. thismax = b->cdigit; } // Create c (the divide answer) and set up exponent and sign. createnum( c, thismax + 1 ); c->exp = (a->cdigit+a->exp) - (b->cdigit+b->exp) + 1; c->sign = a->sign * b->sign; ptrc = c->mant + thismax; cdigits = 0; DUPNUM( rem, a ); rem->sign = b->sign; rem->exp = b->cdigit + b->exp - rem->cdigit; while ( cdigits++ < thismax && !zernum(rem) ) { long digit = 0; *ptrc = 0; while ( !lessnum( rem, b ) ) { digit = 1; DUPNUM( tmp, b ); destroynum( lasttmp ); lasttmp=longtonum( 0, BASEX ); while ( lessnum( tmp, rem ) ) { destroynum( lasttmp ); DUPNUM(lasttmp,tmp); addnum( &tmp, tmp, BASEX ); digit *= 2; } if ( lessnum( rem, tmp ) ) { // too far, back up... destroynum( tmp ); digit /= 2; tmp=lasttmp; lasttmp= nullptr; } tmp->sign *= -1; addnum( &rem, tmp, BASEX ); destroynum( tmp ); destroynum( lasttmp ); *ptrc |= digit; } rem->exp++; ptrc--; } cdigits--; if ( c->mant != ++ptrc ) { memmove( c->mant, ptrc, (int)(cdigits*sizeof(MANTTYPE)) ); } if ( !cdigits ) { // A zero, make sure no weird exponents creep in c->exp = 0; c->cdigit = 1; } else { c->cdigit = cdigits; c->exp -= cdigits; // prevent different kinds of zeros, by stripping leading duplicate // zeros. digits are in order of increasing significance. while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 ) { c->cdigit--; } } destroynum( rem ); destroynum( *pa ); *pa=c; }