calculator/src/CalcManager/Ratpack/basex.cpp

363 lines
11 KiB
C++

// 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 "ratpak.h"
#include <cstring> // for memmove
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 mulnumx(_Inout_ PNUMBER* pa, _In_ 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.
int32_t 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.
int32_t iadigit = 0; // Index of digit being used in the first number.
int32_t 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.
int32_t 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 = (uint64_t)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] + ((uint32_t)mcy & ((uint32_t)~BASEX));
// update result digit from
ptrc[icdigit++] = (MANTTYPE)((uint32_t)cy & ((uint32_t)~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: numpowi32x
//
// ARGUMENTS: root as number power as int32_t
// 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 numpowi32x(_Inout_ PNUMBER* proot, int32_t power)
{
PNUMBER lret = i32tonum(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 divnumx(_Inout_ PNUMBER* pa, _In_ 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.
int32_t 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.
int32_t cdigits; // count of digits for answer.
MANTTYPE* ptrc; // ptrc is a pointer to the mantissa of c.
int32_t 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))
{
int32_t digit = 0;
*ptrc = 0;
while (!lessnum(rem, b))
{
digit = 1;
DUPNUM(tmp, b);
destroynum(lasttmp);
lasttmp = i32tonum(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;
}