.CM  SCRIPT , Version - 1.1 , last edited by peter
.ad 8
.bm 8
.fm 4
.bt $Copyright (c) 1992-2004 SAP AG$$Page %$
.tm 12
.hm 6
.hs 3
.TT 1 $SQL$Project Distributed Database System$VSP46C$
.tt 2 $$$
.TT 3 $$In-memory BCD conversions$1994-12-12$
***********************************************************
.nf

.nf


    ========== licence begin  GPL
    Copyright (c) 1992-2004 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end

.fo


.fo
.nf
.sp
MODULE  : In-memory BCD conversions
=========
.sp
Purpose : Conversion between character and binary coded decimal
.CM *-END-* purpose -------------------------------------
.sp
.cp 3
Define  :
 
void s46stodc (char*, int, int, const char*, int, int, char*);
void s46dctos (const char*, int, int, char*, int, int, int*,
                     char*);
 
.CM *-END-* define --------------------------------------
.sp;.cp 3
Use     :
 
.CM *-END-* use -----------------------------------------
.sp;.cp 3
Synonym :
 
.CM *-END-* synonym -------------------------------------
.sp;.cp 3
Author  : 
.sp
.cp 3
Created : 1992-11-13
.sp
.cp 3
Version : 1992-11-26
.sp
.cp 3
Release :  3.1.2       Date : 1993-06-18
.sp
***********************************************************
.sp
.cp 10
.fo
.oc _/1
Specification:
.sp;.nf
void s46dctos (val, prec, scal, buf, pos, len, rlen, res)
        unsigned char *val;
        int prec, scal;
        char *buf;
        int pos, len;
        int *rlen;
        char *res;
 
Conversion from a binary coded decimal number to its character
representation.
 
Parameters:
 
Input:
        val - buffer address of the number to be converted
        prec- decimal precision
        scal- decimal scale, i.e. the number of digits following the
              decimal point
        buf - char buffer address for output of the resulting string
        pos - byte count (pos = 1 means buf [0]) as starting position
              of the string
        len - output field length, the output string is stored left
              justified between buf [pos - 1] and buf [pos + len - 2]
 
Output:
        rlen- number of characters resulting from the conversion,
              *rlen = len means success,
              *rlen < 0 indicates a conversion error, buf is left
                      unchanged, *res is set to
                      num_invalid,
              if *rlen > len, digits from the fraction part are
                      stripped off. If the resulting number fits
                      into the output field, its rounded value is
                      stored and *res is set to num_trunc, else
                      buf is left unchanged and *res is set
                      to num_overflow
        res - return status num_ok, num_invalid, num_trunc or
              num_overflow
 
void s46stodc (val, prec, scal, buf, pos, len, res)
        unsigned char *val;
        int prec, scal;
        char *buf;
        int pos, len;
        char *res;
 
Conversion from a character string representing a decimal number
with whole and optional fractional part (but without exponential
part) to its corresponding binary coded decimal representation.
 
Parameters:
 
Input:
        val - buffer address of the converted number
        prec- decimal precision
        scal- decimal scale, i.e. the number of digits following the
              decimal point
        buf - character buffer address holding the input string
        pos - byte count (pos = 1 means buf [0]) as starting position
              of the string
        len - maximum field length, the input string is stored
              between buf [pos - 1] and buf [pos + len - 2] with
              possibly preceding and trailing whitespace characters
 
Output:
        res - return status, num_ok indicates successful completion,
              num_trunc truncation (a rounded value is stored),
              num_overflow an overflow condition and num_invalid a
              conversion error
 
.CM *-END-* specification -------------------------------
.sp 2
***********************************************************
.sp
.cp 10
.fo
.oc _/1
Description:
.CM *-END-* description ---------------------------------
.sp 2
***********************************************************
.sp
.cp 10
.nf
.oc _/1
Structure:
 
.CM *-END-* structure -----------------------------------
.sp 2
**********************************************************
.sp
.cp 10
.nf
.oc _/1
.CM -lll-
Code    :
/*PRETTY*/
 
/* exported functions */
 
void s46dctos ();
void s46stodc ();
 
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <ctype.h>
 
static unsigned char dmask [] = {0x0f, 0xf0};
static unsigned char dlog2 [] = {4, 0};
 
/* function definitions */
 
static unsigned char s46xdig (val, i, prec)
/* Extract the coefficient of the i-th power of 10 from bcd val of
   precision prec and scale 0 */
 
        unsigned char *val;
        int i;
        int prec;
{
        int j, k;
 
        j = prec/2 - (i + 1)/2;
        k = (i + 2) % 2;
        return (val [j] >> dlog2 [k]) & dmask [0];
}
 
