root/src/libs/mapm/mapm_fpf.c

/* 
 *  M_APM  -  mapm_fpf.c
 *
 *  Copyright (C) 2001 - 2007   Michael C. Ring
 *
 *  Permission to use, copy, and distribute this software and its
 *  documentation for any purpose with or without fee is hereby granted,
 *  provided that the above copyright notice appear in all copies and
 *  that both that copyright notice and this permission notice appear
 *  in supporting documentation.
 *
 *  Permission to modify the software is granted. Permission to distribute
 *  the modified code is granted. Modifications are to be distributed by
 *  using the file 'license.txt' as a template to modify the file header.
 *  'license.txt' is available in the official MAPM distribution.
 *
 *  This software is provided "as is" without express or implied warranty.
 */

/*
 *      $Id: mapm_fpf.c,v 1.10 2007/12/03 01:39:57 mike Exp $
 *
 *      This file contains the Fixed Point Formatting functions
 *
 *      $Log: mapm_fpf.c,v $
 *      Revision 1.10  2007/12/03 01:39:57  mike
 *      Update license
 *
 *      Revision 1.9  2003/07/21 20:15:12  mike
 *      Modify error messages to be in a consistent format.
 *
 *      Revision 1.8  2003/03/31 22:11:14  mike
 *      call generic error handling function
 *
 *      Revision 1.7  2002/11/05 23:31:00  mike
 *      use new set_to_zero call instead of copy
 *
 *      Revision 1.6  2002/11/03 22:33:24  mike
 *      Updated function parameters to use the modern style
 *
 *      Revision 1.5  2002/02/14 19:31:44  mike
 *      eliminate need for conditional compile
 *
 *      Revision 1.4  2001/08/26 22:35:50  mike
 *      no LCC conditional needed on fixpt_string
 *
 *      Revision 1.3  2001/08/26 22:11:10  mike
 *      add new 'stringexp' function
 *
 *      Revision 1.2  2001/08/25 22:30:09  mike
 *      fix LCC-WIN32 compile problem
 *
 *      Revision 1.1  2001/08/25 16:50:59  mike
 *      Initial revision
 */

#include "m_apm_lc.h"
#include <ctype.h>

