656 lines
19 KiB
C++
656 lines
19 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Package Title ratpak
|
|
// File num.c
|
|
// Copyright (C) 1995-97 Microsoft
|
|
// Date 01-16-95
|
|
//
|
|
//
|
|
// Description
|
|
//
|
|
// Contains number routines for add, mul, div, rem and other support
|
|
// and longs.
|
|
//
|
|
// Special Information
|
|
//
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
#include "pch.h"
|
|
#include "ratpak.h"
|
|
|
|
using namespace std;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: addnum
|
|
//
|
|
// ARGUMENTS: pointer to a number a second number, and the
|
|
// radix.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of *pa += b.
|
|
// Assumes radix is the base of both numbers.
|
|
//
|
|
// ALGORITHM: Adds each digit from least significant to most
|
|
// significant.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix);
|
|
|
|
void __inline addnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
|
|
|
|
{
|
|
if ( b->cdigit > 1 || b->mant[0] != 0 )
|
|
{ // If b is zero we are done.
|
|
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 0 )
|
|
{ // pa and b are both nonzero.
|
|
_addnum( pa, b, radix);
|
|
}
|
|
else
|
|
{ // if pa is zero and b isn't just copy b.
|
|
DUPNUM(*pa,b);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _addnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
|
|
|
|
{
|
|
PNUMBER c= nullptr; // c will contain the result.
|
|
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
|
|
MANTTYPE *pcha; // pcha is a pointer to the mantissa of a.
|
|
MANTTYPE *pchb; // pchb is a pointer to the mantissa of b.
|
|
MANTTYPE *pchc; // pchc is a pointer to the mantissa of c.
|
|
long cdigits; // cdigits is the max count of the digits results
|
|
// used as a counter.
|
|
long mexp; // mexp is the exponent of the result.
|
|
MANTTYPE da; // da is a single 'digit' after possible padding.
|
|
MANTTYPE db; // db is a single 'digit' after possible padding.
|
|
MANTTYPE cy=0; // cy is the value of a carry after adding two 'digits'
|
|
long fcompla = 0; // fcompla is a flag to signal a is negative.
|
|
long fcomplb = 0; // fcomplb is a flag to signal b is negative.
|
|
|
|
a=*pa;
|
|
|
|
|
|
// Calculate the overlap of the numbers after alignment, this includes
|
|
// necessary padding 0's
|
|
cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
|
|
min( a->exp, b->exp );
|
|
|
|
createnum( c, cdigits + 1 );
|
|
c->exp = min( a->exp, b->exp );
|
|
mexp = c->exp;
|
|
c->cdigit = cdigits;
|
|
pcha = a->mant;
|
|
pchb = b->mant;
|
|
pchc = c->mant;
|
|
|
|
// Figure out the sign of the numbers
|
|
if ( a->sign != b->sign )
|
|
{
|
|
cy = 1;
|
|
fcompla = ( a->sign == -1 );
|
|
fcomplb = ( b->sign == -1 );
|
|
}
|
|
|
|
// Loop over all the digits, real and 0 padded. Here we know a and b are
|
|
// aligned
|
|
for ( ;cdigits > 0; cdigits--, mexp++ )
|
|
{
|
|
|
|
// Get digit from a, taking padding into account.
|
|
da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp >
|
|
(c->cdigit - a->cdigit) ) ) ?
|
|
*pcha++ : 0 );
|
|
// Get digit from b, taking padding into account.
|
|
db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp >
|
|
(c->cdigit - b->cdigit) ) ) ?
|
|
*pchb++ : 0 );
|
|
|
|
// Handle complementing for a and b digit. Might be a better way, but
|
|
// haven't found it yet.
|
|
if ( fcompla )
|
|
{
|
|
da = (MANTTYPE)(radix) - 1 - da;
|
|
}
|
|
if ( fcomplb )
|
|
{
|
|
db = (MANTTYPE)(radix) - 1 - db;
|
|
}
|
|
|
|
// Update carry as necessary
|
|
cy = da + db + cy;
|
|
*pchc++ = (MANTTYPE)(cy % (MANTTYPE)radix);
|
|
cy /= (MANTTYPE)radix;
|
|
}
|
|
|
|
// Handle carry from last sum as extra digit
|
|
if ( cy && !(fcompla || fcomplb) )
|
|
{
|
|
*pchc++ = cy;
|
|
c->cdigit++;
|
|
}
|
|
|
|
// Compute sign of result
|
|
if ( !(fcompla || fcomplb) )
|
|
{
|
|
c->sign = a->sign;
|
|
}
|
|
else
|
|
{
|
|
if ( cy )
|
|
{
|
|
c->sign = 1;
|
|
}
|
|
else
|
|
{
|
|
// In this particular case an overflow or underflow has occoured
|
|
// and all the digits need to be complemented, at one time an
|
|
// attempt to handle this above was made, it turned out to be much
|
|
// slower on average.
|
|
c->sign = -1;
|
|
cy = 1;
|
|
for ( ( cdigits = c->cdigit ), (pchc = c->mant);
|
|
cdigits > 0;
|
|
cdigits-- )
|
|
{
|
|
cy = (MANTTYPE)radix - (MANTTYPE)1 - *pchc + cy;
|
|
*pchc++ = (MANTTYPE)( cy % (MANTTYPE)radix);
|
|
cy /= (MANTTYPE)radix;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove leading zeroes, remember digits are in order of
|
|
// increasing significance. i.e. 100 would be 0,0,1
|
|
while ( c->cdigit > 1 && *(--pchc) == 0 )
|
|
{
|
|
c->cdigit--;
|
|
}
|
|
destroynum( *pa );
|
|
*pa=c;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: mulnum
|
|
//
|
|
// ARGUMENTS: pointer to a number a second number, and the
|
|
// radix.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of *pa *= b.
|
|
// Assumes radix is the radix of both numbers. This algorithm is the
|
|
// same one you learned in gradeschool.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix);
|
|
|
|
void __inline mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
|
|
|
|
{
|
|
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
|
|
{ // If b is one we don't multiply exactly.
|
|
if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 )
|
|
{ // pa and b are both nonone.
|
|
_mulnum( pa, b, radix);
|
|
}
|
|
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
|
|
{ // But we do have to set the sign.
|
|
(*pa)->sign *= b->sign;
|
|
}
|
|
}
|
|
|
|
void _mulnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
|
|
|
|
{
|
|
PNUMBER c= nullptr; // c will contain the result.
|
|
PNUMBER a= nullptr; // a is the dereferenced number pointer from *pa
|
|
MANTTYPE *pcha; // pcha is a pointer to the mantissa of a.
|
|
MANTTYPE *pchb; // pchb is a pointer to the mantissa of b.
|
|
MANTTYPE *pchc; // pchc is a pointer to the mantissa of c.
|
|
MANTTYPE *pchcoffset; // pchcoffset, 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;
|
|
pcha = a->mant;
|
|
pchcoffset = c->mant;
|
|
|
|
for ( iadigit = a->cdigit; iadigit > 0; iadigit-- )
|
|
{
|
|
da = *pcha++;
|
|
pchb = b->mant;
|
|
|
|
// Shift pchc, and pchcoffset, one for each digit
|
|
pchc = pchcoffset++;
|
|
|
|
for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- )
|
|
{
|
|
cy = 0;
|
|
mcy = (TWO_MANTTYPE)da * *pchb;
|
|
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)pchc[icdigit]+(mcy%(TWO_MANTTYPE)radix);
|
|
|
|
// update result digit from
|
|
pchc[icdigit++]=(MANTTYPE)(cy%(TWO_MANTTYPE)radix);
|
|
|
|
// update carries from
|
|
mcy /= (TWO_MANTTYPE)radix;
|
|
cy /= (TWO_MANTTYPE)radix;
|
|
}
|
|
|
|
pchb++;
|
|
pchc++;
|
|
}
|
|
}
|
|
|
|
// prevent different kinds of zeros, by stripping leading duplicate zeroes.
|
|
// digits are in order of increasing significance.
|
|
while ( c->cdigit > 1 && c->mant[c->cdigit-1] == 0 )
|
|
{
|
|
c->cdigit--;
|
|
}
|
|
|
|
destroynum( *pa );
|
|
*pa=c;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: remnum
|
|
//
|
|
// ARGUMENTS: pointer to a number a second number, and the
|
|
// radix.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of *pa %= b.
|
|
// Repeatedly subtracts off powers of 2 of b until *pa < b.
|
|
//
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void remnum( PNUMBER *pa, PNUMBER b, uint32_t radix)
|
|
|
|
{
|
|
PNUMBER tmp = nullptr; // tmp is the working remainder.
|
|
PNUMBER lasttmp = nullptr; // lasttmp is the last remainder which worked.
|
|
|
|
// Once *pa is less than b, *pa is the remainder.
|
|
while ( !lessnum( *pa, b ) )
|
|
{
|
|
DUPNUM( tmp, b );
|
|
if ( lessnum( tmp, *pa ) )
|
|
{
|
|
// Start off close to the right answer for subtraction.
|
|
tmp->exp = (*pa)->cdigit+(*pa)->exp - tmp->cdigit;
|
|
if ( MSD(*pa) <= MSD(tmp) )
|
|
{
|
|
// Don't take the chance that the numbers are equal.
|
|
tmp->exp--;
|
|
}
|
|
}
|
|
|
|
destroynum( lasttmp );
|
|
lasttmp=longtonum( 0, radix);
|
|
|
|
while ( lessnum( tmp, *pa ) )
|
|
{
|
|
DUPNUM( lasttmp, tmp );
|
|
addnum( &tmp, tmp, radix);
|
|
}
|
|
|
|
if ( lessnum( *pa, tmp ) )
|
|
{
|
|
// too far, back up...
|
|
destroynum( tmp );
|
|
tmp=lasttmp;
|
|
lasttmp= nullptr;
|
|
}
|
|
|
|
// Subtract the working remainder from the remainder holder.
|
|
tmp->sign = -1*(*pa)->sign;
|
|
addnum( pa, tmp, radix);
|
|
|
|
destroynum( tmp );
|
|
destroynum( lasttmp );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: divnum
|
|
//
|
|
// ARGUMENTS: pointer to a number a second number, and the
|
|
// radix.
|
|
//
|
|
// RETURN: None, changes first pointer.
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of *pa /= b.
|
|
// Assumes radix is the radix of both numbers.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision);
|
|
|
|
void __inline divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision)
|
|
|
|
{
|
|
if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
|
|
{
|
|
// b is not one
|
|
_divnum( pa, b, radix, precision);
|
|
}
|
|
else
|
|
{ // But we do have to set the sign.
|
|
(*pa)->sign *= b->sign;
|
|
}
|
|
}
|
|
|
|
void _divnum( PNUMBER *pa, PNUMBER b, uint32_t radix, int32_t precision)
|
|
{
|
|
PNUMBER a = *pa;
|
|
long thismax = precision + 2;
|
|
if (thismax < a->cdigit)
|
|
{
|
|
thismax = a->cdigit;
|
|
}
|
|
|
|
if (thismax < b->cdigit)
|
|
{
|
|
thismax = b->cdigit;
|
|
}
|
|
|
|
PNUMBER c = nullptr;
|
|
createnum(c, thismax + 1);
|
|
c->exp = (a->cdigit + a->exp) - (b->cdigit + b->exp) + 1;
|
|
c->sign = a->sign * b->sign;
|
|
|
|
MANTTYPE *ptrc = c->mant + thismax;
|
|
PNUMBER rem = nullptr;
|
|
PNUMBER tmp = nullptr;
|
|
DUPNUM(rem, a);
|
|
DUPNUM(tmp, b);
|
|
tmp->sign = a->sign;
|
|
rem->exp = b->cdigit + b->exp - rem->cdigit;
|
|
|
|
// Build a table of multiplications of the divisor, this is quicker for
|
|
// more than radix 'digits'
|
|
list<PNUMBER> numberList{ longtonum(0L, radix) };
|
|
for (unsigned long i = 1; i < radix; i++)
|
|
{
|
|
PNUMBER newValue = nullptr;
|
|
DUPNUM(newValue, numberList.front());
|
|
addnum(&newValue, tmp, radix);
|
|
|
|
numberList.emplace_front(newValue);
|
|
}
|
|
destroynum(tmp);
|
|
|
|
long digit;
|
|
long cdigits = 0;
|
|
while (cdigits++ < thismax && !zernum(rem))
|
|
{
|
|
digit = radix - 1;
|
|
PNUMBER multiple = nullptr;
|
|
for (const auto& num : numberList)
|
|
{
|
|
if (!lessnum(rem, num) || !--digit)
|
|
{
|
|
multiple = num;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (digit)
|
|
{
|
|
multiple->sign *= -1;
|
|
addnum(&rem, multiple, radix);
|
|
multiple->sign *= -1;
|
|
}
|
|
rem->exp++;
|
|
*ptrc-- = (MANTTYPE)digit;
|
|
}
|
|
cdigits--;
|
|
|
|
if (c->mant != ++ptrc)
|
|
{
|
|
memmove(c->mant, ptrc, (int)(cdigits * sizeof(MANTTYPE)));
|
|
}
|
|
|
|
// Cleanup table structure
|
|
for (auto& num : numberList)
|
|
{
|
|
destroynum(num);
|
|
}
|
|
|
|
if (!cdigits)
|
|
{
|
|
c->cdigit = 1;
|
|
c->exp = 0;
|
|
}
|
|
else
|
|
{
|
|
c->cdigit = cdigits;
|
|
c->exp -= cdigits;
|
|
while (c->cdigit > 1 && c->mant[c->cdigit - 1] == 0)
|
|
{
|
|
c->cdigit--;
|
|
}
|
|
}
|
|
destroynum(rem);
|
|
|
|
destroynum(*pa);
|
|
*pa = c;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: equnum
|
|
//
|
|
// ARGUMENTS: two numbers.
|
|
//
|
|
// RETURN: Boolean
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of ( a == b )
|
|
// Only assumes that a and b are the same radix.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool equnum( PNUMBER a, PNUMBER b )
|
|
|
|
{
|
|
long diff;
|
|
MANTTYPE *pa;
|
|
MANTTYPE *pb;
|
|
long cdigits;
|
|
long ccdigits;
|
|
MANTTYPE da;
|
|
MANTTYPE db;
|
|
|
|
diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
|
|
if ( diff < 0 )
|
|
{
|
|
// If the exponents are different, these are different numbers.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ( diff > 0 )
|
|
{
|
|
// If the exponents are different, these are different numbers.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// OK the exponents match.
|
|
pa = a->mant;
|
|
pb = b->mant;
|
|
pa += a->cdigit - 1;
|
|
pb += b->cdigit - 1;
|
|
cdigits = max( a->cdigit, b->cdigit );
|
|
ccdigits = cdigits;
|
|
|
|
// Loop over all digits until we run out of digits or there is a
|
|
// difference in the digits.
|
|
for ( ;cdigits > 0; cdigits-- )
|
|
{
|
|
da = ( (cdigits > (ccdigits - a->cdigit) ) ?
|
|
*pa-- : 0 );
|
|
db = ( (cdigits > (ccdigits - b->cdigit) ) ?
|
|
*pb-- : 0 );
|
|
if ( da != db )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// In this case, they are equal.
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: lessnum
|
|
//
|
|
// ARGUMENTS: two numbers.
|
|
//
|
|
// RETURN: Boolean
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of ( abs(a) < abs(b) )
|
|
// Only assumes that a and b are the same radix, WARNING THIS IS AN.
|
|
// UNSIGNED COMPARE!
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool lessnum( PNUMBER a, PNUMBER b )
|
|
|
|
{
|
|
long diff;
|
|
MANTTYPE *pa;
|
|
MANTTYPE *pb;
|
|
long cdigits;
|
|
long ccdigits;
|
|
MANTTYPE da;
|
|
MANTTYPE db;
|
|
|
|
|
|
diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
|
|
if ( diff < 0 )
|
|
{
|
|
// The exponent of a is less than b
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if ( diff > 0 )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
pa = a->mant;
|
|
pb = b->mant;
|
|
pa += a->cdigit - 1;
|
|
pb += b->cdigit - 1;
|
|
cdigits = max( a->cdigit, b->cdigit );
|
|
ccdigits = cdigits;
|
|
for ( ;cdigits > 0; cdigits-- )
|
|
{
|
|
da = ( (cdigits > (ccdigits - a->cdigit) ) ?
|
|
*pa-- : 0 );
|
|
db = ( (cdigits > (ccdigits - b->cdigit) ) ?
|
|
*pb-- : 0 );
|
|
diff = da-db;
|
|
if ( diff )
|
|
{
|
|
return( diff < 0 );
|
|
}
|
|
}
|
|
// In this case, they are equal.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNCTION: zernum
|
|
//
|
|
// ARGUMENTS: number
|
|
//
|
|
// RETURN: Boolean
|
|
//
|
|
// DESCRIPTION: Does the number equivalent of ( !a )
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
bool zernum( PNUMBER a )
|
|
|
|
{
|
|
long length;
|
|
MANTTYPE *pcha;
|
|
length = a->cdigit;
|
|
pcha = a->mant;
|
|
|
|
// loop over all the digits until you find a nonzero or until you run
|
|
// out of digits
|
|
while ( length-- > 0 )
|
|
{
|
|
if ( *pcha++ )
|
|
{
|
|
// One of the digits isn't zero, therefore the number isn't zero
|
|
return false;
|
|
}
|
|
}
|
|
// All of the digits are zero, therefore the number is zero
|
|
return true;
|
|
}
|