1
2/*
3 *  M_APM  -  mapm_fpf.c
4 *
5 *  Copyright (C) 2001 - 2007   Michael C. Ring
6 *
7 *  Permission to use, copy, and distribute this software and its
8 *  documentation for any purpose with or without fee is hereby granted,
9 *  provided that the above copyright notice appear in all copies and
10 *  that both that copyright notice and this permission notice appear
11 *  in supporting documentation.
12 *
13 *  Permission to modify the software is granted. Permission to distribute
14 *  the modified code is granted. Modifications are to be distributed by
15 *  using the file 'license.txt' as a template to modify the file header.
16 *  'license.txt' is available in the official MAPM distribution.
17 *
18 *  This software is provided "as is" without express or implied warranty.
19 */
20
21/*
22 *      $Id: mapm_fpf.c,v 1.10 2007/12/03 01:39:57 mike Exp $
23 *
24 *      This file contains the Fixed Point Formatting functions
25 *
26 *      $Log: mapm_fpf.c,v $
27 *      Revision 1.10  2007/12/03 01:39:57  mike
28 *      Update license
29 *
30 *      Revision 1.9  2003/07/21 20:15:12  mike
31 *      Modify error messages to be in a consistent format.
32 *
33 *      Revision 1.8  2003/03/31 22:11:14  mike
34 *      call generic error handling function
35 *
36 *      Revision 1.7  2002/11/05 23:31:00  mike
37 *      use new set_to_zero call instead of copy
38 *
39 *      Revision 1.6  2002/11/03 22:33:24  mike
40 *      Updated function parameters to use the modern style
41 *
42 *      Revision 1.5  2002/02/14 19:31:44  mike
43 *      eliminate need for conditional compile
44 *
45 *      Revision 1.4  2001/08/26 22:35:50  mike
46 *      no LCC conditional needed on fixpt_string
47 *
48 *      Revision 1.3  2001/08/26 22:11:10  mike
49 *      add new 'stringexp' function
50 *
51 *      Revision 1.2  2001/08/25 22:30:09  mike
52 *      fix LCC-WIN32 compile problem
53 *
54 *      Revision 1.1  2001/08/25 16:50:59  mike
55 *      Initial revision
56 */
57
58#include "m_apm_lc.h"
59#include <ctype.h>
60
61/****************************************************************************/
62char	*m_apm_to_fixpt_stringexp(int dplaces, M_APM atmp,
63				  char ch_radx, char ch_sep, int ct_sep)
64{
65int	places, xp, dl, ii;
66char	*cpr;
67
68places = dplaces;
69
70dl = atmp->m_apm_datalength;
71xp = atmp->m_apm_exponent;
72
73if (places < 0)				/* show ALL digits */
74  {
75   if (xp < 0)
76      ii = dl - xp;
77   else
78     {
79      if (dl > xp)
80        ii = dl;
81      else
82        ii = xp;
83     }
84  }
85else
86  {
87   ii = places;
88
89   if (xp > 0)
90     ii += xp;
91  }
92
93if (ct_sep != 0 && ch_sep != 0 && xp > 0)
94  ii += xp / ct_sep;
95
96if ((cpr = (char *)MAPM_MALLOC((ii + 32) * sizeof(char))) == NULL)
97  return(NULL);
98
99m_apm_to_fixpt_stringex(cpr,places,atmp,ch_radx,ch_sep,ct_sep);
100
101return(cpr);
102}
103/****************************************************************************/
104void	m_apm_to_fixpt_stringex(char *s, int dplaces, M_APM atmp,
105				char ch_radix, char ch_sep, int count_sep)
106{
107M_APM   btmp;
108char    ch, *cpd, *cps;
109int	ii, jj, kk, ct, dl, xp, no_sep_flg, places;
110
111btmp       = M_get_stack_var();
112places     = dplaces;
113cpd        = s;
114no_sep_flg = FALSE;
115
116m_apm_absolute_value(btmp, atmp);	/* do conversion of positive number */
117
118if (ch_sep == 0 || count_sep == 0)	/* no separator char OR count */
119  no_sep_flg = TRUE;
120
121/* determine how much memory to get for the temp string */
122
123dl = btmp->m_apm_datalength;
124xp = btmp->m_apm_exponent;
125
126if (places < 0)				/* show ALL digits */
127  {
128   if (xp < 0)
129      ii = dl - xp;
130   else
131     {
132      if (dl > xp)
133        ii = dl;
134      else
135        ii = xp;
136     }
137  }
138else
139  {
140   ii = places;
141
142   if (xp > 0)
143     ii += xp;
144  }
145
146if ((cps = (char *)MAPM_MALLOC((ii + 32) * sizeof(char))) == NULL)
147  {
148   /* fatal, this does not return */
149
150   M_apm_log_error_msg(M_APM_FATAL,
151                       "\'m_apm_to_fixpt_stringex\', Out of memory");
152  }
153
154m_apm_to_fixpt_string(cps, places, btmp);
155
156/*
157 *  the converted string may be all 'zero', 0.0000...
158 *  if so and the original number is negative,
159 *  do NOT set the '-' sign of our output string.
160 */
161
162if (atmp->m_apm_sign == -1)		/* if input number negative */
163  {
164   kk = 0;
165   jj = 0;
166
167   while (TRUE)
168     {
169      ch = cps[kk++];
170      if ((ch == '\0') || (jj != 0))
171        break;
172
173      if (isdigit((int)ch))
174        {
175	 if (ch != '0')
176	   jj = 1;
177	}
178     }
179
180   if (jj)
181     *cpd++ = '-';
182  }
183
184ct = M_strposition(cps, ".");      /* find the default (.) radix char */
185
186if (ct == -1)			   /* if not found .. */
187  {
188   strcat(cps, ".");               /* add one */
189   ct = M_strposition(cps, ".");   /* and then find it */
190  }
191
192if (places == 0)		   /* int format, terminate at radix char */
193  cps[ct] = '\0';
194else
195  cps[ct] = ch_radix;		   /* assign the radix char */
196
197/*
198 *  if the number is small enough to not have any separator char's ...
199 */
200
201if (ct <= count_sep)
202  no_sep_flg = TRUE;
203
204if (no_sep_flg)
205  {
206   strcpy(cpd, cps);
207  }
208else
209  {
210   jj = 0;
211   kk = count_sep;
212   ii = ct / count_sep;
213
214   if ((ii = ct - ii * count_sep) == 0)
215     ii = count_sep;
216
217   while (TRUE)				/* write out the first 1,2  */
218     {					/* (up to count_sep) digits */
219      *cpd++ = cps[jj++];
220
221      if (--ii == 0)
222        break;
223     }
224
225   while (TRUE)				/* write rest of the string   */
226     {
227      if (kk == count_sep)		/* write a new separator char */
228        {
229	 if (jj != ct)			/* unless we're at the radix  */
230	   {
231            *cpd++ = ch_sep;		/* note that this also disables */
232	    kk = 0;			/* the separator char AFTER     */
233	   }				/* the radix char               */
234	}
235
236      if ((*cpd++ = cps[jj++]) == '\0')
237        break;
238
239      kk++;
240     }
241  }
242
243MAPM_FREE(cps);
244M_restore_stack(1);
245}
246/****************************************************************************/
247void	m_apm_to_fixpt_string(char *ss, int dplaces, M_APM mtmp)
248{
249M_APM   ctmp;
250void	*vp;
251int	places, i2, ii, jj, kk, xp, dl, numb;
252UCHAR   *ucp, numdiv, numrem;
253char	*cpw, *cpd, sbuf[128];
254
255ctmp   = M_get_stack_var();
256vp     = NULL;
257cpd    = ss;
258places = dplaces;
259
260/* just want integer portion if places == 0 */
261
262if (places == 0)
263  {
264   if (mtmp->m_apm_sign >= 0)
265     m_apm_add(ctmp, mtmp, MM_0_5);
266   else
267     m_apm_subtract(ctmp, mtmp, MM_0_5);
268
269   m_apm_to_integer_string(cpd, ctmp);
270
271   M_restore_stack(1);
272   return;
273  }
274
275if (places > 0)
276  M_apm_round_fixpt(ctmp, places, mtmp);
277else
278  m_apm_copy(ctmp, mtmp);	  /* show ALL digits */
279
280if (ctmp->m_apm_sign == 0)        /* result is 0 */
281  {
282   if (places < 0)
283     {
284      cpd[0] = '0';		  /* "0.0" */
285      cpd[1] = '.';
286      cpd[2] = '0';
287      cpd[3] = '\0';
288     }
289   else
290     {
291      memset(cpd, '0', (places + 2));	/* pre-load string with all '0' */
292      cpd[1] = '.';
293      cpd[places + 2] = '\0';
294     }
295
296   M_restore_stack(1);
297   return;
298  }
299
300xp   = ctmp->m_apm_exponent;
301dl   = ctmp->m_apm_datalength;
302numb = (dl + 1) >> 1;
303
304if (places < 0)
305  {
306   if (dl > xp)
307     jj = dl + 16;
308   else
309     jj = xp + 16;
310  }
311else
312  {
313   jj = places + 16;
314
315   if (xp > 0)
316     jj += xp;
317  }
318
319if (jj > 112)
320  {
321   if ((vp = (void *)MAPM_MALLOC((jj + 16) * sizeof(char))) == NULL)
322     {
323      /* fatal, this does not return */
324
325      M_apm_log_error_msg(M_APM_FATAL,
326                          "\'m_apm_to_fixpt_string\', Out of memory");
327     }
328
329   cpw = (char *)vp;
330  }
331else
332  {
333   cpw = sbuf;
334  }
335
336/*
337 *  at this point, the number is non-zero and the the output
338 *  string will contain at least 1 significant digit.
339 */
340
341if (ctmp->m_apm_sign == -1) 	  /* negative number */
342  {
343   *cpd++ = '-';
344  }
345
346ucp = ctmp->m_apm_data;
347ii  = 0;
348
349/* convert MAPM num to ASCII digits and store in working char array */
350
351while (TRUE)
352  {
353   M_get_div_rem_10((int)(*ucp++), &numdiv, &numrem);
354
355   cpw[ii++] = numdiv + '0';
356   cpw[ii++] = numrem + '0';
357
358   if (--numb == 0)
359     break;
360  }
361
362i2 = ii;		/* save for later */
363
364if (places < 0)		/* show ALL digits */
365  {
366   places = dl - xp;
367
368   if (places < 1)
369     places = 1;
370  }
371
372/* pad with trailing zeros if needed */
373
374kk = xp + places + 2 - ii;
375
376if (kk > 0)
377  memset(&cpw[ii], '0', kk);
378
379if (xp > 0)          /* |num| >= 1, NO lead-in "0.nnn" */
380  {
381   ii = xp + places + 1;
382   jj = 0;
383
384   for (kk=0; kk < ii; kk++)
385     {
386      if (kk == xp)
387        cpd[jj++] = '.';
388
389      cpd[jj++] = cpw[kk];
390     }
391
392   cpd[ii] = '\0';
393  }
394else			/* |num| < 1, have lead-in "0.nnn" */
395  {
396   jj = 2 - xp;
397   ii = 2 + places;
398   memset(cpd, '0', (ii + 1));	/* pre-load string with all '0' */
399   cpd[1] = '.';		/* assign decimal point */
400
401   for (kk=0; kk < i2; kk++)
402     {
403      cpd[jj++] = cpw[kk];
404     }
405
406   cpd[ii] = '\0';
407  }
408
409if (vp != NULL)
410  MAPM_FREE(vp);
411
412M_restore_stack(1);
413}
414/****************************************************************************/
415void	M_apm_round_fixpt(M_APM btmp, int places, M_APM atmp)
416{
417int	xp, ii;
418
419xp = atmp->m_apm_exponent;
420ii = xp + places - 1;
421
422M_set_to_zero(btmp); /* assume number is too small so the net result is 0 */
423
424if (ii >= 0)
425  {
426   m_apm_round(btmp, ii, atmp);
427  }
428else
429  {
430   if (ii == -1)	/* next digit is significant which may round up */
431     {
432      if (atmp->m_apm_data[0] >= 50)	/* digit >= 5, round up */
433        {
434         m_apm_copy(btmp, atmp);
435	 btmp->m_apm_data[0] = 10;
436	 btmp->m_apm_exponent += 1;
437	 btmp->m_apm_datalength = 1;
438	 M_apm_normalize(btmp);
439	}
440     }
441  }
442}
443/****************************************************************************/
444
445