/****************************************************************************/
char    *m_apm_to_fixpt_stringexp(int dplaces, M_APM atmp, 
                                  char ch_radx, char ch_sep, int ct_sep)
{
int     places, xp, dl, ii;
char    *cpr;

places = dplaces;

dl = atmp->m_apm_datalength;
xp = atmp->m_apm_exponent;

if (places < 0)                         /* show ALL digits */
  {
   if (xp < 0)
      ii = dl - xp;
   else
     {
      if (dl > xp)
        ii = dl;
      else
        ii = xp;
     }
  }
else
  {
   ii = places;
      
   if (xp > 0)
     ii += xp;
  }

if (ct_sep != 0 && ch_sep != 0 && xp > 0)
  ii += xp / ct_sep;

if ((cpr = (char *)MAPM_MALLOC((ii + 32) * sizeof(char))) == NULL)
  return(NULL);

m_apm_to_fixpt_stringex(cpr,places,atmp,ch_radx,ch_sep,ct_sep);

return(cpr);
}
/****************************************************************************/
void    m_apm_to_fixpt_stringex(char *s, int dplaces, M_APM atmp, 
                                char ch_radix, char ch_sep, int count_sep)
{
M_APM   btmp;
char    ch, *cpd, *cps;
int     ii, jj, kk, ct, dl, xp, no_sep_flg, places;

btmp       = M_get_stack_var();
places     = dplaces;
cpd        = s;
no_sep_flg = FALSE;

m_apm_absolute_value(btmp, atmp);       /* do conversion of positive number */

if (ch_sep == 0 || count_sep == 0)      /* no separator char OR count */
  no_sep_flg = TRUE;

/* determine how much memory to get for the temp string */

dl = btmp->m_apm_datalength;
xp = btmp->m_apm_exponent;

if (places < 0)                         /* show ALL digits */
  {
   if (xp < 0)
      ii = dl - xp;
   else
     {
      if (dl > xp)
        ii = dl;
      else
        ii = xp;
     }
  }
else
  {
   ii = places;
      
   if (xp > 0)
     ii += xp;
  }

if ((cps = (char *)MAPM_MALLOC((ii + 32) * sizeof(char))) == NULL)
  {
   /* fatal, this does not return */

   M_apm_log_error_msg(M_APM_FATAL, 
                       "\'m_apm_to_fixpt_stringex\', Out of memory");
  }

m_apm_to_fixpt_string(cps, places, btmp);

/*
 *  the converted string may be all 'zero', 0.0000...
 *  if so and the original number is negative,
 *  do NOT set the '-' sign of our output string.
 */

if (atmp->m_apm_sign == -1)             /* if input number negative */
  {
   kk = 0;
   jj = 0;

   while (TRUE)
     {
      ch = cps[kk++];
      if ((ch == '\0') || (jj != 0))
        break;

      if (isdigit((int)ch))
        {
         if (ch != '0')
           jj = 1;
        }
     }

   if (jj)
     *cpd++ = '-';
  }

ct = M_strposition(cps, ".");      /* find the default (.) radix char */

if (ct == -1)                      /* if not found .. */
  {
   strcat(cps, ".");               /* add one */
   ct = M_strposition(cps, ".");   /* and then find it */
  }

if (places == 0)                   /* int format, terminate at radix char */
  cps[ct] = '\0';
else
  cps[ct] = ch_radix;              /* assign the radix char */

/*
 *  if the number is small enough to not have any separator char's ...
 */

if (ct <= count_sep)
  no_sep_flg = TRUE;

if (no_sep_flg)
  {
   strcpy(cpd, cps);
  }
else
  {
   jj = 0;
   kk = count_sep;
   ii = ct / count_sep;

   if ((ii = ct - ii * count_sep) == 0)
     ii = count_sep;

   while (TRUE)                         /* write out the first 1,2  */
     {                                  /* (up to count_sep) digits */
      *cpd++ = cps[jj++];

      if (--ii == 0)
        break;
     }

   while (TRUE)                         /* write rest of the string   */
     {
      if (kk == count_sep)              /* write a new separator char */
        {
         if (jj != ct)                  /* unless we're at the radix  */
           {
            *cpd++ = ch_sep;            /* note that this also disables */
            kk = 0;                     /* the separator char AFTER     */
           }                            /* the radix char               */
        }

      if ((*cpd++ = cps[jj++]) == '\0')
        break;

      kk++;
     }
  }

MAPM_FREE(cps);
M_restore_stack(1);
}
/****************************************************************************/
void    m_apm_to_fixpt_string(char *ss, int dplaces, M_APM mtmp)
{
M_APM   ctmp;
void    *vp;
int     places, i2, ii, jj, kk, xp, dl, numb;
UCHAR   *ucp, numdiv, numrem;
char    *cpw, *cpd, sbuf[128];

ctmp   = M_get_stack_var();
vp     = NULL;
cpd    = ss;
places = dplaces;

/* just want integer portion if places == 0 */

if (places == 0)
  {
   if (mtmp->m_apm_sign >= 0)
     m_apm_add(ctmp, mtmp, MM_0_5);
   else
     m_apm_subtract(ctmp, mtmp, MM_0_5);

   m_apm_to_integer_string(cpd, ctmp);

   M_restore_stack(1);
   return;
  }

if (places > 0)
  M_apm_round_fixpt(ctmp, places, mtmp);
else
  m_apm_copy(ctmp, mtmp);         /* show ALL digits */

if (ctmp->m_apm_sign == 0)        /* result is 0 */
  {
   if (places < 0)
     {
      cpd[0] = '0';               /* "0.0" */
      cpd[1] = '.';
      cpd[2] = '0';
      cpd[3] = '\0';
     }
   else
     {
      memset(cpd, '0', (places + 2));   /* pre-load string with all '0' */
      cpd[1] = '.';
      cpd[places + 2] = '\0';
     }

   M_restore_stack(1);
   return;
  }

xp   = ctmp->m_apm_exponent;
dl   = ctmp->m_apm_datalength;
numb = (dl + 1) >> 1;

if (places < 0)
  {
   if (dl > xp)
     jj = dl + 16;
   else
     jj = xp + 16;
  }
else
  {
   jj = places + 16;
   
   if (xp > 0)
     jj += xp;
  }

if (jj > 112)
  {
   if ((vp = (void *)MAPM_MALLOC((jj + 16) * sizeof(char))) == NULL)
     {
      /* fatal, this does not return */

      M_apm_log_error_msg(M_APM_FATAL, 
                          "\'m_apm_to_fixpt_string\', Out of memory");
     }

   cpw = (char *)vp;
  }
else
  {
   cpw = sbuf;
  }

/*
 *  at this point, the number is non-zero and the the output
 *  string will contain at least 1 significant digit.
 */

if (ctmp->m_apm_sign == -1)       /* negative number */
  {
   *cpd++ = '-';
  }

ucp = ctmp->m_apm_data;
ii  = 0;

/* convert MAPM num to ASCII digits and store in working char array */

while (TRUE)
  {
   M_get_div_rem_10((int)(*ucp++), &numdiv, &numrem);

   cpw[ii++] = numdiv + '0';
   cpw[ii++] = numrem + '0';

   if (--numb == 0)
     break;
  }

i2 = ii;                /* save for later */

if (places < 0)         /* show ALL digits */
  {
   places = dl - xp;

   if (places < 1)
     places = 1;
  }

/* pad with trailing zeros if needed */

kk = xp + places + 2 - ii;

if (kk > 0)
  memset(&cpw[ii], '0', kk);

if (xp > 0)          /* |num| >= 1, NO lead-in "0.nnn" */
  {
   ii = xp + places + 1;
   jj = 0;

   for (kk=0; kk < ii; kk++)
     {
      if (kk == xp)
        cpd[jj++] = '.';

      cpd[jj++] = cpw[kk];
     }

   cpd[ii] = '\0';
  }
else                    /* |num| < 1, have lead-in "0.nnn" */
  {
   jj = 2 - xp;
   ii = 2 + places;
   memset(cpd, '0', (ii + 1));  /* pre-load string with all '0' */
   cpd[1] = '.';                /* assign decimal point */

   for (kk=0; kk < i2; kk++)
     {
      cpd[jj++] = cpw[kk];
     }

   cpd[ii] = '\0';
  }

if (vp != NULL)
  MAPM_FREE(vp);

M_restore_stack(1);
}
/****************************************************************************/
void    M_apm_round_fixpt(M_APM btmp, int places, M_APM atmp)
{
int     xp, ii;

xp = atmp->m_apm_exponent;
ii = xp + places - 1;

M_set_to_zero(btmp); /* assume number is too small so the net result is 0 */

if (ii >= 0)
  {
   m_apm_round(btmp, ii, atmp);
  }
else
  {
   if (ii == -1)        /* next digit is significant which may round up */
     {
      if (atmp->m_apm_data[0] >= 50)    /* digit >= 5, round up */
        {
         m_apm_copy(btmp, atmp);
         btmp->m_apm_data[0] = 10;
         btmp->m_apm_exponent += 1;
         btmp->m_apm_datalength = 1;
         M_apm_normalize(btmp);
        }
     }
  }
}
/****************************************************************************/