[poincare] Get 14 significant digits when writing complex to text

Change-Id: I0bdcc932782dce91d5861acc698e5b2f7a15355c
This commit is contained in:
Émilie Feral
2017-11-30 10:47:20 +01:00
parent 79b174337a
commit 81016fa168
3 changed files with 92 additions and 86 deletions

View File

@@ -3,6 +3,7 @@
#include <poincare/preferences.h>
#include <poincare/static_hierarchy.h>
#include <poincare/integer.h>
#include <assert.h>
namespace Poincare {
@@ -12,14 +13,29 @@ namespace PrintFloat {
// The wors case is -1.234E-38
return numberOfSignificantDigits + 7;
}
/* This function prints the int i in the buffer with a '.' at the position
/* This function prints the integer i in the buffer with a '.' at the position
* specified by the decimalMarkerPosition. It starts printing at the end of the
* buffer and print from right to left. The integer given should be of the right
* length to be written in bufferLength chars. If the integer is to small, the
* empty chars on the left side are completed with '0'. If the integer is too
* big, the printing stops when no more empty chars are available without
* returning any warning. */
void printBase10IntegerWithDecimalMarker(char * buffer, int bufferSize, int i, int decimalMarkerPosition);
* returning any warning.
* Warning: the buffer is not null terminated but is ensured to hold
* bufferLength chars. */
void printBase10IntegerWithDecimalMarker(char * buffer, int bufferLength, Integer i, int decimalMarkerPosition);
constexpr static int k_numberOfPrintedSignificantDigits = 7;
constexpr static int k_numberOfStoredSignificantDigits = 14;
/* We here define the buffer size to write the lengthest float possible.
* At maximum, the number has 15 significant digits so, in the worst case it
* has the form -1.99999999999999e-308 (15+7+1 char) (the auto mode is always
* shorter. */
constexpr static int k_maxFloatBufferLength = k_numberOfStoredSignificantDigits+7+1;
/* We here define the buffer size to write the lengthest complex possible.
* The worst case has the form
* -1.99999999999999e-308*e^(-1.99999999999999e-308*i) (14+14+7+1 char) */
constexpr static int k_maxComplexBufferLength = k_maxFloatBufferLength-1+k_maxFloatBufferLength-1+7+1;
}
template<typename T>
@@ -65,23 +81,13 @@ public:
static int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode mode = Expression::FloatDisplayMode::Default);
private:
Complex(T a, T b);
constexpr static int k_numberOfSignificantDigits = 7;
ExpressionLayout * privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const override;
Expression * privateApproximate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate<float>(context, angleUnit); }
Expression * privateApproximate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate<double>(context, angleUnit); }
template<typename U> Complex<U> * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const;
/* We here define the buffer size to write the lengthest float possible.
* At maximum, the number has 7 significant digits so, in the worst case it
* has the form -1.999999e-308 (7+7+1 char) (the auto mode is always
* shorter. */
constexpr static int k_maxFloatBufferLength = 7+7+1;
/* We here define the buffer size to write the lengthest complex possible.
* The worst case has the form -1.999999E-308*e^(-1.999999E-308*i) (14+14+7+1
* char) */
constexpr static int k_maxComplexBufferLength = 14+14+7+1;
/* convertComplexToText and convertFloatToTextPrivate return the string length
* of the buffer (does not count the 0 last char)*/
int convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, char multiplicationSign) const;
int convertComplexToText(char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, char multiplicationSign) const;
static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Expression::FloatDisplayMode mode);
ExpressionLayout * createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const;
ExpressionLayout * createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const;

View File

