Modify how modulo is calculated in Normal and Scientific mode. (#412)
## Fixes #111 > The modulo operator on this calculator gives the result that is different to the most used calculators. The current `modrate` function is the equivalent of rem(...)/remainder(...), not mod(...)/modulo(...) available in some popular Math apps. ### Description of the changes: - rename `modrate` in `remrate` to be more accurate. - add `modrate`, calculating modulo similarly to Matlab, Bing, Google calculator, Maxima, Wolfram Alpha and Microsoft Excel - Add `RationalMath::Mod` using `modrate` as an alternative to `Rational::operator%` using `remrate` - Add a helper `SIGN` to retrieve the sign of a `Rational`. - modify `CalcEngine` to use `modrate` in Normal and Scientific mode and `remrate` in Programmer mode. ### How changes were validated: - manually and unit tests added
This commit is contained in:
committed by
Daniel Belcher
parent
ad25feda6b
commit
7a7ceb5888
@@ -408,7 +408,7 @@ void powratNumeratorDenominator(PRAT *px, PRAT y, uint32_t radix, int32_t precis
|
||||
//---------------------------------------------------------------------------
|
||||
void powratcomp(PRAT *px, PRAT y, uint32_t radix, int32_t precision)
|
||||
{
|
||||
int32_t sign = ((*px)->pp->sign * (*px)->pq->sign);
|
||||
int32_t sign = SIGN(*px);
|
||||
|
||||
// Take the absolute value
|
||||
(*px)->pp->sign = 1;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -216,7 +216,7 @@ void factrat( PRAT *px, uint32_t radix, int32_t precision)
|
||||
|
||||
// Check for negative integers and throw an error.
|
||||
if ( ( zerrat(frac) || ( LOGRATRADIX(frac) <= -precision) ) &&
|
||||
( (*px)->pp->sign * (*px)->pq->sign == -1 ) )
|
||||
( SIGN(*px) == -1 ) )
|
||||
{
|
||||
throw CALC_E_DOMAIN;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -92,11 +92,9 @@ void asinanglerat( _Inout_ PRAT *pa, ANGLE_TYPE angletype, uint32_t radix, int32
|
||||
void asinrat( PRAT *px, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
int32_t sgn;
|
||||
PRAT pret= nullptr;
|
||||
PRAT phack= nullptr;
|
||||
|
||||
sgn = (*px)->pp->sign* (*px)->pq->sign;
|
||||
int32_t sgn = SIGN(*px);
|
||||
|
||||
(*px)->pp->sign = 1;
|
||||
(*px)->pq->sign = 1;
|
||||
@@ -204,9 +202,7 @@ void _acosrat( PRAT *px, int32_t precision)
|
||||
void acosrat( PRAT *px, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
int32_t sgn;
|
||||
|
||||
sgn = (*px)->pp->sign*(*px)->pq->sign;
|
||||
int32_t sgn = SIGN(*px);
|
||||
|
||||
(*px)->pp->sign = 1;
|
||||
(*px)->pq->sign = 1;
|
||||
@@ -291,10 +287,8 @@ void _atanrat( PRAT *px, int32_t precision)
|
||||
void atanrat( PRAT *px, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
int32_t sgn;
|
||||
PRAT tmpx= nullptr;
|
||||
|
||||
sgn = (*px)->pp->sign * (*px)->pq->sign;
|
||||
int32_t sgn = SIGN(*px);
|
||||
|
||||
(*px)->pp->sign = 1;
|
||||
(*px)->pq->sign = 1;
|
||||
|
@@ -18,54 +18,54 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
void lshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
void lshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
PRAT pwr= nullptr;
|
||||
PRAT pwr = nullptr;
|
||||
int32_t intb;
|
||||
|
||||
intrat(pa, radix, precision);
|
||||
if ( !zernum( (*pa)->pp ) )
|
||||
{
|
||||
if (!zernum((*pa)->pp))
|
||||
{
|
||||
// If input is zero we're done.
|
||||
if ( rat_gt( b, rat_max_exp, precision) )
|
||||
{
|
||||
if (rat_gt(b, rat_max_exp, precision))
|
||||
{
|
||||
// Don't attempt lsh of anything big
|
||||
throw( CALC_E_DOMAIN );
|
||||
}
|
||||
throw(CALC_E_DOMAIN);
|
||||
}
|
||||
intb = rattoi32(b, radix, precision);
|
||||
DUPRAT(pwr,rat_two);
|
||||
DUPRAT(pwr, rat_two);
|
||||
ratpowi32(&pwr, intb, precision);
|
||||
mulrat(pa, pwr, precision);
|
||||
destroyrat(pwr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rshrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
void rshrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
PRAT pwr= nullptr;
|
||||
PRAT pwr = nullptr;
|
||||
int32_t intb;
|
||||
|
||||
intrat(pa, radix, precision);
|
||||
if ( !zernum( (*pa)->pp ) )
|
||||
{
|
||||
if (!zernum((*pa)->pp))
|
||||
{
|
||||
// If input is zero we're done.
|
||||
if ( rat_lt( b, rat_min_exp, precision) )
|
||||
{
|
||||
if (rat_lt(b, rat_min_exp, precision))
|
||||
{
|
||||
// Don't attempt rsh of anything big and negative.
|
||||
throw( CALC_E_DOMAIN );
|
||||
}
|
||||
throw(CALC_E_DOMAIN);
|
||||
}
|
||||
intb = rattoi32(b, radix, precision);
|
||||
DUPRAT(pwr,rat_two);
|
||||
DUPRAT(pwr, rat_two);
|
||||
ratpowi32(&pwr, intb, precision);
|
||||
divrat(pa, pwr, precision);
|
||||
destroyrat(pwr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision);
|
||||
void boolnum( PNUMBER *pa, PNUMBER b, int func );
|
||||
void boolrat(PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision);
|
||||
void boolnum(PNUMBER *pa, PNUMBER b, int func);
|
||||
|
||||
|
||||
enum {
|
||||
@@ -74,22 +74,22 @@ enum {
|
||||
FUNC_XOR
|
||||
} BOOL_FUNCS;
|
||||
|
||||
void andrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
void andrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
boolrat( pa, b, FUNC_AND, radix, precision);
|
||||
boolrat(pa, b, FUNC_AND, radix, precision);
|
||||
}
|
||||
|
||||
void orrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
void orrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
boolrat( pa, b, FUNC_OR, radix, precision);
|
||||
boolrat(pa, b, FUNC_OR, radix, precision);
|
||||
}
|
||||
|
||||
void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
void xorrat(PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
boolrat( pa, b, FUNC_XOR, radix, precision);
|
||||
boolrat(pa, b, FUNC_XOR, radix, precision);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -104,15 +104,15 @@ void xorrat( PRAT *pa, PRAT b, uint32_t radix, int32_t precision)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
|
||||
void boolrat(PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
|
||||
|
||||
{
|
||||
PRAT tmp= nullptr;
|
||||
intrat( pa, radix, precision);
|
||||
DUPRAT(tmp,b);
|
||||
intrat( &tmp, radix, precision);
|
||||
PRAT tmp = nullptr;
|
||||
intrat(pa, radix, precision);
|
||||
DUPRAT(tmp, b);
|
||||
intrat(&tmp, radix, precision);
|
||||
|
||||
boolnum( &((*pa)->pp), tmp->pp, func );
|
||||
boolnum(&((*pa)->pp), tmp->pp, func);
|
||||
destroyrat(tmp);
|
||||
}
|
||||
|
||||
@@ -130,11 +130,11 @@ void boolrat( PRAT *pa, PRAT b, int func, uint32_t radix, int32_t precision)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void boolnum( PNUMBER *pa, PNUMBER b, int func )
|
||||
void boolnum(PNUMBER *pa, PNUMBER b, int func)
|
||||
|
||||
{
|
||||
PNUMBER c= nullptr;
|
||||
PNUMBER a= nullptr;
|
||||
PNUMBER c = nullptr;
|
||||
PNUMBER a = nullptr;
|
||||
MANTTYPE *pcha;
|
||||
MANTTYPE *pchb;
|
||||
MANTTYPE *pchc;
|
||||
@@ -143,26 +143,26 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
|
||||
MANTTYPE da;
|
||||
MANTTYPE db;
|
||||
|
||||
a=*pa;
|
||||
cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
|
||||
min( a->exp, b->exp );
|
||||
createnum( c, cdigits );
|
||||
c->exp = min( a->exp, b->exp );
|
||||
a = *pa;
|
||||
cdigits = max(a->cdigit + a->exp, b->cdigit + b->exp) -
|
||||
min(a->exp, b->exp);
|
||||
createnum(c, cdigits);
|
||||
c->exp = min(a->exp, b->exp);
|
||||
mexp = c->exp;
|
||||
c->cdigit = cdigits;
|
||||
pcha = a->mant;
|
||||
pchb = b->mant;
|
||||
pchc = c->mant;
|
||||
for ( ;cdigits > 0; cdigits--, mexp++ )
|
||||
for (; cdigits > 0; cdigits--, mexp++)
|
||||
{
|
||||
da = (((mexp >= a->exp) && (cdigits + a->exp - c->exp >
|
||||
(c->cdigit - a->cdigit))) ?
|
||||
*pcha++ : 0);
|
||||
db = (((mexp >= b->exp) && (cdigits + b->exp - c->exp >
|
||||
(c->cdigit - b->cdigit))) ?
|
||||
*pchb++ : 0);
|
||||
switch (func)
|
||||
{
|
||||
da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp >
|
||||
(c->cdigit - a->cdigit) ) ) ?
|
||||
*pcha++ : 0 );
|
||||
db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp >
|
||||
(c->cdigit - b->cdigit) ) ) ?
|
||||
*pchb++ : 0 );
|
||||
switch ( func )
|
||||
{
|
||||
case FUNC_AND:
|
||||
*pchc++ = da & db;
|
||||
break;
|
||||
@@ -172,15 +172,51 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
|
||||
case FUNC_XOR:
|
||||
*pchc++ = da ^ db;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
c->sign = a->sign;
|
||||
while ( c->cdigit > 1 && *(--pchc) == 0 )
|
||||
{
|
||||
while (c->cdigit > 1 && *(--pchc) == 0)
|
||||
{
|
||||
c->cdigit--;
|
||||
}
|
||||
destroynum( *pa );
|
||||
*pa=c;
|
||||
}
|
||||
destroynum(*pa);
|
||||
*pa = c;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// FUNCTION: remrat
|
||||
//
|
||||
// ARGUMENTS: pointer to a rational a second rational.
|
||||
//
|
||||
// RETURN: None, changes pointer.
|
||||
//
|
||||
// DESCRIPTION: Calculate the remainder of *pa / b,
|
||||
// equivalent of 'pa % b' in C/C++ and produces a result
|
||||
// that is either zero or has the same sign as the dividend.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void remrat(PRAT *pa, PRAT b)
|
||||
|
||||
{
|
||||
if (zerrat(b))
|
||||
{
|
||||
throw CALC_E_INDEFINITE;
|
||||
}
|
||||
|
||||
PRAT tmp = nullptr;
|
||||
DUPRAT(tmp, b);
|
||||
|
||||
mulnumx(&((*pa)->pp), tmp->pq);
|
||||
mulnumx(&(tmp->pp), (*pa)->pq);
|
||||
remnum(&((*pa)->pp), tmp->pp, BASEX);
|
||||
mulnumx(&((*pa)->pq), tmp->pq);
|
||||
|
||||
// Get *pa back in the integer over integer form.
|
||||
RENORMALIZE(*pa);
|
||||
|
||||
destroyrat(tmp);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -191,28 +227,38 @@ void boolnum( PNUMBER *pa, PNUMBER b, int func )
|
||||
//
|
||||
// RETURN: None, changes pointer.
|
||||
//
|
||||
// DESCRIPTION: Does the rational equivalent of frac(*pa);
|
||||
// DESCRIPTION: Calculate the remainder of *pa / b, with the sign of the result
|
||||
// either zero or has the same sign as the divisor.
|
||||
// NOTE: When *pa or b are negative, the result won't be the same as
|
||||
// the C/C++ operator %, use remrat if it's the behavior you expect.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void modrat( PRAT *pa, PRAT b )
|
||||
|
||||
void modrat(PRAT *pa, PRAT b)
|
||||
{
|
||||
//contrary to remrat(X, 0) returning 0, modrat(X, 0) must return X
|
||||
if (zerrat(b))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PRAT tmp = nullptr;
|
||||
DUPRAT(tmp, b);
|
||||
|
||||
if ( zerrat( b ) )
|
||||
{
|
||||
throw CALC_E_INDEFINITE;
|
||||
}
|
||||
DUPRAT(tmp,b);
|
||||
auto needAdjust = (SIGN(*pa) == -1 ? (SIGN(b) == 1) : (SIGN(b) == -1));
|
||||
|
||||
mulnumx( &((*pa)->pp), tmp->pq );
|
||||
mulnumx( &(tmp->pp), (*pa)->pq );
|
||||
remnum( &((*pa)->pp), tmp->pp, BASEX );
|
||||
mulnumx( &((*pa)->pq), tmp->pq );
|
||||
mulnumx(&((*pa)->pp), tmp->pq);
|
||||
mulnumx(&(tmp->pp), (*pa)->pq);
|
||||
remnum(&((*pa)->pp), tmp->pp, BASEX);
|
||||
mulnumx(&((*pa)->pq), tmp->pq);
|
||||
|
||||
if (needAdjust && !zerrat(*pa))
|
||||
{
|
||||
addrat(pa, b, BASEX);
|
||||
}
|
||||
|
||||
// Get *pa back in the integer over integer form.
|
||||
RENORMALIZE(*pa);
|
||||
|
||||
destroyrat( tmp );
|
||||
destroyrat(tmp);
|
||||
}
|
||||
|
@@ -148,6 +148,9 @@ extern PRAT rat_min_i32;
|
||||
#define LOGNUM2(pnum) ((pnum)->cdigit+(pnum)->exp)
|
||||
#define LOGRAT2(prat) (LOGNUM2((prat)->pp)-LOGNUM2((prat)->pq))
|
||||
|
||||
// SIGN returns the sign of the rational
|
||||
#define SIGN(prat) ((prat)->pp->sign*(prat)->pq->sign)
|
||||
|
||||
#if defined( DEBUG_RATPAK )
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
@@ -423,7 +426,8 @@ extern void divnumx( _Inout_ PNUMBER *pa, _In_ PNUMBER b, int32_t precision);
|
||||
extern void divrat( _Inout_ PRAT *pa, _In_ PRAT b, int32_t precision);
|
||||
extern void fracrat( _Inout_ PRAT *pa , uint32_t radix, int32_t precision);
|
||||
extern void factrat( _Inout_ PRAT *pa, uint32_t radix, int32_t precision);
|
||||
extern void modrat( _Inout_ PRAT *pa, _In_ PRAT b );
|
||||
extern void remrat(_Inout_ PRAT *pa, _In_ PRAT b);
|
||||
extern void modrat(_Inout_ PRAT *pa, _In_ PRAT b);
|
||||
extern void gcdrat( _Inout_ PRAT *pa, int32_t precision);
|
||||
extern void intrat( _Inout_ PRAT *px, uint32_t radix, int32_t precision);
|
||||
extern void mulnum( _Inout_ PNUMBER *pa, _In_ PNUMBER b, uint32_t radix);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -296,7 +296,7 @@ void intrat( PRAT *px, uint32_t radix, int32_t precision)
|
||||
// Subtract the fractional part of the rational
|
||||
PRAT pret = nullptr;
|
||||
DUPRAT(pret,*px);
|
||||
modrat( &pret, rat_one );
|
||||
remrat( &pret, rat_one );
|
||||
|
||||
subrat( px, pret, precision);
|
||||
destroyrat( pret );
|
||||
@@ -348,8 +348,7 @@ bool rat_ge( PRAT a, PRAT b, int32_t precision)
|
||||
b->pp->sign *= -1;
|
||||
addrat( &rattmp, b, precision);
|
||||
b->pp->sign *= -1;
|
||||
bool bret = ( zernum( rattmp->pp ) ||
|
||||
rattmp->pp->sign * rattmp->pq->sign == 1 );
|
||||
bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == 1 );
|
||||
destroyrat( rattmp );
|
||||
return( bret );
|
||||
}
|
||||
@@ -374,8 +373,7 @@ bool rat_gt( PRAT a, PRAT b, int32_t precision)
|
||||
b->pp->sign *= -1;
|
||||
addrat( &rattmp, b, precision);
|
||||
b->pp->sign *= -1;
|
||||
bool bret = ( !zernum( rattmp->pp ) &&
|
||||
rattmp->pp->sign * rattmp->pq->sign == 1 );
|
||||
bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == 1 );
|
||||
destroyrat( rattmp );
|
||||
return( bret );
|
||||
}
|
||||
@@ -400,8 +398,7 @@ bool rat_le( PRAT a, PRAT b, int32_t precision)
|
||||
b->pp->sign *= -1;
|
||||
addrat( &rattmp, b, precision);
|
||||
b->pp->sign *= -1;
|
||||
bool bret = ( zernum( rattmp->pp ) ||
|
||||
rattmp->pp->sign * rattmp->pq->sign == -1 );
|
||||
bool bret = ( zernum( rattmp->pp ) || SIGN(rattmp) == -1 );
|
||||
destroyrat( rattmp );
|
||||
return( bret );
|
||||
}
|
||||
@@ -426,8 +423,7 @@ bool rat_lt( PRAT a, PRAT b, int32_t precision)
|
||||
b->pp->sign *= -1;
|
||||
addrat( &rattmp, b, precision);
|
||||
b->pp->sign *= -1;
|
||||
bool bret = ( !zernum( rattmp->pp ) &&
|
||||
rattmp->pp->sign * rattmp->pq->sign == -1 );
|
||||
bool bret = ( !zernum( rattmp->pp ) && SIGN(rattmp) == -1 );
|
||||
destroyrat( rattmp );
|
||||
return( bret );
|
||||
}
|
||||
|
Reference in New Issue
Block a user