#include extern "C" { #include #include #include #include } #include #include #include "layout/string_layout.h" #include "layout/baseline_relative_layout.h" #include namespace Poincare { void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferSize, int i, int decimalMarkerPosition) { /* The decimal marker position is always preceded by a char, thus, it is never * in first position. When called by convertFloatToText, the buffer length is * always > 0 as we asserted a minimal number of available chars. */ assert(bufferSize > 0 && decimalMarkerPosition != 0); int endChar = bufferSize - 1, startChar = 0; int dividend = i, digit = 0, quotien = 0; if (i < 0) { buffer[startChar++] = '-'; dividend = -i; decimalMarkerPosition += 1; } /* This loop acts correctly as we asserted the endChar >= 0 and * decimalMarkerPosition != 0 */ do { if (endChar == decimalMarkerPosition) { assert(endChar >= 0 && endChar < bufferSize); buffer[endChar--] = '.'; } quotien = dividend/10; digit = dividend - quotien*10; assert(endChar >= 0 && endChar < bufferSize); buffer[endChar--] = '0'+digit; dividend = quotien; } while (endChar >= startChar); } template Complex Complex::Float(T x) { return Complex(x,0); } template Complex Complex::Cartesian(T a, T b) { return Complex(a,b); } template Complex Complex::Polar(T r, T th) { // If the radius is 0, theta may be undefined but shouldn't be able to affect the result. if (r == 0) { return Complex(0,0); } T c = std::cos(th); T s = std::sin(th); /* Cheat: see comment on cosine.cpp. * Sine and cosine openbsd immplementationd are numerical approximation. * We though want to avoid evaluating e^(i*pi) to -1+1E-17i. We thus round * cosine and sine results to 0 if they are negligible compared to the * argument th. */ c = th != 0 && std::fabs(c/th) <= Expression::epsilon() ? 0 : c; s = th != 0 && std::fabs(s/th) <= Expression::epsilon() ? 0 : s; return Complex(r*c,r*s); } template static inline T setSign(T f, bool negative) { if (negative) { return -f; } return f; } template T digitsToFloat(const char * digits, int length) { if (digits == nullptr) { return 0; } T result = 0; const char * digit = digits; for (int i = 0; i < length; i++) { result = 10 * result; result += *digit-'0'; digit++; } return result; } template Complex::Complex(const char * integralPart, int integralPartLength, bool integralNegative, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative) { T i = digitsToFloat(integralPart, integralPartLength); T j = digitsToFloat(fractionalPart, fractionalPartLength); T l = setSign(digitsToFloat(exponent, exponentLength), exponentNegative); m_a = setSign((i + j*std::pow(10, -std::ceil((T)fractionalPartLength)))* std::pow(10, l), integralNegative); m_b = 0; } template T Complex::toScalar() const { if (m_b != 0) { return NAN; } return m_a; } template int Complex::numberOfRows() const { return 1; } template int Complex::numberOfColumns() const { return 1; } template Expression::Type Complex::type() const { return Expression::Type::Complex; } template Complex * Complex::clone() const { return new Complex(*this); } template Evaluation * Complex::cloneWithDifferentOperands(Expression** newOperands, int numberOfOperands, bool cloneOperands) const { return this->clone(); } template int Complex::writeTextInBuffer(char * buffer, int bufferSize) const { return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat()); } template Evaluation * Complex::createInverse() const { return new Complex(Cartesian(1/m_a, -1/m_b)); } template T Complex::a() const { return m_a; } template T Complex::b() const { return m_b; } template T Complex::r() const { // We want to avoid a^2 and b^2 which could both easily overflow. // min, max = minmax(abs(a), abs(b)) (*minmax returns both arguments sorted*) // abs(a + bi) == sqrt(a^2 + b^2) // == sqrt(abs(a)^2 + abs(b)^2) // == sqrt(min^2 + max^2) // == sqrt((min^2 + max^2) * max^2/max^2) // == sqrt((min^2 + max^2) / max^2)*sqrt(max^2) // == sqrt(min^2/max^2 + 1) * max // == sqrt((min/max)^2 + 1) * max // min >= 0 && // max >= 0 && // min <= max => min/max <= 1 // => (min/max)^2 <= 1 // => (min/max)^2 + 1 <= 2 // => sqrt((min/max)^2 + 1) <= sqrt(2) // So the calculation is guaranteed to not overflow until the final multiply. // If (min/max)^2 underflows then min doesn't contribute anything significant // compared to max, and the formula reduces to simply max as it should. // We do need to be careful about the case where a == 0 && b == 0 which would // cause a division by zero. T min = std::fabs(m_a); if (m_b == 0) { return min; } T max = std::fabs(m_b); if (max < min) { T temp = min; min = max; max = temp; } T temp = min/max; return std::sqrt(temp*temp + 1) * max; } template T Complex::th() const { T result = std::atan(m_b/m_a) + M_PI; if (m_a >= 0) { T a = m_a == 0 ? 0 : m_a; result = std::atan(m_b/a); } if (result > M_PI + FLT_EPSILON) { result = result - 2*M_PI; } return result; } template Complex Complex::conjugate() const { return Cartesian(m_a, -m_b); } template int Complex::convertFloatToText(T f, char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode mode) { assert(numberOfSignificantDigits > 0); if (mode == Expression::FloatDisplayMode::Default) { return convertFloatToText(f, buffer, bufferSize, numberOfSignificantDigits, Preferences::sharedPreferences()->displayMode()); } char tempBuffer[k_maxFloatBufferLength]; int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode); /* if the required buffer size overflows the buffer size, we first force the * display mode to scientific and decrease the number of significant digits to * fit the buffer size. If the buffer size is still to small, we only write * the beginning of the float and truncate it (which can result in a non sense * text) */ if (mode == Expression::FloatDisplayMode::Decimal && requiredLength >= bufferSize) { requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, Expression::FloatDisplayMode::Scientific); } if (requiredLength >= bufferSize) { int adjustedNumberOfSignificantDigits = numberOfSignificantDigits - requiredLength + bufferSize - 1; adjustedNumberOfSignificantDigits = adjustedNumberOfSignificantDigits < 1 ? 1 : adjustedNumberOfSignificantDigits; requiredLength = convertFloatToTextPrivate(f, tempBuffer, adjustedNumberOfSignificantDigits, Expression::FloatDisplayMode::Scientific); } requiredLength = requiredLength < bufferSize ? requiredLength : bufferSize; strlcpy(buffer, tempBuffer, bufferSize); return requiredLength; } template Complex::Complex(T a, T b) : m_a(a), m_b(b) { } template const Complex * Complex::complexOperand(int i) const { return this; } template ExpressionLayout * Complex::privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const { assert(floatDisplayMode != Expression::FloatDisplayMode::Default); if (complexFormat == Expression::ComplexFormat::Polar) { return createPolarLayout(floatDisplayMode); } return createCartesianLayout(floatDisplayMode); } template template Evaluation * Complex::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { return new Complex(Complex::Cartesian((U)m_a, (U)m_b)); } template int Complex::convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat) const { assert(displayMode != Expression::FloatDisplayMode::Default); int numberOfChars = 0; if (std::isnan(m_a) || std::isnan(m_b)) { return convertFloatToText(NAN, buffer, bufferSize, k_numberOfSignificantDigits, displayMode); } if (complexFormat == Expression::ComplexFormat::Polar) { if (r() != 1 || th() == 0) { numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode); if (r() != 0 && th() != 0 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '*'; // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } } if (r() != 0 && th() != 0) { if (bufferSize > numberOfChars+3) { buffer[numberOfChars++] = Ion::Charset::Exponential; buffer[numberOfChars++] = '^'; buffer[numberOfChars++] = '('; // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); if (bufferSize > numberOfChars+3) { buffer[numberOfChars++] = '*'; buffer[numberOfChars++] = Ion::Charset::IComplex; buffer[numberOfChars++] = ')'; buffer[numberOfChars] = 0; } } return numberOfChars; } if (m_a != 0 || m_b == 0) { numberOfChars = convertFloatToText(m_a, buffer, bufferSize, k_numberOfSignificantDigits, displayMode); if (m_b > 0 && !std::isnan(m_b) && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '+'; // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } } if (m_b != 1 && m_b != -1 && m_b != 0) { numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); buffer[numberOfChars++] = '*'; } if (m_b == -1 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '-'; } if (m_b != 0 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = Ion::Charset::IComplex; buffer[numberOfChars] = 0; } return numberOfChars; } template int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Expression::FloatDisplayMode mode) { assert(mode != Expression::FloatDisplayMode::Default); assert(numberOfSignificantDigits > 0); if (std::isinf(f)) { int currentChar = 0; if (f < 0) { buffer[currentChar++] = '-'; } buffer[currentChar++] = 'i'; buffer[currentChar++] = 'n'; buffer[currentChar++] = 'f'; buffer[currentChar] = 0; return currentChar; } if (std::isnan(f)) { int currentChar = 0; buffer[currentChar++] = 'u'; buffer[currentChar++] = 'n'; buffer[currentChar++] = 'd'; buffer[currentChar++] = 'e'; buffer[currentChar++] = 'f'; buffer[currentChar] = 0; return currentChar; } T logBase10 = f != 0 ? std::log10(std::fabs(f)) : 0; int exponentInBase10 = std::floor(logBase10); /* Correct the exponent in base 10: sometines the exact log10 of f is 6.999999 * but is stored as 7 in hardware. We catch these cases here. */ if (f != 0 && logBase10 == (int)logBase10 && std::fabs(f) < std::pow(10, logBase10)) { exponentInBase10--; } Expression::FloatDisplayMode displayMode = mode; if ((exponentInBase10 >= numberOfSignificantDigits || exponentInBase10 <= -numberOfSignificantDigits) && mode == Expression::FloatDisplayMode::Decimal) { displayMode = Expression::FloatDisplayMode::Scientific; } // Number of char available for the mantissa int availableCharsForMantissaWithoutSign = numberOfSignificantDigits + 1; int availableCharsForMantissaWithSign = f >= 0 ? availableCharsForMantissaWithoutSign : availableCharsForMantissaWithoutSign + 1; // Compute mantissa /* The number of digits in an integer is capped because the maximal integer is * 2^31 - 1. As our mantissa is an integer, we assert that we stay beyond this * threshold during computation. */ assert(availableCharsForMantissaWithoutSign - 1 < std::log10(std::pow(2.0f, 31.0f))); int numberOfDigitBeforeDecimal = exponentInBase10 >= 0 || displayMode == Expression::FloatDisplayMode::Scientific ? exponentInBase10 + 1 : 1; T mantissa = std::round(f * std::pow(10, (T)availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)); /* if availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal * is too big (or too small), mantissa is now inf. We handle this case by * using logarithm function. */ if (std::isnan(mantissa) || std::isinf(mantissa)) { mantissa = std::round(std::pow(10, std::log10(std::fabs(f))+(T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))); mantissa = std::copysign(mantissa, f); } /* We update the exponent in base 10 (if 0.99999999 was rounded to 1 for * instance) */ T truncatedMantissa = (int)(f * std::pow(10, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))); if (std::isinf(truncatedMantissa) || std::isnan(truncatedMantissa)) { truncatedMantissa = (int)(std::pow(10, std::log10(std::fabs(f))+(T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))); truncatedMantissa = std::copysign(truncatedMantissa, f); } if (mantissa != truncatedMantissa) { T newLogBase10 = mantissa != 0 ? std::log10(std::fabs(mantissa/std::pow((T)10, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)))) : 0; if (std::isnan(newLogBase10) || std::isinf(newLogBase10)) { newLogBase10 = std::log10(std::fabs((T)mantissa)) - (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal); } exponentInBase10 = std::floor(newLogBase10); } int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == Expression::FloatDisplayMode::Scientific ? 1 : exponentInBase10+1; // Correct the number of digits in mantissa after rounding int mantissaExponentInBase10 = exponentInBase10 > 0 || displayMode == Expression::FloatDisplayMode::Scientific ? availableCharsForMantissaWithoutSign - 1 : availableCharsForMantissaWithoutSign + exponentInBase10; if ((int)(std::fabs(mantissa) * std::pow((T)10, - mantissaExponentInBase10)) > 0) { mantissa = mantissa/10; } int numberOfCharExponent = exponentInBase10 != 0 ? std::log10(std::fabs((T)exponentInBase10)) + 1 : 1; if (exponentInBase10 < 0){ // If the exponent is < 0, we need a additional char for the sign numberOfCharExponent++; } // Supress the 0 on the right side of the mantissa int dividend = std::fabs((T)mantissa); int quotien = dividend/10; int digit = dividend - quotien*10; int minimumNumberOfCharsInMantissa = 1; while (digit == 0 && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa && (availableCharsForMantissaWithoutSign > exponentInBase10+2 || displayMode == Expression::FloatDisplayMode::Scientific)) { mantissa = mantissa/10; availableCharsForMantissaWithoutSign--; availableCharsForMantissaWithSign--; dividend = quotien; quotien = dividend/10; digit = dividend - quotien*10; } // Suppress the decimal marker if no fractional part if ((displayMode == Expression::FloatDisplayMode::Decimal && availableCharsForMantissaWithoutSign == exponentInBase10+2) || (displayMode == Expression::FloatDisplayMode::Scientific && availableCharsForMantissaWithoutSign == 2)) { availableCharsForMantissaWithSign--; } // Print mantissa assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength); PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, mantissa, decimalMarkerPosition); if (displayMode == Expression::FloatDisplayMode::Decimal || exponentInBase10 == 0) { buffer[availableCharsForMantissaWithSign] = 0; return availableCharsForMantissaWithSign; } // Print exponent assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength); buffer[availableCharsForMantissaWithSign] = Ion::Charset::Exponent; assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < k_maxFloatBufferLength); PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, exponentInBase10, -1); buffer[availableCharsForMantissaWithSign+1+numberOfCharExponent] = 0; return (availableCharsForMantissaWithSign+1+numberOfCharExponent); } template ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const { char bufferBase[k_maxFloatBufferLength+2]; int numberOfCharInBase = 0; char bufferSuperscript[k_maxFloatBufferLength+2]; int numberOfCharInSuperscript = 0; if (std::isnan(r()) || std::isnan(th())) { numberOfCharInBase = convertFloatToText(NAN, bufferBase, k_maxComplexBufferLength, k_numberOfSignificantDigits, floatDisplayMode); return new StringLayout(bufferBase, numberOfCharInBase); } if (r() != 1 || th() == 0) { numberOfCharInBase = convertFloatToText(r(), bufferBase, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); if (r() != 0 && th() != 0) { bufferBase[numberOfCharInBase++] = '*'; } } if (r() != 0 && th() != 0) { bufferBase[numberOfCharInBase++] = Ion::Charset::Exponential; bufferBase[numberOfCharInBase] = 0; } if (r() != 0 && th() != 0) { numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); bufferSuperscript[numberOfCharInSuperscript++] = '*'; bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex; bufferSuperscript[numberOfCharInSuperscript] = 0; } if (numberOfCharInSuperscript == 0) { return new StringLayout(bufferBase, numberOfCharInBase); } return new BaselineRelativeLayout(new StringLayout(bufferBase, numberOfCharInBase), new StringLayout(bufferSuperscript, numberOfCharInSuperscript), BaselineRelativeLayout::Type::Superscript); } template ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const { char buffer[k_maxComplexBufferLength]; int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, Expression::ComplexFormat::Cartesian); return new StringLayout(buffer, numberOfChars); } template class Poincare::Complex; template class Poincare::Complex; template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; }