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
@@ -182,6 +182,13 @@ namespace CalcEngine
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the remainder after division, the sign of a result will match the sign of the current object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function has the same behavior as the standard C/C++ operator '%'
|
||||
/// to calculate the modulus after division instead, use <see cref="RationalMath::Mod"/> instead.
|
||||
/// </remarks>
|
||||
Rational& Rational::operator%=(Rational const& rhs)
|
||||
{
|
||||
PRAT lhsRat = this->ToPRAT();
|
||||
@@ -189,7 +196,7 @@ namespace CalcEngine
|
||||
|
||||
try
|
||||
{
|
||||
modrat(&lhsRat, rhsRat);
|
||||
remrat(&lhsRat, rhsRat);
|
||||
destroyrat(rhsRat);
|
||||
}
|
||||
catch (uint32_t error)
|
||||
@@ -342,6 +349,12 @@ namespace CalcEngine
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the remainder after division, the sign of a result will match the sign of lhs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function has the same behavior as the standard C/C++ operator '%', to calculate the modulus after division instead, use <see cref="Rational::operator%"/> instead.
|
||||
/// </remarks>
|
||||
Rational operator%(Rational lhs, Rational const& rhs)
|
||||
{
|
||||
lhs %= rhs;
|
||||
|
||||
@@ -387,3 +387,33 @@ Rational RationalMath::ATanh(Rational const& rat)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the modulus after division, the sign of the result will match the sign of b.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When one of the operand is negative
|
||||
/// the result will differ from the C/C++ operator '%'
|
||||
/// use <see cref="Rational::operator%"/> instead to calculate the remainder after division.
|
||||
/// </remarks>
|
||||
Rational RationalMath::Mod(Rational const& a, Rational const& b)
|
||||
{
|
||||
PRAT prat = a.ToPRAT();
|
||||
PRAT pn = b.ToPRAT();
|
||||
|
||||
try
|
||||
{
|
||||
modrat(&prat, pn);
|
||||
destroyrat(pn);
|
||||
}
|
||||
catch (uint32_t error)
|
||||
{
|
||||
destroyrat(prat);
|
||||
destroyrat(pn);
|
||||
throw(error);
|
||||
}
|
||||
|
||||
auto res = Rational{ prat };
|
||||
destroyrat(prat);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
|
||||
case IDC_DIV:
|
||||
case IDC_MOD:
|
||||
{
|
||||
int iNumeratorSign = 1, iDenominatorSign = 1, iFinalSign = 1;
|
||||
int iNumeratorSign = 1, iDenominatorSign = 1;
|
||||
auto temp = result;
|
||||
result = rhs;
|
||||
|
||||
@@ -107,20 +107,30 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa
|
||||
|
||||
if (operation == IDC_DIV)
|
||||
{
|
||||
iFinalSign = iNumeratorSign * iDenominatorSign;
|
||||
result /= temp;
|
||||
if (m_fIntegerMode && (iNumeratorSign * iDenominatorSign) == -1)
|
||||
{
|
||||
result = -(Integer(result));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iFinalSign = iNumeratorSign;
|
||||
result %= temp;
|
||||
}
|
||||
if (m_fIntegerMode)
|
||||
{
|
||||
// Programmer mode, use remrat (remainder after division)
|
||||
result %= temp;
|
||||
|
||||
if (m_fIntegerMode && iFinalSign == -1)
|
||||
{
|
||||
result = -(Integer(result));
|
||||
if (iNumeratorSign == -1)
|
||||
{
|
||||
result = -(Integer(result));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//other modes, use modrat (modulus after division)
|
||||
result = Mod(result, temp);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user