@@ -13,31 +13,28 @@ extern "C" {
namespace Poincare {
void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferSize, int i, int decimalMarkerPosition) {
void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLength, Integer 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--] = '.';
assert(bufferLength > 0 && decimalMarkerPosition != 0);
char tempBuffer[PrintFloat::k_maxFloatBufferLength];
int intLength = i.writeTextInBuffer(tempBuffer, PrintFloat::k_maxFloatBufferLength);
int firstDigitChar = tempBuffer[0] == '-' ? 1 : 0;
for (int k = bufferLength-1; k >= firstDigitChar; k--) {
if (k == decimalMarkerPosition) {
buffer[k] = '.';
continue;
}
quotien = dividend/10;
digit = dividend - quotien*10;
assert(endChar >= 0 && endChar < bufferSize);
buffer[endChar--] = '0'+digit;
dividend = quotien;
} while (endChar >= startChar);
if (intLength > firstDigitChar) {
buffer[k] = tempBuffer[--intLength];
continue;
}
buffer[k] = '0';
}
if (firstDigitChar == 1) {
buffer[0] = tempBuffer[0];
}
}
template<typename T>
@@ -200,7 +197,7 @@ T Complex<T>::toScalar() const {
template <class T>
int Complex<T>::writeTextInBuffer(char * buffer, int bufferSize) const {
return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat(), Ion::Charset::MultiplicationSign);
return convertComplexToText(buffer, bufferSize, PrintFloat::k_numberOfStoredSignificantDigits, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat(), Ion::Charset::MultiplicationSign);
}
template <class T>
@@ -210,7 +207,7 @@ int Complex<T>::convertFloatToText(T f, char * buffer, int bufferSize,
if (mode == Expression::FloatDisplayMode::Default) {
return convertFloatToText(f, buffer, bufferSize, numberOfSignificantDigits, Preferences::sharedPreferences()->displayMode());
}
char tempBuffer[k_maxFloatBufferLength];
char tempBuffer[PrintFloat::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
@@ -253,15 +250,15 @@ Complex<U> * Complex<T>::templatedApproximate(Context& context, Expression::Angl
}
template <class T>
int Complex<T>::convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat, char multiplicationSpecialChar) const {
int Complex<T>::convertComplexToText(char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat, char multiplicationSpecialChar) 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);
return convertFloatToText(NAN, buffer, bufferSize, numberOfSignificantDigits, displayMode);
}
if (complexFormat == Expression::ComplexFormat::Polar) {
if (r() != 1 || th() == 0) {
numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode);
numberOfChars = convertFloatToText(r(), buffer, bufferSize, numberOfSignificantDigits, displayMode);
if (r() != 0 && th() != 0 && bufferSize > numberOfChars+1) {
buffer[numberOfChars++] = multiplicationSpecialChar;
// Ensure that the string is null terminated even if buffer size is to small
@@ -276,7 +273,7 @@ int Complex<T>::convertComplexToText(char * buffer, int bufferSize, Expression::
// 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);
numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, numberOfSignificantDigits, displayMode);
if (bufferSize > numberOfChars+3) {
buffer[numberOfChars++] = multiplicationSpecialChar;
buffer[numberOfChars++] = Ion::Charset::IComplex;
@@ -288,7 +285,7 @@ int Complex<T>::convertComplexToText(char * buffer, int bufferSize, Expression::
}
if (m_a != 0 || m_b == 0) {
numberOfChars = convertFloatToText(m_a, buffer, bufferSize, k_numberOfSignificantDigits, displayMode);
numberOfChars = convertFloatToText(m_a, buffer, bufferSize, 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
@@ -296,7 +293,7 @@ int Complex<T>::convertComplexToText(char * buffer, int bufferSize, Expression::
}
}
if (m_b != 1 && m_b != -1 && m_b != 0) {
numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode);
numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, numberOfSignificantDigits, displayMode);
buffer[numberOfChars++] = multiplicationSpecialChar;
}
if (m_b == -1 && bufferSize > numberOfChars+1) {
@@ -354,14 +351,17 @@ int Complex<T>::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif
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)));
/* The number of digits in an mantissa is capped because the maximal int64_t
* is 2^63 - 1. As our mantissa is an integer built from an int64_t, we assert
* that we stay beyond this threshold during computation. */
assert(availableCharsForMantissaWithoutSign - 1 < std::log10(std::pow(2.0f, 63.0f)));
int numberOfDigitBeforeDecimal = exponentInBase10 >= 0 || displayMode == Expression::FloatDisplayMode::Scientific ?
exponentInBase10 + 1 : 1;
T mantissa = std::round(f * std::pow(10, (T)availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal));
T unroundedMantissa = f * std::pow(10, (T)availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal);
T mantissa = std::round(unroundedMantissa);
/* if availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal
* is too big (or too small), mantissa is now inf. We handle this case by
* using logarithm function. */
@@ -378,10 +378,11 @@ int Complex<T>::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif
exponentInBase10 = std::floor(newLogBase10);
int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == Expression::FloatDisplayMode::Scientific ?
1 : exponentInBase10+1;
decimalMarkerPosition = f < 0 ? decimalMarkerPosition+1 : decimalMarkerPosition;
// 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) {
if (std::floor(std::fabs((T)mantissa) * std::pow((T)10, - mantissaExponentInBase10)) > 0) {
mantissa = mantissa/10;
}
@@ -392,18 +393,18 @@ int Complex<T>::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif
}
// 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;
Integer dividend = Integer((int64_t)std::fabs(mantissa));
Integer quotient = Integer::Division(dividend, Integer(10)).quotient;
Integer digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10)));
int minimumNumberOfCharsInMantissa = 1;
while (digit == 0 && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa &&
while (digit.isZero() && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa &&
(availableCharsForMantissaWithoutSign > exponentInBase10+2 || displayMode == Expression::FloatDisplayMode::Scientific)) {
mantissa = mantissa/10;
availableCharsForMantissaWithoutSign--;
availableCharsForMantissaWithSign--;
dividend = quotien;
quotien = dividend/10;
digit = dividend - quotien*10;
dividend = quotient;
quotient = Integer::Division(dividend, Integer(10)).quotient;
digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10)));
}
// Suppress the decimal marker if no fractional part
@@ -413,34 +414,34 @@ int Complex<T>::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif
}
// Print mantissa
assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength);
PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, mantissa, decimalMarkerPosition);
assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength);
PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, Integer((int64_t)mantissa), decimalMarkerPosition);
if (displayMode == Expression::FloatDisplayMode::Decimal || exponentInBase10 == 0) {
buffer[availableCharsForMantissaWithSign] = 0;
return availableCharsForMantissaWithSign;
}
// Print exponent
assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength);
assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength);
buffer[availableCharsForMantissaWithSign] = Ion::Charset::Exponent;
assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < k_maxFloatBufferLength);
PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, exponentInBase10, -1);
assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < PrintFloat::k_maxFloatBufferLength);
PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, Integer(exponentInBase10), -1);
buffer[availableCharsForMantissaWithSign+1+numberOfCharExponent] = 0;
return (availableCharsForMantissaWithSign+1+numberOfCharExponent);
}
template <class T>
ExpressionLayout * Complex<T>::createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const {
char bufferBase[k_maxFloatBufferLength+2];
char bufferBase[PrintFloat::k_maxFloatBufferLength+2];
int numberOfCharInBase = 0;
char bufferSuperscript[k_maxFloatBufferLength+2];
char bufferSuperscript[PrintFloat::k_maxFloatBufferLength+2];
int numberOfCharInSuperscript = 0;
if (std::isnan(r()) || (std::isnan(th()) && r() != 0)) {
numberOfCharInBase = convertFloatToText(NAN, bufferBase, k_maxComplexBufferLength, k_numberOfSignificantDigits, floatDisplayMode);
numberOfCharInBase = convertFloatToText(NAN, bufferBase, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode);
return new StringLayout(bufferBase, numberOfCharInBase);
}
if (r() != 1 || th() == 0) {
numberOfCharInBase = convertFloatToText(r(), bufferBase, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode);
numberOfCharInBase = convertFloatToText(r(), bufferBase, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode);
if (r() != 0 && th() != 0) {
bufferBase[numberOfCharInBase++] = Ion::Charset::MiddleDot;
}
@@ -451,7 +452,7 @@ ExpressionLayout * Complex<T>::createPolarLayout(Expression::FloatDisplayMode fl
}
if (r() != 0 && th() != 0) {
numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode);
numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode);
bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::MiddleDot;
bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex;
bufferSuperscript[numberOfCharInSuperscript] = 0;
@@ -464,8 +465,8 @@ ExpressionLayout * Complex<T>::createPolarLayout(Expression::FloatDisplayMode fl
template <class T>
ExpressionLayout * Complex<T>::createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const {
char buffer[k_maxComplexBufferLength];
int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot);
char buffer[PrintFloat::k_maxComplexBufferLength];
int numberOfChars = convertComplexToText(buffer, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot);
return new StringLayout(buffer, numberOfChars);
}

View File

@@ -91,6 +91,7 @@ QUIZ_CASE(assert_float_prints_to) {
assert_float_prints_to(123.421, "1.2E2", DecimalDisplay, 5, 6);
assert_float_prints_to(9.999999f, "10", DecimalDisplay, 6);
assert_float_prints_to(-9.99999904, "-10", DecimalDisplay, 6);
assert_float_prints_to(-0.017452, "-0.01745", DecimalDisplay, 6);
assert_float_prints_to(1E50, "1E50", DecimalDisplay, 9);
}
@@ -164,29 +165,27 @@ QUIZ_CASE(poincare_decimal_to_text) {
QUIZ_CASE(poincare_complex_to_text) {
Complex<double> c0 = Complex<double>::Cartesian(1.0, 2.0);
assert_expression_prints_to(&c0, "1+2*I", DecimalDisplay, Cartesian);
Complex<float> c1 = Complex<float>::Cartesian(1.0f, 2.0f);
assert_expression_prints_to(&c1, "2.236068*X^(1.107149*I)", DecimalDisplay, Polar);
Complex<float> c2 = Complex<float>::Cartesian(-1.3f, 2.444f);
Complex<double> c1 = Complex<double>::Cartesian(1.0, 2.0);
assert_expression_prints_to(&c1, "2.2360679774998*X^(1.1071487177941*I)", DecimalDisplay, Polar);
Complex<double> c2 = Complex<double>::Cartesian(-1.3, 2.444);
assert_expression_prints_to(&c2, "-1.3+2.444*I", DecimalDisplay, Cartesian);
Complex<double> c3 = Complex<double>::Cartesian(-1.3, 2.444);
assert_expression_prints_to(&c3, "2.768237*X^(2.059649*I)", DecimalDisplay, Polar);
Complex<float> c4 = Complex<float>::Cartesian(-1.3f, -2.444f);
assert_expression_prints_to(&c3, "2.7682369840749*X^(2.0596486811226*I)", DecimalDisplay, Polar);
Complex<double> c4 = Complex<double>::Cartesian(-1.3, -2.444);
assert_expression_prints_to(&c4, "-1.3-2.444*I", DecimalDisplay, Cartesian);
Complex<double> c5 = Complex<double>::Cartesian(64078208.0, 119229408.0);
assert_expression_prints_to(&c5, "6.407821E7+1.192294E8*I", DecimalDisplay, Cartesian);
Complex<float> c6 = Complex<float>::Cartesian(64078208.0f, 119229408.0f);
assert_expression_prints_to(&c6, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar);
Complex<float> c7 = Complex<float>::Cartesian(64078208.0f, 119229408.0f);
assert_expression_prints_to(&c7, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar);
Complex<float> c8 = Complex<float>::Cartesian(INFINITY, 119229408.0f);
assert_expression_prints_to(&c8, "undef", DecimalDisplay, Polar);
Complex<float> c9 = Complex<float>::Cartesian(0.0f, 0.0f);
assert_expression_prints_to(&c9, "0", DecimalDisplay, Polar);
Complex<float> c10 = Complex<float>::Cartesian(NAN, 0.0f);
assert_expression_prints_to(&c5, "64078208+119229408*I", DecimalDisplay, Cartesian);
Complex<double> c6 = Complex<double>::Cartesian(64078208.0, 119229408.0);
assert_expression_prints_to(&c6, "135357557.86997*X^(1.0776501182461*I)", DecimalDisplay, Polar);
Complex<double> c7 = Complex<double>::Cartesian(INFINITY, 119229408.0);
assert_expression_prints_to(&c7, "undef", DecimalDisplay, Polar);
Complex<float> c8 = Complex<float>::Cartesian(0.0f, 0.0f);
assert_expression_prints_to(&c8, "0", DecimalDisplay, Polar);
Complex<float> c9 = Complex<float>::Cartesian(NAN, 0.0f);
assert_expression_prints_to(&c9, "undef", DecimalDisplay, Polar);
Complex<float> c10 = Complex<float>::Cartesian(0.0f, NAN);
assert_expression_prints_to(&c10, "undef", DecimalDisplay, Polar);
Complex<float> c11 = Complex<float>::Cartesian(0.0f, NAN);
Complex<double> c11 = Complex<double>::Cartesian(NAN, NAN);
assert_expression_prints_to(&c11, "undef", DecimalDisplay, Polar);
Complex<double> c12 = Complex<double>::Cartesian(NAN, NAN);
assert_expression_prints_to(&c12, "undef", DecimalDisplay, Polar);
}