amath  1.8.5
Simple command line calculator
ntextp.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 #include "amath.h"
31 #include "amathc.h"
32 #include "mathr.h"
33 #include "mathi.h"
34 #include "real.h"
35 #include "cplex.h"
36 #include "ntext.h"
37 #include "ntextd.h"
38 #include "charbuf.h"
39 
41  unsigned int base,
42  unsigned int digits,
43  const char fractionPoint) : digits(digits)
44 {
45  this->fractionPoint = fractionPoint;
46  this->baseInteger = base;
47  this->baseDouble = base * 1.0;
48  this->maxNumeric = (base > 10 ? 10 : base) + '0' - 1;
49  this->maxAlphaLower = base > 10 ? base + 'a' - 11 : 0;
50  this->maxAlphaUpper = base > 10 ? base + 'A' - 11 : 0;
51 }
52 
54 {
55 }
56 
57 bool PositionalNumeralSystem::IsDigit(char *digit)
58 {
59  return true;
60 }
61 
63 {
64  switch (baseInteger)
65  {
66  case 2:
67  return "binary";
68  case 8:
69  return "octal";
70  case 10:
71  return "decimal";
72  case 16:
73  return "hexadecimal";
74  }
75 
76  const char *text = "base ";
78  NumeralSystem *ns = new DecimalSystem(2);
79  const char *numtext = ns->GetText(n);
80 
81  buf->EnsureSize(StrLen(text) + StrLen(numtext) + 1);
83  buf->Append(text);
84  buf->Append(numtext);
85 
86  delete ns;
87  delete n;
88  return buf->GetString();
89 }
90 
92 {
93  // TODO: Consider using prefix with numral systems != base 10
94  return EMPTYSTRING;
95 }
96 
98 {
99  return digits;
100 }
101 
102 void PositionalNumeralSystem::SetDigits(unsigned int digits)
103 {
104  this->digits = digits;
105 }
106 
108 {
109  return static_cast<const char>(this->fractionPoint);
110 }
111 
112 void PositionalNumeralSystem::SetFractionPoint(const char fractionPoint)
113 {
114  this->fractionPoint = fractionPoint;
115 }
116 
118 {
119  if (number->IsNaN())
120  {
121  buf->Empty();
122  buf->Append("NaN");
123  return buf->GetString();
124  }
125 
126  if (number->IsNotImplemented())
127  {
128  buf->Empty();
129  buf->Append("NotImplemented");
130  return buf->GetString();
131  }
132 
133  if (number->IsInfinite() && number->IsNegative())
134  {
135  buf->Empty();
136  buf->Append("-Inf");
137  return buf->GetString();
138  }
139 
140  if (number->IsInfinite() && !number->IsNegative())
141  {
142  buf->Empty();
143  buf->Append("Inf");
144  return buf->GetString();
145  }
146 
147  if (number->IsZero())
148  {
149  buf->Empty();
150  buf->Append("0");
151  return buf->GetString();
152  }
153 
154  return nullptr;
155 }
156 
158 {
159  const char *sc = GetSpecialCase(number);
160  if (sc != nullptr)
161  {
162  return sc;
163  }
164 
165  if (number->system == nsysreal)
166  {
167  return GetText(number->GetRealValue());
168  }
169 
170  complex w = static_cast<ComplexNumber *>(number)->GetComplexValue();
171  double a = creal(w);
172  double b = cimag(w);
173 
174  if (a == 0.0 && b == 0.0)
175  {
176  buf->Empty();
177  buf->Append('0');
178  return buf->GetString();
179  }
180 
181  CharBuffer *val = new CharBuffer(512);
182  val->Empty();
183 
184  if (a != 0.0)
185  {
186  const char *real = GetText(a);
187  val->Append(real);
188  }
189 
190  const char *imag = GetText(b);
191  if (a != 0.0 && b > 0.0)
192  {
193  val->Append('+');
194  }
195 
196  if (b != 0.0)
197  {
198  val->Append(imag);
199  val->Append('i');
200  }
201 
202  buf->Copy(val);
203  delete val;
204 
205  return buf->GetString();
206 }
207 
208 const char *PositionalNumeralSystem::GetText(double number) const
209 {
210  if (number == 0.0)
211  {
212  return "0";
213  }
214 
215  buf->Empty();
216 
217  double dnumber = number;
218  if (dnumber < 0.0)
219  {
220  buf->Append('-');
221  dnumber = -dnumber;
222  }
223 
224  double expbor = log2p(baseDouble, dnumber);
225  double expacc = expbor > 0.0 ? 4e-14 : -1e-15;
226  double expborder = trunc(expbor + expacc);
227 
228  int exponent = 0;
229  double rounding;
230 
231  double bordermax = trunc(9.0 * 10 / baseDouble);
232  double bordermin = trunc(-8.0 * 10 / baseDouble);
233 
234  // Find exponent
235  if (expborder >= bordermax || expborder <= bordermin)
236  {
237  double dexp = trunc(log2p(baseDouble, dnumber) + expacc);
238  dnumber = dnumber * pow(baseDouble, -dexp);
239 
240  // pow is inaccurate on small and large numbers
241  if (dexp > 15 || dexp < -15)
242  {
243  dnumber += 2e-15;
244  }
245 
246  // Adjust if below zero
247  if (dnumber < 1.0)
248  {
249  dexp--;
250  dnumber *= baseDouble;
251  }
252 
253  exponent = static_cast<int>(dexp);
254  rounding = 0;
255  }
256  else
257  {
258  double acc = exponent > 0 ? 15 : -15;
259  rounding = pow(baseDouble, exponent + acc);
260  }
261 
262  int digitout;
263  int intdigits;
264 
265  double intvalue = trunc(dnumber + rounding);
266  IntegerToBuffer(intvalue, digits, &intdigits);
267 
268  int fragdigits = digits - intdigits + (intvalue < 1.0 ? 1 : 0);
269  if (fragdigits > 0)
270  {
272 
273  double fraction = fabs(round((dnumber - intvalue) * pow(baseDouble, fragdigits)));
274  double temp1 = log2p(baseDouble, fraction);
275  FloatUnion64 temp2;
276  temp2.floatingPoint = temp1;
277  bool fin = !temp2.IsInf();
278  int actualdigits = static_cast<int>(trunc(temp1 + 3e-15));
279  int padding = fragdigits - (fin == 1 ? actualdigits : 0) - 1;
280 
281  // Pad zeros if needed
282  while (padding-- > 0)
283  {
284  buf->Append('0');
285  }
286 
287  intvalue = static_cast<int64_t>(trunc(fraction * baseDouble) / baseDouble);
288  IntegerToBuffer(intvalue, fragdigits, &digitout);
289 
290  // Remove trailing zeros
291  // ReSharper disable once CppPossiblyErroneousEmptyStatements
292  while (buf->RemoveTrailing('0'))
293  ;
294 
296  }
297 
298  // Add exponent
299  if (exponent != 0)
300  {
301  buf->Append('e');
302  buf->Append(exponent > 0 ? '+' : '-');
303  IntegerToBuffer(abs(exponent), 3, &digitout);
304  }
305 
306  // Make sure no rounding error is returned
307  if (buf->Is("-0"))
308  {
309  buf->Empty();
310  buf->Append('0');
311  }
312 
313  return buf->GetString();
314 }
315 
316 void PositionalNumeralSystem::IntegerToBuffer(double value, unsigned int digits, int *outdigits) const
317 {
318  static const char *alphaNumerics = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
319  unsigned int count = 0;
320  char *chars = new char[128];
321  char *start = chars;
322 
323  do
324  {
325  count++;
326  unsigned int intremainder = static_cast<unsigned int>(trunc(fmod(value, baseDouble)));
327  *chars++ = alphaNumerics[intremainder];
328  value /= baseDouble;
329  } while (value >= 1.0);
330 
331  unsigned int n = count;
332  unsigned int q = digits;
333  chars--;
334 
335  while (n-- != 0 && q-- != 0)
336  {
337  buf->Append(*chars--);
338  }
339 
340  n++;
341  while (n-- != 0)
342  {
343  buf->Append('0');
344  }
345 
346  *outdigits = count;
347  delete[] start;
348 }
349 
350 Number *PositionalNumeralSystem::Parse(const char *text, unsigned int *length, char **end)
351 {
352  unsigned int pos = 0;
353  double integer = 0;
354  bool done = false;
355 
356  // Parse integer part of number
357  do
358  {
359  double addition;
360  if (*text == '\0')
361  {
362  done = true;
363  continue;
364  }
365 
366  if (*text >= '0' && *text <= maxNumeric)
367  {
368  addition = (*text - '0') * 1.0;
369  }
370  else if (maxAlphaUpper != 0 && *text >= 'A' && *text <= maxAlphaUpper)
371  {
372  addition = (*text - 'A' + 10) * 1.0;
373  }
374  else if (maxAlphaLower != 0 && *text >= 'a' && *text <= maxAlphaLower)
375  {
376  addition = (*text - 'a' + 10) * 1.0;
377  }
378  else
379  {
380  done = true;
381  continue;
382  }
383 
384  integer = integer * baseDouble + addition;
385  text++;
386  pos++;
387  } while (!done);
388 
389  // Digits not found
390  if (pos == 0)
391  {
392  *length = 0;
393  *end = const_cast<char *>(text);
394  return new RealNumber();
395  }
396 
397  // Parse fraction part of number
398  double fraction = 0.0;
399  double divisor = 1.0;
400  if (*text == fractionPoint && fractionPoint != '\0')
401  {
402  done = false;
403  double addition;
404  text++;
405  pos++;
406 
407  do
408  {
409  if (*text == '\0')
410  {
411  done = true;
412  continue;
413  }
414 
415  if (*text >= '0' && *text <= maxNumeric)
416  {
417  addition = (*text - '0') * 1.0;
418  }
419  else if (maxAlphaUpper != 0 && *text >= 'A' && *text <= maxAlphaUpper)
420  {
421  addition = (*text - 'A' + 10) * 1.0;
422  }
423  else if (maxAlphaLower != 0 && *text >= 'a' && *text <= maxAlphaLower)
424  {
425  addition = (*text - 'a' + 10) * 1.0;
426  }
427  else
428  {
429  done = true;
430  continue;
431  }
432 
433  fraction = fraction * baseDouble + addition;
434  divisor *= baseDouble;
435  text++;
436  pos++;
437  } while (!done);
438  }
439 
440  // Parse exponent part of number
441  double exp = 0.0;
442  if (*text == 'e' || *text == 'E')
443  {
444  double addition;
445  text++;
446  pos++;
447 
448  double sign = *text == '+' ? 1.0 : *text == '-' ? -1.0 : 0.0;
449 
450  if (sign != 0.0)
451  {
452  done = false;
453  text++;
454  pos++;
455 
456  do
457  {
458  if (*text == '\0')
459  {
460  done = true;
461  continue;
462  }
463 
464  if (*text >= '0' && *text <= maxNumeric)
465  {
466  addition = (*text - '0') * 1.0;
467  }
468  else if (maxAlphaUpper != 0 && *text >= 'A' && *text <= maxAlphaUpper)
469  {
470  addition = (*text - 'A' + 10) * 1.0;
471  }
472  else if (maxAlphaLower != 0 && *text >= 'a' && *text <= maxAlphaLower)
473  {
474  addition = (*text - 'a' + 10) * 1.0;
475  }
476  else
477  {
478  done = true;
479  continue;
480  }
481 
482  exp = exp * baseDouble + addition;
483  text++;
484  pos++;
485  } while (!done);
486  exp *= sign;
487  }
488  else
489  {
490  text--;
491  pos--;
492  }
493  }
494 
495  *length = pos;
496  *end = const_cast<char *>(text);
497 
498  double dnumber = (integer + (fraction / divisor));
499 
500  if (exp != 0.0)
501  {
502  // pow seems a bit off
503  dnumber *= pow(baseDouble, exp + 4e-15);
504  }
505 
506  return new RealNumber(dnumber);
507 }
virtual bool IsNegative()=0
#define abs(x)
Definition: mathr.h:55
void Append(const char c)
Definition: charbuf.cpp:245
CharBuffer * buf
Definition: ntext.h:65
bool IsDigit(char *digit)
Definition: ntextp.cpp:57
virtual void SetFractionPoint(const char fractionPoint)
Definition: ntextp.cpp:112
const char * GetText(double number) const
Definition: ntextp.cpp:208
#define EMPTYSTRING
Definition: amath.h:213
unsigned int digits
Definition: ntextp.h:61
void Empty()
Definition: charbuf.cpp:218
virtual const char * GetPrefix()
Definition: ntextp.cpp:91
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
virtual const char * GetName()
Definition: ntextp.cpp:62
virtual bool IsInfinite()=0
double pow(double x, double y)
Expontation function.
Definition: pow.c:138
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
CharBuffer(unsigned int size)
Initialize while allocating specified amount of memory.
Definition: charbuf.cpp:49
virtual bool IsZero()=0
double creal(complex z)
Real part of complex number.
Definition: prim.c:38
bool Is(const char *string) const
Compare content of CharBuffer with string)
Definition: charbuf.cpp:194
double round(double x)
Round function.
Definition: round.c:40
void Append(const char *source)
Definition: charbuf.cpp:262
Definition: numb.h:66
const char * GetSpecialCase(Number *number)
Definition: ntextp.cpp:117
double fmod(double x, double y)
Return x mod y in exact arithmetic.
Definition: fmod.c:58
virtual const char * GetText(Number *number)=0
bool IsInf() const
Definition: numb.h:47
RealNumber(unsigned int i)
Definition: real.cpp:76
virtual const char GetFractionPoint()
Definition: ntextp.cpp:107
virtual bool IsNotImplemented()=0
virtual const char * GetText(Number *number)
Definition: ntextp.cpp:157
unsigned int baseInteger
Definition: ntextp.h:68
double floatingPoint
Definition: numb.h:53
virtual bool IsNaN()=0
Base class for all numeral systems with a positional notation.
Definition: ntextp.h:41
Represent a real number with 15 significant digits.
Definition: real.h:45
virtual double GetRealValue()=0
RealNumber()
Definition: real.cpp:37
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
double log2p(double x, double y)
Definition: log2p.c:32
virtual unsigned int GetDigits()
Definition: ntextp.cpp:97
Represent a complex number with 2 components of 15 significant digits.
Definition: cplex.h:46
virtual Number * Parse(const char *text, unsigned int *length, char **end)
Definition: ntextp.cpp:350
complex GetComplexValue() const
Definition: cplex.cpp:75
virtual void SetDigits(unsigned int digits)
Definition: ntextp.cpp:102
int StrLen(const char *string)
Get the length of a null terminated string.
Definition: strlen.c:34
RealNumber(double x)
Definition: real.cpp:42
void Copy(CharBuffer *buf)
Definition: charbuf.cpp:233
Base class for all numeral systems.
Definition: ntext.h:49
DecimalSystem(unsigned int digits)
Definition: ntextd.cpp:66
Encapsulate an character array which can be used as a string.
Definition: charbuf.h:44
void EnsureSize(unsigned int size)
Ensure a memory block of specified size is allocated.
Definition: charbuf.cpp:114
Definition: numb.h:61