Numworks Epsilon  1.4.1
Graphing Calculator Operating System
print_float.cpp
Go to the documentation of this file.
1 #include <poincare/print_float.h>
2 #include <poincare/preferences.h>
3 #include <poincare/ieee754.h>
4 extern "C" {
5 #include <assert.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <float.h>
9 }
10 #include <cmath>
11 #include <ion.h>
12 #include <stdio.h>
13 
14 namespace Poincare {
15 
16 void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLength, Integer i, int decimalMarkerPosition) {
17  /* The decimal marker position is always preceded by a char, thus, it is never
18  * in first position. When called by convertFloatToText, the buffer length is
19  * always > 0 as we asserted a minimal number of available chars. */
20  assert(bufferLength > 0 && decimalMarkerPosition != 0);
21  char tempBuffer[PrintFloat::k_maxFloatBufferLength];
22  int intLength = i.writeTextInBuffer(tempBuffer, PrintFloat::k_maxFloatBufferLength);
23  int firstDigitChar = tempBuffer[0] == '-' ? 1 : 0;
24  for (int k = bufferLength-1; k >= firstDigitChar; k--) {
25  if (k == decimalMarkerPosition) {
26  buffer[k] = '.';
27  continue;
28  }
29  if (intLength > firstDigitChar) {
30  buffer[k] = tempBuffer[--intLength];
31  continue;
32  }
33  buffer[k] = '0';
34  }
35  if (firstDigitChar == 1) {
36  buffer[0] = tempBuffer[0];
37  }
38 }
39 
40 template <class T>
41 int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize,
42  int numberOfSignificantDigits, Mode mode) {
43  assert(numberOfSignificantDigits > 0);
44  if (mode == Mode::Default) {
45  return convertFloatToText(f, buffer, bufferSize, numberOfSignificantDigits, Preferences::sharedPreferences()->displayMode());
46  }
47  char tempBuffer[PrintFloat::k_maxFloatBufferLength];
48  int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode);
49  /* if the required buffer size overflows the buffer size, we first force the
50  * display mode to scientific and decrease the number of significant digits to
51  * fit the buffer size. If the buffer size is still to small, we only write
52  * the beginning of the float and truncate it (which can result in a non sense
53  * text) */
54  if (mode == Mode::Decimal && requiredLength >= bufferSize) {
55  requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, Mode::Scientific);
56  }
57  if (requiredLength >= bufferSize) {
58  int adjustedNumberOfSignificantDigits = numberOfSignificantDigits - requiredLength + bufferSize - 1;
59  adjustedNumberOfSignificantDigits = adjustedNumberOfSignificantDigits < 1 ? 1 : adjustedNumberOfSignificantDigits;
60  requiredLength = convertFloatToTextPrivate(f, tempBuffer, adjustedNumberOfSignificantDigits, Mode::Scientific);
61  }
62  requiredLength = requiredLength < bufferSize ? requiredLength : bufferSize;
63  strlcpy(buffer, tempBuffer, bufferSize);
64  return requiredLength;
65 }
66 
67 template <class T>
68 int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Mode mode) {
69  assert(mode != Mode::Default);
70  assert(numberOfSignificantDigits > 0);
71  /*if (std::isinf(f)) {
72  int currentChar = 0;
73  if (f < 0) {
74  buffer[currentChar++] = '-';
75  }
76  buffer[currentChar++] = 'i';
77  buffer[currentChar++] = 'n';
78  buffer[currentChar++] = 'f';
79  buffer[currentChar] = 0;
80  return currentChar;
81  }*/
82 
83  if (std::isinf(f) || std::isnan(f)) {
84  int currentChar = 0;
85  buffer[currentChar++] = 'u';
86  buffer[currentChar++] = 'n';
87  buffer[currentChar++] = 'd';
88  buffer[currentChar++] = 'e';
89  buffer[currentChar++] = 'f';
90  buffer[currentChar] = 0;
91  return currentChar;
92  }
93 
94  int exponentInBase10 = IEEE754<T>::exponentBase10(f);
95 
96  Mode displayMode = mode;
97  if ((exponentInBase10 >= numberOfSignificantDigits || exponentInBase10 <= -numberOfSignificantDigits) && mode == Mode::Decimal) {
98  displayMode = Mode::Scientific;
99  }
100 
101  // Number of char available for the mantissa
102  int availableCharsForMantissaWithoutSign = numberOfSignificantDigits + 1;
103  int availableCharsForMantissaWithSign = f >= 0 ? availableCharsForMantissaWithoutSign : availableCharsForMantissaWithoutSign + 1;
104 
105  // Compute mantissa
106  /* The number of digits in an mantissa is capped because the maximal int64_t
107  * is 2^63 - 1. As our mantissa is an integer built from an int64_t, we assert
108  * that we stay beyond this threshold during computation. */
109  assert(availableCharsForMantissaWithoutSign - 1 < std::log10(std::pow(2.0f, 63.0f)));
110 
111  int numberOfDigitBeforeDecimal = exponentInBase10 >= 0 || displayMode == Mode::Scientific ?
112  exponentInBase10 + 1 : 1;
113 
114  T unroundedMantissa = f * std::pow((T)10.0, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal));
115  T mantissa = std::round(unroundedMantissa);
116 
117  /* if availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal
118  * is too big (or too small), mantissa is now inf. We handle this case by
119  * using logarithm function. */
120  if (std::isnan(mantissa) || std::isinf(mantissa)) {
121  mantissa = std::round(std::pow(10, std::log10(std::fabs(f))+(T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)));
122  mantissa = std::copysign(mantissa, f);
123  }
124  /* We update the exponent in base 10 (if 0.99999999 was rounded to 1 for
125  * instance)
126  * NB: the following if-condition would rather be:
127  * "exponentBase10(unroundedMantissa) != exponentBase10(mantissa)",
128  * however, unroundedMantissa can have a different exponent than expected
129  * (ex: f = 1E13, unroundedMantissa = 99999999.99 and mantissa = 1000000000) */
130  if (f != 0 && IEEE754<T>::exponentBase10(mantissa)-exponentInBase10 != availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal) {
131  exponentInBase10++;
132  }
133 
134  // Update the display mode if the exponent changed
135  if ((exponentInBase10 >= numberOfSignificantDigits || exponentInBase10 <= -numberOfSignificantDigits) && mode == Mode::Decimal) {
136  displayMode = Mode::Scientific;
137  }
138 
139  int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == Mode::Scientific ?
140  1 : exponentInBase10+1;
141  decimalMarkerPosition = f < 0 ? decimalMarkerPosition+1 : decimalMarkerPosition;
142 
143  // Correct the number of digits in mantissa after rounding
144  int mantissaExponentInBase10 = exponentInBase10 > 0 || displayMode == Mode::Scientific ? availableCharsForMantissaWithoutSign - 1 : availableCharsForMantissaWithoutSign + exponentInBase10;
145  if (std::floor(std::fabs((T)mantissa) * std::pow((T)10, - mantissaExponentInBase10)) > 0) {
146  mantissa = mantissa/10;
147  }
148 
149  int numberOfCharExponent = exponentInBase10 != 0 ? std::log10(std::fabs((T)exponentInBase10)) + 1 : 1;
150  if (exponentInBase10 < 0){
151  // If the exponent is < 0, we need a additional char for the sign
152  numberOfCharExponent++;
153  }
154 
155  // Supress the 0 on the right side of the mantissa
156  Integer dividend = Integer((int64_t)std::fabs(mantissa));
157  Integer quotient = Integer::Division(dividend, Integer(10)).quotient;
158  Integer digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10)));
159  int minimumNumberOfCharsInMantissa = 1;
160  while (digit.isZero() && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa &&
161  (availableCharsForMantissaWithoutSign > exponentInBase10+2 || displayMode == Mode::Scientific)) {
162  mantissa = mantissa/10;
163  availableCharsForMantissaWithoutSign--;
164  availableCharsForMantissaWithSign--;
165  dividend = quotient;
166  quotient = Integer::Division(dividend, Integer(10)).quotient;
167  digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10)));
168  }
169 
170  // Suppress the decimal marker if no fractional part
171  if ((displayMode == Mode::Decimal && availableCharsForMantissaWithoutSign == exponentInBase10+2)
172  || (displayMode == Mode::Scientific && availableCharsForMantissaWithoutSign == 2)) {
173  availableCharsForMantissaWithSign--;
174  }
175 
176  // Print mantissa
177  assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength);
178  PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, Integer((int64_t)mantissa), decimalMarkerPosition);
179  if (displayMode == Mode::Decimal || exponentInBase10 == 0) {
180  buffer[availableCharsForMantissaWithSign] = 0;
181  return availableCharsForMantissaWithSign;
182  }
183  // Print exponent
184  assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength);
185  buffer[availableCharsForMantissaWithSign] = Ion::Charset::Exponent;
186  assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < PrintFloat::k_maxFloatBufferLength);
187  PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, Integer(exponentInBase10), -1);
188  buffer[availableCharsForMantissaWithSign+1+numberOfCharExponent] = 0;
189  return (availableCharsForMantissaWithSign+1+numberOfCharExponent);
190 }
191 
192 template int PrintFloat::convertFloatToText<float>(float, char*, int, int, PrintFloat::Mode);
193 template int PrintFloat::convertFloatToText<double>(double, char*, int, int, PrintFloat::Mode);
194 
195 }
196 
static Preferences * sharedPreferences()
Definition: preferences.cpp:14
#define copysign(x, y)
Definition: math.h:171
#define assert(e)
Definition: assert.h:9
int writeTextInBuffer(char *buffer, int bufferSize) const
Definition: integer.cpp:545
#define isinf(x)
Definition: math.h:44
void printBase10IntegerWithDecimalMarker(char *buffer, int bufferLength, Integer i, int decimalMarkerPosition)
Definition: print_float.cpp:16
#define T(x)
Definition: events.cpp:26
size_t strlcpy(char *dst, const char *src, size_t len)
Definition: strlcpy.c:3
#define fabs(x)
Definition: math.h:178
int convertFloatToText(T d, char *buffer, int bufferSize, int numberOfSignificantDigits, Mode mode=Mode::Default)
Definition: print_float.cpp:41
#define round(x)
Definition: math.h:192
#define pow(x, y)
Definition: math.h:190
#define log10(x)
Definition: math.h:186
#define isnan(x)
Definition: math.h:43
static int exponentBase10(T f)
Definition: ieee754.h:54
static Integer Subtraction(const Integer &i, const Integer &j)
Definition: integer.cpp:236
signed long long int64_t
Definition: stdint.h:12
#define floor(x)
Definition: math.h:179
static Integer Multiplication(const Integer &i, const Integer &j)
Definition: integer.cpp:240
static IntegerDivision Division(const Integer &numerator, const Integer &denominator)
Definition: integer.cpp:281