mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-23 07:40:42 +01:00
272 lines
9.5 KiB
C++
272 lines
9.5 KiB
C++
#include <poincare/decimal.h>
|
|
#include <poincare/complex.h>
|
|
#include <poincare/rational.h>
|
|
#include <poincare/opposite.h>
|
|
#include <poincare/ieee754.h>
|
|
#include <assert.h>
|
|
#include <ion.h>
|
|
#include <cmath>
|
|
extern "C" {
|
|
#include <assert.h>
|
|
}
|
|
|
|
#include "layout/string_layout.h"
|
|
|
|
namespace Poincare {
|
|
|
|
int Decimal::exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative) {
|
|
int base = 10;
|
|
int exp = 0;
|
|
for (int i = 0; i < exponentLength; i++) {
|
|
exp *= base;
|
|
exp += *exponent-'0';
|
|
exponent++;
|
|
}
|
|
if (exponentNegative) {
|
|
exp = -exp;
|
|
}
|
|
const char * integralPartEnd = integralPart + integralPartLength;
|
|
if (integralPart != nullptr) {
|
|
while (*integralPart == '0' && integralPart < integralPartEnd) {
|
|
integralPart++;
|
|
}
|
|
}
|
|
exp += integralPartEnd-integralPart-1;
|
|
if (integralPart == integralPartEnd) {
|
|
const char * fractionalPartEnd = fractionalPart + fractionalPartLength;
|
|
if (fractionalPart != nullptr) {
|
|
while (*fractionalPart == '0' && fractionalPart < fractionalPartEnd) {
|
|
fractionalPart++;
|
|
exp--;
|
|
}
|
|
}
|
|
if (fractionalPart == fractionalPartEnd) {
|
|
exp += fractionalPartLength+1;
|
|
}
|
|
}
|
|
return exp;
|
|
}
|
|
|
|
void removeZeroAtTheEnd(Integer & i) {
|
|
if (i.isZero()) {
|
|
return;
|
|
}
|
|
Integer base = Integer(10);
|
|
IntegerDivision d = Integer::Division(i, base);
|
|
while (d.remainder.isZero()) {
|
|
i = d.quotient;
|
|
d = Integer::Division(i, base);
|
|
}
|
|
}
|
|
|
|
Integer Decimal::mantissa(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, bool negative) {
|
|
Integer zero = Integer(0);
|
|
Integer base = Integer(10);
|
|
Integer numerator = Integer(integralPart, negative);
|
|
for (int i = 0; i < fractionalPartLength; i++) {
|
|
numerator = Integer::Multiplication(numerator, base);
|
|
numerator = Integer::Addition(numerator, Integer(*fractionalPart-'0'));
|
|
fractionalPart++;
|
|
}
|
|
removeZeroAtTheEnd(numerator);
|
|
return numerator;
|
|
}
|
|
|
|
Decimal::Decimal(Integer mantissa, int exponent) :
|
|
m_mantissa(mantissa),
|
|
m_exponent(exponent)
|
|
{
|
|
}
|
|
|
|
Decimal::Decimal(double f) {
|
|
m_exponent = IEEE754<double>::exponentBase10(f);
|
|
int64_t mantissaf = std::round(f * std::pow(10.0, -m_exponent+PrintFloat::k_numberOfStoredSignificantDigits+1));
|
|
m_mantissa = Integer(mantissaf);
|
|
removeZeroAtTheEnd(m_mantissa);
|
|
}
|
|
|
|
Expression::Type Decimal::type() const {
|
|
return Type::Decimal;
|
|
}
|
|
|
|
Expression * Decimal::clone() const {
|
|
return new Decimal(m_mantissa, m_exponent);
|
|
}
|
|
|
|
template<typename T> Expression * Decimal::templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const {
|
|
T m = m_mantissa.approximate<T>();
|
|
int numberOfDigits = numberOfDigitsInMantissaWithoutSign();
|
|
return new Complex<T>(Complex<T>::Float(m*std::pow((T)10.0, (T)(m_exponent-numberOfDigits+1))));
|
|
}
|
|
|
|
int Decimal::convertToText(char * buffer, int bufferSize, PrintFloat::Mode mode, int numberOfSignificantDigits) const {
|
|
if (bufferSize == 0) {
|
|
return -1;
|
|
}
|
|
buffer[bufferSize-1] = 0;
|
|
int currentChar = 0;
|
|
if (currentChar >= bufferSize-1) { return bufferSize-1; }
|
|
if (m_mantissa.isZero()) {
|
|
buffer[currentChar++] = '0';
|
|
buffer[currentChar] = 0;
|
|
return currentChar;
|
|
}
|
|
char tempBuffer[PrintFloat::k_numberOfStoredSignificantDigits+1];
|
|
// truncate the integer if m_mantissa > 10^numberOfSignificantDigits-1
|
|
Integer absMantissa = m_mantissa;
|
|
absMantissa.setNegative(false);
|
|
while (Integer::NaturalOrder(absMantissa, Integer((int64_t)std::pow(10.0, numberOfSignificantDigits))) >= 0) {
|
|
absMantissa = Integer::Division(absMantissa, Integer(10)).quotient;
|
|
}
|
|
int mantissaLength = absMantissa.writeTextInBuffer(tempBuffer, PrintFloat::k_numberOfStoredSignificantDigits+1);
|
|
if (strcmp(tempBuffer, "undef") == 0) {
|
|
strlcpy(buffer, tempBuffer, bufferSize);
|
|
return mantissaLength;
|
|
}
|
|
int numberOfRequiredDigits = mantissaLength;
|
|
if (mode == PrintFloat::Mode::Decimal) {
|
|
numberOfRequiredDigits = mantissaLength > m_exponent ? mantissaLength : m_exponent;
|
|
numberOfRequiredDigits = m_exponent < 0 ? 1+mantissaLength-m_exponent : numberOfRequiredDigits;
|
|
}
|
|
if (m_mantissa.isNegative()) {
|
|
buffer[currentChar++] = '-';
|
|
if (currentChar >= bufferSize-1) { return bufferSize-1; }
|
|
}
|
|
/* Case 0: the number would be too long if we print it as a natural decimal */
|
|
if (numberOfRequiredDigits > PrintFloat::k_numberOfStoredSignificantDigits || mode == PrintFloat::Mode::Scientific) {
|
|
if (mantissaLength == 1) {
|
|
currentChar += strlcpy(buffer+currentChar, tempBuffer, bufferSize-currentChar);
|
|
} else {
|
|
currentChar++;
|
|
int decimalMarkerPosition = currentChar;
|
|
if (currentChar >= bufferSize-1) { return bufferSize-1; }
|
|
currentChar += strlcpy(buffer+currentChar, tempBuffer, bufferSize-currentChar);
|
|
buffer[decimalMarkerPosition-1] = buffer[decimalMarkerPosition];
|
|
buffer[decimalMarkerPosition] = '.';
|
|
}
|
|
if (m_exponent == 0) {
|
|
return currentChar;
|
|
}
|
|
if (currentChar >= bufferSize-1) { return bufferSize-1; }
|
|
buffer[currentChar++] = Ion::Charset::Exponent;
|
|
currentChar += Integer(m_exponent).writeTextInBuffer(buffer+currentChar, bufferSize-currentChar);
|
|
return currentChar;
|
|
}
|
|
/* Case 1: Print a natural decimal number */
|
|
int deltaCharMantissa = m_exponent < 0 ? -m_exponent+1 : 0;
|
|
strlcpy(buffer+currentChar+deltaCharMantissa, tempBuffer, bufferSize-deltaCharMantissa-currentChar);
|
|
if (m_exponent < 0) {
|
|
for (int i = 0; i <= -m_exponent; i++) {
|
|
if (currentChar >= bufferSize-1) { return bufferSize-1; }
|
|
if (i == 1) {
|
|
buffer[currentChar++] = '.';
|
|
continue;
|
|
}
|
|
buffer[currentChar++] = '0';
|
|
}
|
|
}
|
|
currentChar += mantissaLength;
|
|
if (m_exponent >= 0 && m_exponent < mantissaLength-1) {
|
|
if (currentChar+1 >= bufferSize-1) { return bufferSize-1; }
|
|
int decimalMarkerPosition = m_mantissa.isNegative() ? m_exponent + 1 : m_exponent;
|
|
for (int i = currentChar-1; i > decimalMarkerPosition; i--) {
|
|
buffer[i+1] = buffer[i];
|
|
}
|
|
buffer[decimalMarkerPosition+1] = '.';
|
|
currentChar++;
|
|
}
|
|
if (m_exponent >= 0 && m_exponent > mantissaLength-1) {
|
|
int endMarkerPosition = m_mantissa.isNegative() ? m_exponent+1 : m_exponent;
|
|
for (int i = currentChar-1; i < endMarkerPosition; i++) {
|
|
if (currentChar+1 >= bufferSize-1) { return bufferSize-1; }
|
|
buffer[currentChar++] = '0';
|
|
}
|
|
}
|
|
buffer[currentChar] = 0;
|
|
return currentChar;
|
|
}
|
|
|
|
int Decimal::writeTextInBuffer(char * buffer, int bufferSize, int numberOfSignificantDigits) const {
|
|
return convertToText(buffer, bufferSize, PrintFloat::Mode::Decimal, PrintFloat::k_numberOfStoredSignificantDigits);
|
|
}
|
|
|
|
bool Decimal::needParenthesisWithParent(const Expression * e) const {
|
|
if (sign() == Sign::Positive) {
|
|
return false;
|
|
}
|
|
Type types[] = {Type::Addition, Type::Subtraction, Type::Opposite, Type::Multiplication, Type::Division, Type::Power, Type::Factorial};
|
|
return e->isOfType(types, 7);
|
|
}
|
|
|
|
ExpressionLayout * Decimal::privateCreateLayout(PrintFloat::Mode floatDisplayMode, ComplexFormat complexFormat) const {
|
|
char buffer[k_maxBufferSize];
|
|
int numberOfChars = convertToText(buffer, k_maxBufferSize, floatDisplayMode, PrintFloat::k_numberOfStoredSignificantDigits);
|
|
return new StringLayout(buffer, numberOfChars);
|
|
}
|
|
|
|
Expression * Decimal::shallowReduce(Context& context, AngleUnit angleUnit) {
|
|
Expression * e = Expression::shallowReduce(context, angleUnit);
|
|
if (e != this) {
|
|
return e;
|
|
}
|
|
// Do not reduce decimal to rational if the exponent is too big or too small.
|
|
if (m_exponent > k_maxDoubleExponent || m_exponent < -k_maxDoubleExponent) {
|
|
return this; // TODO: return new Infinite() ? new Rational(0) ?
|
|
}
|
|
int numberOfDigits = numberOfDigitsInMantissaWithoutSign();
|
|
Integer numerator = m_mantissa;
|
|
Integer denominator = Integer(1);
|
|
if (m_exponent >= numberOfDigits-1) {
|
|
numerator = Integer::Multiplication(m_mantissa, Integer::Power(Integer(10), Integer(m_exponent-numberOfDigits+1)));
|
|
} else {
|
|
denominator = Integer::Power(Integer(10), Integer(numberOfDigits-1-m_exponent));
|
|
}
|
|
return replaceWith(new Rational(numerator, denominator), true);
|
|
}
|
|
|
|
Expression * Decimal::shallowBeautify(Context & context, AngleUnit angleUnit) {
|
|
if (m_mantissa.isNegative()) {
|
|
m_mantissa.setNegative(false);
|
|
Opposite * o = new Opposite(this, true);
|
|
return replaceWith(o, true);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
int Decimal::simplificationOrderSameType(const Expression * e, bool canBeInterrupted) const {
|
|
assert(e->type() == Type::Decimal);
|
|
const Decimal * other = static_cast<const Decimal *>(e);
|
|
if (sign() == Sign::Negative && other->sign() == Sign::Positive) {
|
|
return -1;
|
|
}
|
|
if (sign() == Sign::Positive && other->sign() == Sign::Negative) {
|
|
return 1;
|
|
}
|
|
assert(sign() == other->sign());
|
|
int unsignedComparison = 0;
|
|
if (exponent() < other->exponent()) {
|
|
unsignedComparison = -1;
|
|
} else if (exponent() > other->exponent()) {
|
|
unsignedComparison = 1;
|
|
} else {
|
|
assert(exponent() == other->exponent());
|
|
unsignedComparison = Integer::NaturalOrder(mantissa(), other->mantissa());
|
|
}
|
|
return ((int)sign())*unsignedComparison;
|
|
}
|
|
|
|
int Decimal::numberOfDigitsInMantissaWithoutSign() const {
|
|
int numberOfDigits = 1;
|
|
Integer mantissaCopy = m_mantissa;
|
|
mantissaCopy.setNegative(false);
|
|
IntegerDivision d = Integer::Division(mantissaCopy, Integer(10));
|
|
while (!d.quotient.isZero()) {
|
|
mantissaCopy = d.quotient;
|
|
d = Integer::Division(mantissaCopy, Integer(10));
|
|
numberOfDigits++;
|
|
}
|
|
return numberOfDigits;
|
|
}
|
|
|
|
}
|