home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / deccvt.zip / deccvt.c
Text File  |  1993-12-28  |  16KB  |  183 lines

  1. /***************************************************************************************************/
  2. /*                                                                                                 */
  3. /* This is a sample routine which converts the DBM and DB2/2 type DECIMAL to a character string.   */
  4. /* Input is a pointer to the sqlvar structure in the sqlda; the data type must be SQL_TYP_DECIMAL  */
  5. /* (484) or SQL_TYP_NDECIMAL (485), but is not checked.  The output will be placed in the passed   */
  6. /* string, which is not length checked.                                                            */
  7. /*                                                                                                 */
  8. /* DECIMAL strings have the format 0x1234567c for the value 1234567.  There are two decimal digits */
  9. /* per byte, and the last nibble is the sign nibble - 0x0b or 0x0b for negative, otherwise it is   */
  10. /* positive.                                                                                       */
  11. /*                                                                                                 */
  12. /* The length and precision is returned in the sqlvar->sqllen field, in the format DDLL, where D   */
  13. /* is the number of digits to the right of the decimal point, and LL is the total length of the    */
  14. /* field.                                                                                          */
  15. /*                                                                                                 */
  16. /* This routine left adjusts the number in the field, with leading zeros replaced by blanks.  The  */
  17. /* sign is floating; it is placed immediately before the first digit of the number if the number   */
  18. /* is negative.  If the number is positive, no sign is placed.  If the number in the range of      */
  19. /* 1 > number > -1, a 0 is inserted before the decimal point.                                      */
  20. /*                                                                                                 */
  21. /* The operation of the routine is as follows: The end of the input is determined and the sign     */
  22. /* nibble checked for 0x0b or 0X0d and bNegativeNumber is set to TRUE if the number is negative,   */
  23. /* and FALSE otherwise.  The location of the decimal point is set into sPointCtr, and the leading  */
  24. /* blanks flag bLeadingBlanks is initialized to TRUE.                                              */
  25. /*                                                                                                 */
  26. /* A loop is then entered to check each nibble of the input number.  On the even numbered count,   */
  27. /* the high nibble is used; on the odd number counts, the low nibble is used and the input pointer */
  28. /* pInputPos is incremented to the next byte.  The last nibble (the sign bits) is not checked.     */
  29. /*                                                                                                 */
  30. /* In the loop, we check to see if we are at the decimal point.  If so, and we are still inserting */
  31. /* leading blanks, a '-' or a blank is added to the string for the sign.  The current digit is     */
  32. /* unconditionally inserted, followed by the decimal point.  Finally, bLeadingBlank is turned off  */
  33. /* to force all other digits to be inserted.                                                       */
  34. /*                                                                                                 */
  35. /* If we are not at the decimal point location, and bLeadingBlanks is TRUE, we check the current   */
  36. /* digit.  If zero, we insert a space and continue.  If non-zero, we insert the sign (as above)    */
  37. /* and the digit, and set bLeadingBlanks to FALSE.  If bLeadingBlanks is FALSE, the digit is       */
  38. /* inserted unconditionally.  In all cases, digit insertion consists of adding '0' (character 0)   */
  39. /* to the digit before inserting into the string, and incrementing the string pointer following    */
  40. /* the insertion.                                                                                  */
  41. /*                                                                                                 */
  42. /* After completion of the loop, a null character is added to terminate the output string.         */
  43. /*                                                                                                 */
  44. /*                                                                                                 */
  45. /* This is example code I have used for courses in DB2/2, and has not been thoroughly checked for  */
  46. /* errors.  No warrenties are implied, but it has worked for the fields I have tested.             */
  47. /*                                                                                                 */
  48. /* Copyright (C) 1993 by JDS & Associates, Raleigh, NC.  CIS ID: 70363,407                         */
  49. /*                                                                                                 */
  50. /***************************************************************************************************/
  51.  
  52. /* Macro definitions */
  53. #define HINIBBLE(x) (((x) & 0XF0) >> 4)                           /* Return high nibble of a byte  */
  54. #define LONIBBLE(x) ((x) & 0X0F)                                  /* Return low nibble of a byte   */
  55.  
  56. char * DecimalToChar (struct sqlvar * sqlvar, PCHAR pOutput)
  57. {
  58.    PCHAR PInputPos = sqlvar->sqldata;                             /* Point at the data area        */
  59.    PCHAR pOutputPos = pOutput;                                    /* Working pointer for output    */
  60.    SHORT sDigitCount = LOBYTE(sqlvar->sqllen);                    /* Total number of digits        */
  61.    SHORT sPointCtr = sDigitCount - HIBYTE(sqlvar->sqllen);        /* Offset of decimal point       */
  62.    SHORT sDigit =                                                 /* One digit from input          */
  63.          LONIBBLE(*(PInputPos + ((sDigitCount + 1) / 2) - 1));    /* Sign character here           */
  64.    BOOL  bLeadingBlanks = TRUE;                                   /* Blanks instead of leading 0's */
  65.    BOOL  bNegativeNumber;                                         /* Get sign nibble               */
  66.    INT i;                                                         /* Loop control                  */
  67.  
  68.    bNegativeNumber = (sDigit == 0X0B) || (sDigit == 0X0D);        /* < 0 if sign = 0x0b or 0x0d    */
  69.    pOutputPos[0] = '\0';                                          /* Initialize string to empty    */
  70.  
  71.    for (i = 0; i < (sDigitCount-1); i++)                          /* Loop through each except last */
  72.    {
  73.       sDigit = i % 2 ?                                            /* Even/Odd time through test    */
  74.          LONIBBLE(*(PInputPos++)) : HINIBBLE(*PInputPos);         /* hi/low digit in byte          */
  75.       if (--sPointCtr == 0)                                       /* If at decimal point           */
  76.       {
  77.          if (bLeadingBlanks)                                      /* If we still have leading zeros*/
  78.             *(pOutputPos++) = bNegativeNumber ? '-' : ' ';        /* Insert '-' if < 0, ' ' if > 0 */
  79.          *(pOutputPos++) = sDigit + '0';                          /* Insert leading zero or digit  */
  80.          *(pOutputPos++) = '.';                                   /* Insert the decimal point      */
  81.          bLeadingBlanks = FALSE;                                  /* No more leading blanks        */
  82.       }
  83.       else
  84.          if (bLeadingBlanks)                                      /* Suppressing leading blanks?   */
  85.             if (sDigit)                                           /* If this digit is non-zero     */
  86.             {
  87.                bLeadingBlanks = FALSE;                            /* No more leading blanks        */
  88.                *(pOutputPos++) = bNegativeNumber ? '-' : ' ';     /* Insert '-' if < 0, ' ' if > 0 */
  89.                *(pOutputPos++) = sDigit + '0';                    /* Insert the character          */
  90.             }
  91.             else                                                  /* Still supressing leading zeros*/
  92.                *(pOutputPos++) = ' ';                             /* Insert a blank                */
  93.          else
  94.             *(pOutputPos++) = sDigit + '0';                       /* Add in the digit              */
  95.    }
  96.    *pOutputPos = '\0';
  97.    return pOutput;
  98. }
  99.  
  100. /***************************************************************************************************/
  101. /*                                                                                                 */
  102. /* This is a sample routine which converts the DBM and DB2/2 type DECIMAL to a long double.        */
  103. /* Input is a pointer to the sqlvar structure in the sqlda; the data type must be SQL_TYP_DECIMAL  */
  104. /* (484) or SQL_TYP_NDECIMAL (485), but is not checked.  The output is a long double type and is   */
  105. /* returned by the function directly.                                                              */
  106. /*                                                                                                 */
  107. /* DECIMAL strings have the format 0x1234567c for the value 1234567.  There are two decimal digits */
  108. /* per byte, and the last nibble is the sign nibble - 0x0b or 0x0b for negative, otherwise it is   */
  109. /* positive.                                                                                       */
  110. /*                                                                                                 */
  111. /* The length and precision is returned in the sqlvar->sqllen field, in the format DDLL, where D   */
  112. /* is the number of digits to the right of the decimal point, and LL is the total length of the    */
  113. /* field.                                                                                          */
  114. /*                                                                                                 */
  115. /* This routine left adjusts the number in the field, with leading zeros replaced by blanks.  The  */
  116. /* sign is floating; it is placed immediately before the first digit of the number if the number   */
  117. /* is negative.  If the number is positive, no sign is placed.  If the number in the range of      */
  118. /* 1 > number > -1, a 0 is inserted before the decimal point.                                      */
  119. /*                                                                                                 */
  120. /* The operation of the routine is as follows: The end of the input is determined and the sign     */
  121. /* nibble checked for 0x0b or 0X0d and bNegativeNumber is set to TRUE if the number is negative,   */
  122. /* and FALSE otherwise.  The location of the decimal point is set into sPointCtr, and the leading  */
  123. /* blanks flag bLeadingBlanks is initialized to TRUE.                                              */
  124. /*                                                                                                 */
  125. /* A loop is then entered to check each nibble of the input number.  On the even numbered count,   */
  126. /* the high nibble is used; on the odd number counts, the low nibble is used and the input pointer */
  127. /* pInputPos is incremented to the next byte.  The last nibble (the sign bits) is not checked.     */
  128. /*                                                                                                 */
  129. /* In the loop, we check to see if we are before the decimal point.  If so, we multiply ldAccum by */
  130. /* 10 and add the current digit.  If we are after the decimal point, we multiply ldDecimalAccum    */
  131. /* by 10 and add the digit.  At the same time, we multiply ldDivisor by 10 to provide a divisor.   */
  132. /*                                                                                                 */
  133. /* This method was selected to minimize the errors encountered by floating point variables not     */
  134. /* being able to represent exact values for fractions.  Computing a new fraction each time through */
  135. /* the loop could introduce truncation errors, which could be compounded at each iteration of the  */
  136. /* loop.  This method minimizes the risk, but obviously cannot elimitate it altogether.            */
  137. /*                                                                                                 */
  138. /* After completion of the loop, ldDecimalAccum (the portion to the right of the decimal place) is */
  139. /* divided by ldDivisor and added to ldAccum.  The sign is checked, and if negative, the negative  */
  140. /* value of the number is returned; otherwise the positive value is returned.                      */
  141. /*                                                                                                 */
  142. /* This is example code I have used for courses in DB2/2, and has not been thoroughly checked for  */
  143. /* errors.  No warrenties are implied, but it has worked for the fields I have tested.             */
  144. /*                                                                                                 */
  145. /* Copyright (C) 1993 by JDS & Associates, Raleigh, NC.  CIS ID: 70363,407                         */
  146. /*                                                                                                 */
  147. /***************************************************************************************************/
  148. long double DecimalToLongDouble (struct sqlvar * sqlvar)
  149. {
  150.    long double ldAccum = 0;                                       /* Accumulator for output        */
  151.    long double ldDecimalAccum = 0;                                /* Accumulator for fractions     */
  152.    long double ldDivisor = 1;                                     /* Divider for Fractions         */
  153.    PCHAR PInputPos = sqlvar->sqldata;                             /* Point at the data area        */
  154.    SHORT sDigitCount = LOBYTE(sqlvar->sqllen);                    /* Total number of digits        */
  155.    SHORT sPointCtr = sDigitCount - HIBYTE(sqlvar->sqllen);        /* Offset of decimal point       */
  156.    SHORT sDigit =                                                 /* One digit from input          */
  157.          LONIBBLE(*(PInputPos + ((sDigitCount + 1) / 2) - 1));    /* Sign character here           */
  158.    BOOL  bNegativeNumber;                                         /* Get sign nibble               */
  159.    INT i;                                                         /* Loop control                  */
  160.  
  161.    bNegativeNumber = (sDigit == 0X0B) || (sDigit == 0X0D);        /* < 0 if sign = 0x0b or 0x0d    */
  162.  
  163.    for (i = 0; i < (sDigitCount-1); i++)                          /* Loop through each except last */
  164.    {
  165.       sDigit = i % 2 ?                                            /* Even/Odd time through test    */
  166.          LONIBBLE(*(PInputPos++)) : HINIBBLE(*PInputPos);         /* hi/low digit in byte          */
  167.       if (--sPointCtr >= 0)                                       /* If before decimal point       */
  168.       {
  169.          ldAccum *= 10;                                           /* Multiply existing value by 10 */
  170.          ldAccum += sDigit;                                       /* Add in the new digit          */
  171.       }
  172.       else
  173.       {
  174.          ldDivisor *= 10;                                         /* Multiply final divisor by 10  */
  175.          ldDecimalAccum *= 10;                                    /* Multiply current decimal by 10*/
  176.          ldDecimalAccum += sDigit;                                /* Add in new value              */
  177.       }
  178.    }
  179.    ldAccum += ldDecimalAccum / ldDivisor;                         /* Compute fraction and add in   */
  180.    return bNegativeNumber ? -ldAccum : ldAccum;                   /* Return sign adjusted number   */
  181. }
  182.  
  183.