amath  1.8.5
Simple command line calculator
ntextd.cpp
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 2014-2018 Carsten Sonne Larsen <cs@innolan.net>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * Project homepage:
26  * https://amath.innolan.net
27  *
28  */
29 
30 /*
31  * Copyright (c) 2014 Ryan Juckett
32  * http://www.ryanjuckett.com/
33  *
34  * This software is provided 'as-is', without any express or implied
35  * warranty. In no event will the authors be held liable for any damages
36  * arising from the use of this software.
37  *
38  * Permission is granted to anyone to use this software for any purpose,
39  * including commercial applications, and to alter it and redistribute it
40  * freely, subject to the following restrictions:
41  *
42  * 1. The origin of this software must not be misrepresented; you must not
43  * claim that you wrote the original software. If you use this software
44  * in a product, an acknowledgment in the product documentation would be
45  * appreciated but is not required.
46  *
47  * 2. Altered source versions must be plainly marked as such, and must not
48  * be misrepresented as being the original software.
49  *
50  * 3. This notice may not be removed or altered from any source
51  * distribution.
52  */
53 
54 #include "ntextd.h"
55 #include "bigint.h"
56 #include "amathc.h"
57 #include "amath.h"
58 #include "mathr.h"
59 #include "mathi.h"
60 #include "cplex.h"
61 
63 {
64 }
65 
66 DecimalSystem::DecimalSystem(unsigned int digits) :
67  PositionalNumeralSystem(10, digits, '\0')
68 {
69 }
70 
71 DecimalSystem::DecimalSystem(unsigned int digits, const char fractionPoint) :
72  PositionalNumeralSystem(10, digits, fractionPoint)
73 {
74 }
75 
77 {
78 }
79 
80 const char *DecimalSystem::GetText(Number *number)
81 {
82  const char *sc = GetSpecialCase(number);
83  if (sc != nullptr)
84  {
85  return sc;
86  }
87 
88  if (number->system == nsysreal)
89  {
91  return GetText(number->GetRealValue());
92  }
93 
94  complex w = ((ComplexNumber *)number)->GetComplexValue();
95  double a = creal(w);
96  double b = cimag(w);
97 
99  if (a != 0.0)
100  {
101  GetText(a);
102  }
103 
104  if (a != 0.0 && b > 0.0)
105  {
106  buf->Append('+');
107  }
108 
109  if (b != 0.0)
110  {
111  GetText(b);
112  buf->Append('i');
113  }
114 
115  return buf->GetString();
116 }
117 
118 const char *DecimalSystem::GetText(double number) const
119 {
120  int32_t printExponent = 0;
121  double dexponent = log10(fabs(number));
122  int32_t precision = digits - (int32_t)trunc(dexponent) - 1;
123  int32_t lim = 15;
124  bool sci = (dexponent > 9.0 || dexponent < -8.0);
125  if (sci)
126  {
127  precision = digits;
128  }
129  else if (dexponent < 0.0)
130  {
131  precision++;
132  lim--;
133  }
134 
135  if (precision > lim)
136  {
137  precision = lim;
138  }
139 
140  static const int size = 64;
141  char *out = new char[size];
142  char *pOutBuffer = out;
143  uint32_t bufferSize = size;
144 
145  FloatUnion64 floatUnion;
146  floatUnion.floatingPoint = number;
147  uint32_t floatExponent = floatUnion.GetExponent();
148  uint64_t floatMantissa = floatUnion.GetMantissa();
149 
150  if (floatUnion.IsNegative())
151  {
152  pOutBuffer[0] = '-';
153  ++pOutBuffer;
154  --bufferSize;
155  }
156 
157  uint64_t mantissa;
158  int32_t exponent;
159  uint32_t mantissaHighBitIdx;
160  bool hasUnequalMargins;
161 
162  if (floatExponent != 0)
163  {
164  // normal
165  mantissa = (1ull << 52) | floatMantissa;
166  exponent = floatExponent - 1023 - 52;
167  mantissaHighBitIdx = 52;
168  hasUnequalMargins = (floatExponent != 1) && (floatMantissa == 0);
169  }
170  else
171  {
172  // subnormal
173  mantissa = floatMantissa;
174  exponent = 1 - 1023 - 52;
175  mantissaHighBitIdx = log2i(mantissa);
176  hasUnequalMargins = false;
177  }
178 
179  if (!sci)
180  {
181  int32_t printExponent;
182  uint32_t numPrintDigits;
183  uint32_t maxPrintLen = bufferSize - 1;
184  numPrintDigits = Dragon4(mantissa,
185  exponent,
186  mantissaHighBitIdx,
187  hasUnequalMargins,
189  precision,
190  pOutBuffer,
191  maxPrintLen,
192  &printExponent);
193 
194  // track the number of digits past the decimal point that have been printed
195  uint32_t numFractionDigits = 0;
196 
197  // if output has a whole number
198  if (printExponent >= 0)
199  {
200  // leave the whole number at the start of the buffer
201  uint32_t numWholeDigits = printExponent + 1;
202  if (numPrintDigits < numWholeDigits)
203  {
204  // don't overflow the buffer
205  if (numWholeDigits > maxPrintLen)
206  numWholeDigits = maxPrintLen;
207 
208  // add trailing zeros up to the decimal point
209  for (; numPrintDigits < numWholeDigits; ++numPrintDigits)
210  pOutBuffer[numPrintDigits] = '0';
211  }
212  // insert the decimal point prior to the fraction
213  else if (numPrintDigits > (uint32_t)numWholeDigits)
214  {
215  numFractionDigits = numPrintDigits - numWholeDigits;
216  uint32_t maxFractionDigits = maxPrintLen - numWholeDigits - 1;
217  if (numFractionDigits > maxFractionDigits)
218  numFractionDigits = maxFractionDigits;
219 
220  MemCopy(pOutBuffer + numWholeDigits + 1, pOutBuffer + numWholeDigits, numFractionDigits);
221  pOutBuffer[numWholeDigits] = fractionPoint;
222  numPrintDigits = numWholeDigits + 1 + numFractionDigits;
223  }
224  }
225  else
226  {
227  // shift out the fraction to make room for the leading zeros
228  if (maxPrintLen > 2)
229  {
230  uint32_t numFractionZeros = (uint32_t)-printExponent - 1;
231  uint32_t maxFractionZeros = maxPrintLen - 2;
232  if (numFractionZeros > maxFractionZeros)
233  numFractionZeros = maxFractionZeros;
234 
235  uint32_t digitsStartIdx = 2 + numFractionZeros;
236 
237  // shift the significant digits right such that there is room for leading zeros
238  numFractionDigits = numPrintDigits;
239  uint32_t maxFractionDigits = maxPrintLen - digitsStartIdx;
240  if (numFractionDigits > maxFractionDigits)
241  numFractionDigits = maxFractionDigits;
242 
243  MemCopy(pOutBuffer + digitsStartIdx, pOutBuffer, numFractionDigits);
244 
245  // insert the leading zeros
246  for (uint32_t i = 2; i < digitsStartIdx; ++i)
247  pOutBuffer[i] = '0';
248 
249  // update the counts
250  numFractionDigits += numFractionZeros;
251  numPrintDigits = numFractionDigits;
252  }
253 
254  // add the decimal point
255  if (maxPrintLen > 1)
256  {
257  pOutBuffer[1] = fractionPoint;
258  numPrintDigits += 1;
259  }
260 
261  // add the initial zero
262  if (maxPrintLen > 0)
263  {
264  pOutBuffer[0] = '0';
265  numPrintDigits += 1;
266  }
267  }
268 
269  // add trailing zeros up to precision length
270  if (precision > (int32_t)numFractionDigits && numPrintDigits < maxPrintLen)
271  {
272  // add a decimal point if this is the first fractional digit we are printing
273  if (numFractionDigits == 0)
274  {
275  pOutBuffer[numPrintDigits++] = fractionPoint;
276  }
277 
278  // compute the number of trailing zeros needed
279  uint32_t totalDigits = numPrintDigits + (precision - numFractionDigits);
280  if (totalDigits > maxPrintLen)
281  totalDigits = maxPrintLen;
282 
283  for (; numPrintDigits < totalDigits; ++numPrintDigits)
284  pOutBuffer[numPrintDigits] = '0';
285  }
286 
287  pOutBuffer[numPrintDigits] = '\0';
288  }
289  else
290  {
291  uint32_t numPrintDigits;
292  numPrintDigits = Dragon4(mantissa,
293  exponent,
294  mantissaHighBitIdx,
295  hasUnequalMargins,
297  precision + 1,
298  pOutBuffer,
299  bufferSize,
300  &printExponent);
301 
302  char *pCurOut = pOutBuffer;
303  pCurOut += 1;
304 
305  // insert the decimal point prior to the fractional number
306  uint32_t numFractionDigits = numPrintDigits - 1;
307  if (numFractionDigits > 0 && bufferSize > 1)
308  {
309  uint32_t maxFractionDigits = bufferSize - 2;
310  if (numFractionDigits > maxFractionDigits)
311  numFractionDigits = maxFractionDigits;
312 
313  MemCopy(pCurOut + 1, pCurOut, numFractionDigits);
314  pCurOut[0] = fractionPoint;
315  pCurOut += (1 + numFractionDigits);
316  }
317 
318  pCurOut[0] = '\0';
319  }
320 
322  buf->Append(out);
323 
325  {
326  while (buf->RemoveTrailing('0'))
327  ;
329  }
330 
331  if (printExponent != 0)
332  {
333  buf->Append('e');
334  if (printExponent >= 0)
335  {
336  buf->Append('+');
337  }
338  else
339  {
340  buf->Append('-');
341  printExponent = -printExponent;
342  }
343  int temp;
344  IntegerToBuffer(printExponent, 3, &temp);
345  }
346 
348  {
349  while (buf->RemoveTrailing('0'))
350  ;
352  }
353 
354  delete[] out;
355  return buf->GetString();
356 }
void Append(const char c)
Definition: charbuf.cpp:245
CharBuffer * buf
Definition: ntext.h:65
unsigned int digits
Definition: ntextp.h:61
void Empty()
Definition: charbuf.cpp:218
PositionalNumeralSystem(unsigned int base, unsigned int digits, const char fractionPoint)
Definition: ntextp.cpp:40
char * GetString() const
Definition: charbuf.cpp:306
double trunc(double x)
Truncate function.
Definition: trunc.c:52
double log10(double x)
Base 10 logarithm function.
Definition: log10.c:93
void IntegerToBuffer(double value, unsigned int digits, int *outdigits) const
Definition: ntextp.cpp:316
NumberSystem system
Definition: numb.h:171
bool RemoveTrailing(const char c)
Definition: charbuf.cpp:270
double creal(complex z)
Real part of complex number.
Definition: prim.c:38
int32_t GetExponent() const
Definition: numb.h:51
void Append(const char *source)
Definition: charbuf.cpp:262
Definition: numb.h:66
const char * GetSpecialCase(Number *number)
Definition: ntextp.cpp:117
const char * GetText(double number) const
Definition: ntextd.cpp:118
bool IsNegative() const
Definition: numb.h:45
DecimalSystem(unsigned int digits, const char fractionPoint)
Definition: ntextd.cpp:71
double floatingPoint
Definition: numb.h:53
unsigned int log2i(unsigned int x)
uint64_t GetMantissa() const
Definition: numb.h:52
Base class for all numeral systems with a positional notation.
Definition: ntextp.h:41
virtual double GetRealValue()=0
double cimag(complex z)
Imaginary part of complex number.
Definition: prim.c:46
double fabs(double x)
Returns the absolute value of x.
Definition: fabs.c:51
~DecimalSystem()
Definition: ntextd.cpp:76
Represent a complex number with 2 components of 15 significant digits.
Definition: cplex.h:46
uint32_t Dragon4(uint64_t mantissa, int32_t exponent, uint32_t mantissaHighBitIdx, bool hasUnequalMargins, tCutoffMode cutoffMode, uint32_t cutoffNumber, char *pOubooluffer, uint32_t bufferSize, int32_t *pOutExponent)
Definition: bigint.cpp:608
void EnsureGrowth(unsigned int size)
Definition: charbuf.cpp:169
complex GetComplexValue() const
Definition: cplex.cpp:75
bool Contains(const char c) const
Definition: charbuf.cpp:199
DecimalSystem(unsigned int digits)
Definition: ntextd.cpp:66
virtual const char * GetText(Number *number)
Definition: ntextd.cpp:80
void MemCopy(void *destination, const void *source, unsigned int length)
Copy a block of memory, handling overlap.
Definition: memcpy.c:75
Definition: numb.h:61