home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / cfuncs / lgconv.c < prev    next >
C/C++ Source or Header  |  2000-07-29  |  5KB  |  182 lines

  1. /*
  2. ############################################################################
  3. #
  4. #    File:     lgconv.c
  5. #
  6. #    Subject:  Function to convert large integer to string
  7. #
  8. #    Author:   Gregg M. Townsend
  9. #
  10. #    Date:     November 17, 1997
  11. #
  12. ############################################################################
  13. #
  14. #   This file is in the public domain.
  15. #
  16. ############################################################################
  17. #
  18. #  lgconv(I) converts a large integer into a string using a series of BCD
  19. #  adds.  (In contrast, the Icon built-in string() function accomplishes
  20. #  the same conversion using a series of divisions by 10.)
  21. #  lgconv is typically 50% to 75% faster than string() on a Sun or Alpha.
  22. #  For some reason it is as much as 125% SLOWER on a SGI 4/380.
  23. #  lgconv(I) works for all integer values of I.  Small integers are
  24. #  simply passed to string() for conversion.
  25. #
  26. ############################################################################
  27. #
  28. #  Requires:  Dynamic loading
  29. #
  30. ############################################################################
  31. */
  32.  
  33. #include "icall.h"
  34. #include <math.h>
  35. #include <string.h>
  36.  
  37. static void bcdadd(unsigned long lside[], unsigned long rside[], int n);
  38.  
  39.  
  40.  
  41. /* definitions copied from Icon source code */
  42.  
  43. typedef unsigned int DIGIT;
  44. #define NB (WordSize / 2)    /* bits per digit */
  45. #define B ((word)1 << NB)    /* bignum radix */
  46.  
  47. struct b_bignum {        /* large integer block */
  48.    word title;            /*   T_Lrgint */
  49.    word blksize;        /*   block size */
  50.    word msd, lsd;        /*   most and least significant digits */
  51.    int sign;            /*   sign; 0 positive, 1 negative */
  52.    DIGIT digits[1];        /*   digits */
  53.    };
  54.  
  55.  
  56.  
  57.  
  58. int lgconv(argc, argv)        /*: convert large integer to string */
  59. int argc;
  60. descriptor *argv;
  61.    {
  62. #define BCDIGITS (2 * sizeof(long))    /* BCD digits per long */
  63.    int nbig, ndec, nbcd, nchr, bcdlen, i, j, n, t;
  64.    char tbuf[25], *o, *p;
  65.    struct b_bignum *big;
  66.    DIGIT d, *dgp;
  67.    char *out;
  68.    unsigned long b, *bp, *bcdbuf, *powbuf, *totbuf;
  69.  
  70.    t = IconType(argv[1]);
  71.    if (t != 'I') {                /* if not large integer */
  72.       ArgInteger(1);                /* must be a small one */
  73.       sprintf(tbuf, "%ld", IntegerVal(argv[1]));
  74.       RetString(tbuf);
  75.       }
  76.  
  77.    big = (struct b_bignum *) argv[1].vword;    /* pointer to bignum struct */
  78.    nbig = big->lsd - big->msd + 1;        /* number of bignum digits */
  79.    ndec = nbig * NB * 0.3010299956639812 + 1;    /* number of decimal digits */
  80.    nbcd = ndec / BCDIGITS + 1;            /* number of BCD longs */
  81.  
  82.    /* allocate string space for computation and output */
  83.    nchr = sizeof(long) * (2 * nbcd + 1);
  84.    out = alcstr(NULL, nchr);
  85.    if (!out)
  86.       Error(306);
  87.  
  88.    /* round up for proper alignment so we can overlay longword buffers */
  89.    n = sizeof(long) - (long)out % sizeof(long);    /* adjustment needed */
  90.    out += n;                    /* increment address */
  91.    nchr -= n;                    /* decrement length */
  92.  
  93.    /* allocate computation buffers to overlay output string */
  94.    bcdbuf = (unsigned long *) out;        /* BCD buffer area */
  95.    bcdlen = 1;                    /* start with just one BCD wd */
  96.    totbuf = bcdbuf + nbcd - bcdlen;        /* BCD version of bignum */
  97.    powbuf = totbuf + nbcd;            /* BCD powers of two */
  98.  
  99.    memset(bcdbuf, 0, 2 * nbcd * sizeof(long));    /* zero BCD buffers */
  100.    powbuf[bcdlen-1] = 1;            /* init powbuf to 1 */
  101.    
  102.    /* compute BCD equivalent of the bignum value */
  103.    dgp = &big->digits[big->lsd];
  104.    for (i = 0; i < nbig; i++) {
  105.       d = *dgp--;
  106.       for (j = NB; j; j--) {
  107.      if (d & 1)                /* if bit is set in bignum */
  108.         bcdadd(totbuf, powbuf, bcdlen);    /* add 2^n to totbuf */
  109.      d >>= 1;
  110.      bcdadd(powbuf, powbuf, bcdlen);    /* double BCD power-of-two */
  111.      if (*powbuf >= (5LU << (WordSize-4))) {/* if too big to add */
  112.         bcdlen += 1;            /* grow buffers */
  113.         powbuf -= 1;
  114.         totbuf -= 1;
  115.         }
  116.      }
  117.       }
  118.  
  119.    /* convert BCD to decimal characters */
  120.    o = p = out + nchr;
  121.    bp = totbuf + bcdlen;
  122.    for (i = 0; i < bcdlen; i++) {
  123.       b = *--bp;
  124.       for (j = 0; j < BCDIGITS; j++) {
  125.      *--o = (b & 0xF) + '0';
  126.      b >>= 4;
  127.      }
  128.       }
  129.  
  130.    /* trim leading zeroes, add sign, and return value */
  131.    while (*o == '0' && o < p - 1)
  132.       o++;
  133.    if (big->sign)
  134.       *--o = '-';
  135.    RetAlcString(o, p - o); 
  136.    }
  137.  
  138.  
  139.  
  140. /*
  141.  * bcdadd(lside,rside,n) -- compute lside += rside for n BCD longwords
  142.  *
  143.  * lside and rside are arrays of n unsigned longs holding BCD values,
  144.  * with MSB in the first longword.  rside is added into lside in place.
  145.  */
  146.  
  147. static void bcdadd(unsigned long lside[], unsigned long rside[], int n)
  148. {
  149. #define CSHIFT (WordSize - 4)
  150. #if WordSize == 64
  151. #define BIAS 0x6666666666666666u
  152. #define MASK 0xF0F0F0F0F0F0F0F0u
  153. #else
  154. #define BIAS 0x66666666u
  155. #define MASK 0xF0F0F0F0u
  156. #endif
  157.    unsigned long lword, rword, low, hgh, carry, icarry;
  158.  
  159.    lside += n;
  160.    rside += n;
  161.    carry = 0;
  162.  
  163.    while (n--) {
  164.       lword = *--lside + BIAS;
  165.       rword = *--rside + carry;
  166.       hgh = (lword & MASK) + (rword & MASK);
  167.       low = (lword & ~MASK) + (rword & ~MASK);
  168.       while (icarry = (hgh & ~MASK) + (low & MASK)) {
  169.      hgh &= MASK;
  170.      low &= ~MASK;
  171.      carry |= icarry;
  172.      icarry = 0x16 * (icarry >> 4);
  173.      hgh += icarry & MASK;
  174.      low += icarry & ~MASK;
  175.      }
  176.       carry = ((lword >> CSHIFT) + (rword >> CSHIFT) + (carry >> CSHIFT)) >> 4;
  177.       *lside = hgh  + low + ((6 * carry) << CSHIFT) - BIAS;
  178.       }
  179. }
  180.