static void s46rdig (val, i, prec, d)
/* Replace the coefficient of the i-th power of 10 from bcd val of
   precision prec and scale 0 with d */
 
        unsigned char *val;
        int i;
        int prec;
        unsigned char d;
{
        int j, k;
        j = prec/2 - (i + 1)/2;
        k = (i + 2) % 2;
        val [j] = (val [j] & dmask [k]) | (d << dlog2 [k]);
}
 
static void s46sdig (val, prec, d)
/* Shift the bcd val of precision prec left by one decimal position
   and replace the least significant digit with d, loosely spoken:
   "shift in the decimal digit d to the rigth of bcd val" */
 
        unsigned char *val;
        int prec;
        unsigned char d;
{
        int i, n = prec/2;
 
        for (i = 0; i < n; i++)
                val [i] = (val [i] << 4) | ((val [i + 1] >> 4) & dmask [0]);
 
        val [n] = (val [n] & dmask [0]) | (d << 4);
}
 
void s46dctos (val, prec, scal, buf, pos, len, rlen, res)
        unsigned char *val;
        int prec, scal;
        char *buf;
        int pos, len;
        int *rlen;
        char *res;
{
        int i, n = prec/2, frac = 0, trunc, dpt;
        char *cdig = buf + pos + len - 2, *cbuf = buf + pos - 1,
             *csig = "";
        unsigned char d, r = 0, *eval = val + n;
 
        if (prec <= 0)
        {
                *res = 3; /* num_invalid */
                return;
        }
        else
                *res = 0; /* num_ok */
 
        /* eliminate leading zeros from the bcd number */
        while ((val < eval) ? (*val == 0) : 0)
        {
                val++;
                n--;
                prec = 2*n + 1;
        }
        if (n > 0 && (*val & dmask [1]) == 0)
                prec = 2*n;
 
        /* compute output length */
        if (prec > scal)
                if (scal > 0)
                {
                        *rlen = prec + 1;
                        frac = scal;
                }
                else if (scal < 0)
                        *rlen = prec - scal;
                else
                        *rlen = prec;
        else
        {
                *rlen = scal + 2;
                frac = prec;
        }
        dpt = frac > 0;
        d = s46xdig (val, -1, prec); /* extract sign */
 
        if (d != 0x0f && d % 2)
        {
                (*rlen)++;
                csig = "-";
        }
        if ((trunc = *rlen - len) > 0)
        {
                if (trunc > frac)
                {
                        *res = 2; /* num_overflow */
                        return;
                }
                else
                {
                        /* skip truncated digits */
                        d = 0;
 
                        for (i = 0; i < trunc; i++)
                        {
                                if (r == 0) r = d;
 
                                if ((d = s46xdig (val, i, prec)) > 9)
                                {
                                        *res = 3; /* num_invalid */
                                        return;
                                }
                        }
                        if (r || d) *res = 1; /* num_trunc */
 
                        r = (d != 5 || r == 0) ? d : 6;
                        if (i == scal)
                        {
                                *cdig-- = '.';
                                dpt = 0;
                        }
                        if (i < prec)
                        {
                                if ((d = s46xdig (val, i, prec)) > 9)
                                {
                                        *res = 3; /* num_invalid */
                                        return;
                                }
                        }
                        else
                                d = 0;
                        i++;
                        /* round the rightmost output digit */
                        r = (r > 5 || (r == 5 && d % 2)) ? d + 1 : d;
                        *cdig-- = r + (unsigned char) '0';
                }
        }
        else
        {
                /* output trailing spaces */
                while (trunc++)
                        *cdig-- = ' ';
                /* output trailing zeros */
                for (i = (scal < 0) ? -scal : 0; i > 0; i--) *cdig-- = '0';
        }
        /* output the fractional part */
        for (; i < frac; i++)
        {
                if ((d = s46xdig (val, i, prec)) > 9)
                {
                        *res = 3; /* num_invalid */
                        return;
                }
                *cdig-- = d + (unsigned char) '0';
        }
        /* output leading zeros of the fractional part */
        for (; i < scal; i++) *cdig-- = '0';
 
        if (dpt) *cdig-- = '.';
        /* output the whole part */
        for (; i < prec; i++)
        {
                if ((d = s46xdig (val, i, prec)) > 9)
                {
                        *res = 3; /* num_invalid */
                        return;
                }
                *cdig-- = d + (unsigned char) '0';
        }
        /* if the whole part is zero, output a leading zero */
        if (scal >= prec) *cdig-- = '0';
        /* output the '-' sign */
        if (*csig) *cdig-- = *csig;
}
 
