Files
Upsilon/poincare/src/complex.cpp
Romain Goyet 419df39993 [poincare] Add some extra assertions in Complex::convertFloatToText
Change-Id: Ie779eb4ee9bc4316c6718820795c5a651f30d058
2017-05-05 09:07:20 +01:00

380 lines
14 KiB
C++

#include <poincare/complex.h>
extern "C" {
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
}
#include "layout/string_layout.h"
#include "layout/baseline_relative_layout.h"
#include <ion.h>
namespace Poincare {
Complex Complex::Float(float x) {
return Complex(x,0.0f);
}
Complex Complex::Cartesian(float a, float b) {
return Complex(a,b);
}
Complex Complex::Polar(float r, float th) {
return Complex(r*cosf(th),r*sinf(th));
}
static inline float setSign(float f, bool negative) {
if (negative) {
return -f;
}
return f;
}
float digitsToFloat(const char * digits, int length) {
if (digits == nullptr) {
return 0.0f;
}
float result = 0.0f;
const char * digit = digits;
for (int i = 0; i < length; i++) {
result = 10.0f * result;
result += *digit-'0';
digit++;
}
return result;
}
Complex::Complex(const char * integralPart, int integralPartLength, bool integralNegative,
const char * fractionalPart, int fractionalPartLength,
const char * exponent, int exponentLength, bool exponentNegative) {
float i = digitsToFloat(integralPart, integralPartLength);
float j = digitsToFloat(fractionalPart, fractionalPartLength);
float l = setSign(digitsToFloat(exponent, exponentLength), exponentNegative);
m_a = setSign((i + j*powf(10.0f, -ceilf(fractionalPartLength)))* powf(10.0f, l), integralNegative);
m_b = 0.0f;
}
Expression * Complex::clone() const {
return new Complex(Cartesian(m_a, m_b));
}
float Complex::privateApproximate(Context& context, AngleUnit angleUnit) const {
assert(angleUnit != AngleUnit::Default);
if (m_b == 0.0f) {
return m_a;
}
return NAN;
}
Expression * Complex::privateEvaluate(Context& context, AngleUnit angleUnit) const {
assert(angleUnit != AngleUnit::Default);
return clone();
}
Expression::Type Complex::type() const {
return Type::Complex;
}
ExpressionLayout * Complex::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const {
assert(floatDisplayMode != FloatDisplayMode::Default);
if (complexFormat == ComplexFormat::Polar) {
char bufferBase[k_maxFloatBufferLength+2];
int numberOfCharInBase = 0;
char bufferSuperscript[k_maxFloatBufferLength+2];
int numberOfCharInSuperscript = 0;
if (r() != 1.0f || th() == 0.0f) {
numberOfCharInBase = convertFloatToText(r(), bufferBase, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode);
if (r() != 0.0f && th() != 0.0f) {
bufferBase[numberOfCharInBase++] = '*';
}
}
if (r() != 0.0f && th() != 0.0f) {
bufferBase[numberOfCharInBase++] = Ion::Charset::Exponential;
bufferBase[numberOfCharInBase] = 0;
}
if (r() != 0.0f && th() != 0.0f) {
numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode);
bufferSuperscript[numberOfCharInSuperscript++] = '*';
bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex;
bufferSuperscript[numberOfCharInSuperscript] = 0;
}
return new BaselineRelativeLayout(new StringLayout(bufferBase, numberOfCharInBase), new StringLayout(bufferSuperscript, numberOfCharInSuperscript), BaselineRelativeLayout::Type::Superscript);
}
char buffer[k_maxComplexBufferLength];
int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, complexFormat);
return new StringLayout(buffer, numberOfChars);
}
int Complex::writeTextInBuffer(char * buffer, int bufferSize) {
return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat());
}
float Complex::a() {
return m_a;
}
float Complex::b() {
return m_b;
}
float Complex::r() const {
if (m_b == 0) {
return fabsf(m_a);
}
return sqrtf(m_a*m_a + m_b*m_b);
}
float Complex::th() const {
float result = atanf(m_b/m_a) + M_PI;
if (m_a >= 0.0f) {
float a = m_a == 0.0f ? 0.0f : m_a;
result = atanf(m_b/a);
}
if (result > M_PI + FLT_EPSILON) {
result = result - 2.0f*M_PI;
}
return result;
}
Complex * Complex::createConjugate() {
return new Complex(Complex::Cartesian(m_a, -m_b));
}
int Complex::convertFloatToText(float f, char * buffer, int bufferSize,
int numberOfSignificantDigits, FloatDisplayMode mode) {
if (mode == 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 == FloatDisplayMode::Decimal && requiredLength >= bufferSize) {
requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, FloatDisplayMode::Scientific);
}
if (requiredLength >= bufferSize) {
requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits - requiredLength + bufferSize - 1, FloatDisplayMode::Scientific);
}
requiredLength = requiredLength < bufferSize ? requiredLength : bufferSize;
strlcpy(buffer, tempBuffer, bufferSize);
return requiredLength;
}
Complex::Complex(float a, float b) :
m_a(a),
m_b(b)
{
}
int Complex::convertComplexToText(char * buffer, int bufferSize, FloatDisplayMode displayMode, ComplexFormat complexFormat) const {
assert(displayMode != FloatDisplayMode::Default);
int numberOfChars = 0;
if (complexFormat == ComplexFormat::Polar) {
if (r() != 1.0f || th() == 0.0f) {
numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode);
if ((r() != 0.0f && th() != 0.0f) && 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.0f && th() != 0.0f) {
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.0f || m_b == 0.0f) {
numberOfChars = convertFloatToText(m_a, buffer, bufferSize, k_numberOfSignificantDigits, displayMode);
if (m_b > 0.0f && 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.0f && m_b != -1.0f && m_b != 0.0f) {
numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode);
buffer[numberOfChars++] = '*';
}
if (m_b == -1.0f && bufferSize > numberOfChars+1) {
buffer[numberOfChars++] = '-';
}
if (m_b != 0.0f && bufferSize > numberOfChars+1) {
buffer[numberOfChars++] = Ion::Charset::IComplex;
buffer[numberOfChars] = 0;
}
return numberOfChars;
}
int Complex::convertFloatToTextPrivate(float f, char * buffer, int numberOfSignificantDigits, FloatDisplayMode mode) {
assert(mode != FloatDisplayMode::Default);
if (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 (isnan(f)) {
buffer[0] = 'u';
buffer[1] = 'n';
buffer[2] = 'd';
buffer[3] = 'e';
buffer[4] = 'f';
buffer[5] = 0;
return 6;
}
float logBase10 = f != 0.0f ? log10f(fabsf(f)) : 0;
int exponentInBase10 = floorf(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.0f && logBase10 == (int)logBase10 && fabsf(f) < powf(10.0f, logBase10)) {
exponentInBase10--;
}
FloatDisplayMode displayMode = mode;
if ((exponentInBase10 >= numberOfSignificantDigits || exponentInBase10 <= -numberOfSignificantDigits) && mode == FloatDisplayMode::Decimal) {
displayMode = FloatDisplayMode::Scientific;
}
int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == FloatDisplayMode::Scientific ?
1 : exponentInBase10+1;
// 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 < log10f(powf(2, 31)));
int numberOfDigitBeforeDecimal = exponentInBase10 >= 0 || displayMode == FloatDisplayMode::Scientific ?
exponentInBase10 + 1 : 1;
float mantissa = roundf(f * powf(10.0f, 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 (isnan(mantissa) || isinf(mantissa)) {
mantissa = roundf(powf(10.0f, log10f(f)+(float)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)));
}
/* We update the exponent in base 10 (if 0.99999999 was rounded to 1 for
* instance) */
float truncatedMantissa = (int)(f * powf(10, availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal));
if (isinf(truncatedMantissa) || isnan(truncatedMantissa)) {
truncatedMantissa = (int)(powf(10.0f, log10f(f)+(float)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)));
}
if (mantissa != truncatedMantissa) {
float newLogBase10 = mantissa != 0.0f ? log10f(fabsf(mantissa/powf(10, availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))) : 0.0f;
if (isnan(newLogBase10) || isinf(newLogBase10)) {
newLogBase10 = log10f(fabsf(mantissa)) - (float)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal);
}
exponentInBase10 = floorf(newLogBase10);
}
// Correct the number of digits in mantissa after rounding
int mantissaExponentInBase10 = exponentInBase10 > 0 || displayMode == FloatDisplayMode::Scientific ? availableCharsForMantissaWithoutSign - 1 : availableCharsForMantissaWithoutSign + exponentInBase10;
if ((int)(mantissa * powf(10, - mantissaExponentInBase10)) > 0) {
mantissa = mantissa/10;
}
int numberOfCharExponent = exponentInBase10 != 0 ? log10f(fabsf((float)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 = fabsf((float)mantissa);
int quotien = dividend/10;
int digit = dividend - quotien*10;
int minimumNumberOfCharsInMantissa = 1;
while (digit == 0 && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa &&
(availableCharsForMantissaWithoutSign > exponentInBase10+2 || displayMode == 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 == FloatDisplayMode::Decimal && availableCharsForMantissaWithoutSign == exponentInBase10+2)
|| (displayMode == FloatDisplayMode::Scientific && availableCharsForMantissaWithoutSign == 2)) {
availableCharsForMantissaWithSign--;
}
// Print mantissa
assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength);
printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, mantissa, decimalMarkerPosition);
if (displayMode == FloatDisplayMode::Decimal || exponentInBase10 == 0.0f) {
buffer[availableCharsForMantissaWithSign] = 0;
return availableCharsForMantissaWithSign;
}
// Print exponent
assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength);
buffer[availableCharsForMantissaWithSign] = Ion::Charset::Exponent;
assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < k_maxFloatBufferLength);
printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, exponentInBase10, -1);
buffer[availableCharsForMantissaWithSign+1+numberOfCharExponent] = 0;
return (availableCharsForMantissaWithSign+1+numberOfCharExponent);
}
void Complex::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);
}
}