home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume20 / hp2pk / part02 / fixnum.c < prev    next >
C/C++ Source or Header  |  1991-06-02  |  5KB  |  199 lines

  1. /*  FIXNUM.C - functions to convert between fixed-pointed numbers and strings
  2.  ***************************************************************************
  3.  *
  4.  *    fixnum str_to_fixnum(str)
  5.  *    char *str;
  6.  *
  7.  *    char *fixnum_to_str(x, buf, numdig)
  8.  *    fixnum x;
  9.  *    char buf[];
  10.  *    int numdig;
  11.  *
  12.  ***************************************************************************
  13.  *    EDIT HISTORY
  14.  *    19-Oct-90    Steve McConnel - finish writing generalized functions
  15.  *                that don't depend on floating point
  16.  *    20-Oct-90    SRMc - remove I/O functions
  17.  ***************************************************************************
  18.  * Copyright 1990 by the Summer Institute of Linguistics, Inc.
  19.  * All rights reserved.
  20.  */
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include "fixnum.h"
  24.  
  25. /************************************************************************/
  26. /*            STATIC GLOBAL VARIABLES                */
  27. /************************************************************************/
  28.  
  29. static unsigned long tenpow[10] = { 0,
  30.     0631463146L,    /* 0.1 */    /* Knuth, vol. 2 (2nd ed.), page 660 */
  31.     0050753412L,    /* 0.01 */   /* 30-bit fractions */
  32.     0004061116L,    /* 0.001 */    /* since we end up with 20-bit */
  33.     0000321556L,    /* 0.0001 */    /* fractions, these should */
  34.     0000024761L,    /* 0.00001 */    /* provide enough precision */
  35.     0000002062L,    /* 0.000001 */
  36.     0000000153L,    /* 0.0000001 */
  37.     0000000013L,    /* 0.00000001 */
  38.     0000000001L        /* 0.000000001 */
  39.     };
  40.  
  41. /****************************************************************************
  42.  * NAME
  43.  *    str_to_fixnum
  44.  * ARGUMENTS
  45.  *    str - string containing fixed-point number
  46.  * DESCRIPTION
  47.  *    Convert ASCII character string to a fixnum binary value.
  48.  * RETURN VALUE
  49.  *    "fixnum" value
  50.  */
  51. fixnum str_to_fixnum(str)
  52. char *str;
  53. {
  54. char *p;        /* pointer into fixed-point number string */
  55. long i;            /* integer part, then entire fixnum value */
  56. unsigned long f;    /* fraction part of fixnum value */
  57. long k;            /* fraction bits thrown away -- useful for rounding */
  58. int neg;        /* flag negative value */
  59. int log;        /* number of decimal digits processed */
  60.  
  61. for ( p = str ; isascii(*p) && isspace(*p) ; ++p )
  62.     ;
  63. if (*p == '-')
  64.     {
  65.     neg = 1;
  66.     ++p;
  67.     }
  68. else
  69.     neg = 0;
  70.  
  71. if ( !isascii(*p) || !isdigit(*p) )
  72.     return( 0L );
  73.  
  74. for ( i = 0 ; isascii(*p) && isdigit(*p) ; ++p)
  75.     i = (i * 10) + (*p - '0');
  76.  
  77. if (i > 2047)
  78.     return( 0x80000000 );    /* overflow condition actually */
  79. i = int_to_fixnum(i);
  80.  
  81. if (*p++ != '.')
  82.     return( (neg) ? -i : i );
  83. /*
  84.  *  convert the fraction part of the number
  85.  */
  86. for ( f = 0, log = 1 ; isascii(*p) && isdigit(*p) && (log < 10) ; ++log )
  87.     f += (*p++ - '0') * tenpow[log];
  88.  
  89. k = f & 0x000003FFL;
  90. f >>= 10;    /* convert from 30-bit fraction to 20-bit fraction */
  91. /*
  92.  *  merge the fraction and the integer parts
  93.  */
  94. i = i | f;
  95. if (k > 0x00000200L)
  96.     ++i;        /* round up the LSB */
  97. return( (neg) ? -i : i );
  98. }
  99.  
  100. /****************************************************************************
  101.  * NAME
  102.  *    fixnum_to_str
  103.  * ARGUMENTS
  104.  *    x      - fixed-point fraction to turn into a string
  105.  *    buf    - character array for holding fixed-point number string
  106.  *    numdig - number of decimal places to expand (buffer must hold at
  107.  *        least 7 more bytes for results like "-2047.xxxxxx\0")
  108.  * DESCRIPTION
  109.  *    Convert a fixnum binary value to an ASCII character string.
  110.  * RETURN VALUE
  111.  *    address of buffer (i.e., buf)
  112.  */
  113. char *fixnum_to_str(x, buf, numdig)
  114. fixnum x;
  115. char buf[];
  116. int numdig;
  117. {
  118. long ix;
  119. char *p;
  120. int j;        /* index into buf[] of last character added */
  121. char tbuf[6];    /* temporary scratch buffer */
  122. int carry;    /* carry flag for rounding up the number string */
  123. char *q;    /* pointer used in shuffling result to add new top digit */
  124. /*
  125.  *  check for a minus sign
  126.  */
  127. j = 0;
  128. if (x < 0L)
  129.     {
  130.     buf[j++] = '-';
  131.     x = -x;
  132.     }
  133. ix = fixnum_to_int(x) & 0x00000FFFL;    /* handle -2048.xxx properly */
  134. /*
  135.  *  work through the integer part of the number
  136.  *  (the digits come out in reverse order with the simplest algorithm)
  137.  */
  138. for ( p = tbuf ; ix ; ix /= 10 )
  139.     *p++ = (ix % 10) + '0';
  140. if (p == tbuf)            /* allow for value == 0 */
  141.     *p++ = '0';
  142. while ( p > tbuf )        /* put the digits in the proper order */
  143.     buf[j++] = (*--p);
  144.  
  145. if (numdig <= 0)
  146.     {
  147.     buf[j] = '\0';        /* no fraction wanted! */
  148.     return(buf);
  149.     }
  150. buf[j++] = '.';
  151. /*
  152.  *  convert the fraction
  153.  */
  154. for ( ix = x & 0x000FFFFFL ; numdig > 0 ; --numdig )
  155.     {
  156.     ix *= 10;
  157.     buf[j++] = fixnum_to_int(ix) + '0';
  158.     ix &= 0x000FFFFFL;
  159.     }
  160. if (ix > 0x00080000L)
  161.     {
  162.     /*
  163.      *  round up.  this can be rather a pain!
  164.      */
  165.     for ( carry = 1, p = &buf[j-1] ; (p >= buf) && carry ; --p )
  166.     {
  167.     if (isdigit(*p))    /* skip over decimal point */
  168.         {
  169.         ++(*p);            /* add the carry bit */
  170.         if (*p > '9')        /* check for overflow */
  171.         {
  172.         *p -= 10;
  173.         carry = 1;
  174.         }
  175.         else
  176.         carry = 0;
  177.         }
  178.     }
  179.     if (carry)
  180.     {
  181.     /*
  182.      *  make room for adding another digit to the integer
  183.      */
  184.     for ( p = &buf[j], q = &buf[j+1] ; p >= buf ; *q-- = *p-- )
  185.         ;
  186.     ++j;            /* add to end-of-string index */
  187.     if (buf[1] == '-')
  188.         {
  189.         buf[0] = '-';
  190.         buf[1] = '1';
  191.         }
  192.     else
  193.         buf[0] = '1';
  194.     }
  195.     }
  196. buf[j] = '\0';
  197. return(buf);
  198. }
  199.