void s46stodc (val, prec, scal, buf, pos, len, res)
        unsigned char *val;
        int prec, scal;
        char *buf;
        int pos, len;
        char *res;
{
        char *cdig = buf + pos - 1, *cbuf = buf + pos + len - 2;
        unsigned char d, r;
        int i, n = prec/2,
        zl = scal - prec,
        zt = (scal < 0) ? -scal : 0,
        dl = (scal > 0) ? -zl : prec,
        dt = (zl > 0) ? prec : scal;
 
        /* skip leading spaces */
        while ((cdig <= cbuf) ? isspace (*cdig) : 0) cdig++;
        /* skip trailing spaces */
        while ((cbuf >= cdig) ? isspace (*cbuf) : 0) cbuf--;
 
        if (prec <= 0 || cdig > cbuf)
        {
                *res = 3; /* num_invalid */
                return;
        }
        /* skip trailing zeros for negative scale */
        else if (zt)
        {
                *res = 0; /* num_ok */
 
                while ((cbuf >= cdig) ? zt-- : 0)
                        if (*cbuf-- != '0')
                        {
                                *res = 3; /* num_invalid */
                                return;
                        }
                if (cdig > cbuf) cbuf++;
        }
        else
                *res = 0; /* num_ok */
 
        for (i = 0; i < n; i++) val [i] = 0;
 
        /* compute the sign byte */
        if (*cdig == '-')
        {
                val [n] = 0x0d;
                cdig++;
        }
        else
        {
                val [n] = 0x0c;
 
                if (*cdig == '+') cdig++;
        }
        if (cdig > cbuf)
        {
                *res = 3; /* num_invalid */
                return;
        }
        /* skip leading zeros */
        while ((cdig <= cbuf) ? (*cdig == '0') : 0) cdig++;
 
        if (zl > 0)
                /* scale greater than precision */
                if ((cdig <= cbuf) ? (*cdig++ == '.') : 1)
                        for (i = 0; i < zl; i++)
                        {
                                if ((cdig <= cbuf) ? (*cdig++ != '0') : 0)
                                {
                                        *res = 3; /* num_invalid */
                                        i = zl;
                                }
                        }
                else
                        *res = 3; /* num_invalid */
        else
        {
                /* extract the whole part */
                for (i = 0; (cdig <= cbuf) ? isdigit (*cdig) : 0; i++)
                {
                        d = (unsigned char) *cdig++ - (unsigned char) '0';
                        s46sdig (val, prec, d);
                }
                if (i > dl)
                        *res = 2; /* num_overflow */
                else if ((cdig <= cbuf) ? (*cdig == '.') : 0)
                {
                        if (scal < 0)
                                *res = 3; /* num_invalid */
                        cdig++;
                }
                else if (cdig <= cbuf)
                        *res = 3; /* num_invalid */
        }
        if (*res != 0 /*num_ok */ ) return;
 
        /* extract the fractional part */
        while ((cdig <= cbuf) ? (dt-- > 0) : 0)
                if (isdigit (*cdig))
                {
                        d = (unsigned char) *cdig++ - (unsigned char) '0';
                        s46sdig (val, prec, d);
                }
                else
                {
                        *res = 3; /* num_invalid */
                        return;
                }
        /* shift in 0's, if there are less fractional digits than required */
        while (dt-- > 0) s46sdig (val, prec, 0);
 
        if (cdig <= cbuf)
        {
                if (isdigit (*cdig))
                {
                        /* truncate fractional digits and decide rounding */
                        r = (unsigned char) *cdig++ - (unsigned char) '0';
 
                        while (cdig <= cbuf)
                                if (isdigit (*cdig))
                                {
                                        if (r == 5 && *cdig++ != '0')
                                                r = 6;
                                }
                                else
                                {
                                        *res = 3; /* num_invalid */
                                        return;
                                }
                }
                else
                {
                        *res = 3; /* num_invalid */
                        return;
                }
                *res = 1; /* num_trunc */
                d = s46xdig (val, 0, prec);
                /* round the least significant digit */
                if (r > 5 || (r == 5 && d % 2)) s46rdig (val, 0, prec, d + 1);
        }
}
 
.CM *-END-* code ----------------------------------------
.SP 2 
***********************************************************
*-PRETTY-*  statements    :        345
*-PRETTY-*  lines of code :        345        PRETTY  3.09 
*-PRETTY-*  lines in file :        500         1992-11-23 
.PA 
