home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / snip9707.zip / BCDD.C < prev    next >
C/C++ Source or Header  |  1997-07-05  |  10KB  |  232 lines

  1. /* +++Date last modified: 05-Jul-1997 */
  2.  
  3. /*FILE*****************************************************************/
  4. /* File Id:                    bcd.c.                                 */
  5. /* Author:                     Stan Milam.                            */
  6. /* Date Written:               31-Jan-95.                             */
  7. /* Description:                                                       */
  8. /*     Routines to manage binary coded decimal.                       */
  9. /*                                                                    */
  10. /*     bcd_to_double() - Convert BCD to type double value.            */
  11. /*     double_to_bcd() - Convert double type value to BCD.            */
  12. /*                                                                    */
  13. /* Placed in the public domain by the author, 8-Sep-95                */
  14. /*                                                                    */
  15. /*****************************************************************FILE*/
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <float.h>
  21. #include <math.h>
  22. #include "snipmath.h"
  23.  
  24. /*FUNCTION*************************************************************/
  25. /* Name: bcd_to_double().                                             */
  26. /*                                                                    */
  27. /* Description:                                                       */
  28. /*     This function will convert buffer containing binary coded dec- */
  29. /*     imal to a double.                                              */
  30. /*                                                                    */
  31. /* Arguments:                                                         */
  32. /*     void     *buf  - Address of buffer containing binary coded     */
  33. /*                      decimal number.                               */
  34. /*     size_t   buflen- Length of the buffer.                         */
  35. /*     int      digits- The number of digits to the right of the      */
  36. /*                      decimal point.                                */
  37. /*                                                                    */
  38. /* Return Value:                                                      */
  39. /*     A value of type double which should be the equivalent of the   */
  40. /*     BCD value.                                                     */
  41. /*                                                                    */
  42. /**********************************************************************/
  43.  
  44. double bcd_to_double(void *buf, size_t len, int digits)
  45. {
  46.       double   rv = 0.0;
  47.       char     *buffer = (char *) buf;
  48.       size_t   high, low, index, max = len - 1;
  49.  
  50.       digits = abs(digits);
  51.  
  52.     /******************************************************************/
  53.     /* Loop through the buffer repeatedly extracting the high and low */
  54.     /* 4 bits from each byte and calculating the return value.        */
  55.     /******************************************************************/
  56.  
  57.       for (index = 0; index < max; index++)
  58.       {
  59.             low  = buffer[index] & 0x0f;
  60.             high = (buffer[index] & 0xf0) >> 4;
  61.             rv = (( rv * 10.0 + high ) * 10.0 + low);
  62.       }
  63.  
  64.     /******************************************************************/
  65.     /* The first byte of the buffer contains the lowest order digit   */
  66.     /* in the upper 4 bits and the sign in the lower 4 bits.          */
  67.     /******************************************************************/
  68.  
  69.       low  = buffer[max] & 0x0f;
  70.       high = (buffer[max] & 0xf0) >> 4;
  71.       rv = rv * 10.0 + high;
  72.       if (digits > 0)
  73.             rv /= pow(10, digits);
  74.       if (low == 0x0d)
  75.             rv = -rv;
  76.       return rv;
  77. }
  78.  
  79. /*FUNCTION*************************************************************/
  80. /* Name: double_to_bcd().                                             */
  81. /*                                                                    */
  82. /* Description:                                                       */
  83. /*     This function will convert a value of type double to a legit-  */
  84. /*     mate Binary Coded Decimal (BCD) value.                         */
  85. /*                                                                    */
  86. /* Arguments:                                                         */
  87. /*     double   arg    - The value to be converted.                   */
  88. /*     char     *buf   - The buffer where the BCD value is stored.    */
  89. /*     size_t   length - The number of significant digits to store in */
  90. /*                       BCD buffer.                                  */
  91. /*     size_t   digits - The number of digits to the right of the     */
  92. /*                       decimal point to be stored in the BCD        */
  93. /*                       buffer.                                      */
  94. /*                                                                    */
  95. /* Return Value:                                                      */
  96. /*     An integer value indicating the length of the BCD value in the */
  97. /*     buffer.  -1 is returned if an error occured.                   */
  98. /*                                                                    */
  99. /*************************************************************FUNCTION*/
  100.  
  101. int double_to_bcd(double arg, char *buf, size_t length, size_t digits )
  102. {
  103.       char wrkbuf[50], format[DBL_DIG + 1];
  104.       int  y_sub, x_sub, rv, negative=0;
  105.  
  106.     /******************************************************************/
  107.     /* Do a couple of sanity checks first.                            */
  108.     /******************************************************************/
  109.  
  110.       if ((length == 0 && digits == 0) || (length + digits > DBL_DIG))
  111.             rv = -1;
  112.       else
  113.       {
  114.         /**************************************************************/
  115.         /* If the double argument is negative make a note of it and   */
  116.         /* con- vert the value to be positive.                        */
  117.         /**************************************************************/
  118.  
  119.             if (arg < 0.0)
  120.             {
  121.                   arg = -arg;
  122.                   negative = 1;
  123.             }
  124.  
  125.         /**************************************************************/
  126.         /* Adjust for decimal digits.                                 */
  127.         /**************************************************************/
  128.  
  129.             if (digits > 0)
  130.             {
  131.                   length += digits;
  132.                   arg *= pow( 10, digits );
  133.             }
  134.  
  135.         /**************************************************************/
  136.         /* Build the format string, build the string and compute the  */
  137.         /* return value.                                              */
  138.         /**************************************************************/
  139.  
  140.             sprintf( format, "%%0%d.0f", length );
  141.             sprintf( wrkbuf, format, floor( arg ) );
  142.             if ((rv = (length / 2 ) + (length / 2 != 0)) == 0)
  143.                   rv = 1;
  144.  
  145.         /**************************************************************/
  146.         /* Compute the subscript values and clear the BCD buffer.     */
  147.         /**************************************************************/
  148.  
  149.             y_sub = rv - 1;
  150.             x_sub = strlen( wrkbuf ) - 1;
  151.             memset( buf, 0, y_sub + 1 );
  152.  
  153.         /**************************************************************/
  154.         /* Plug in the sign bits and first BCD digit.                 */
  155.         /**************************************************************/
  156.  
  157.             buf[y_sub] = negative == 1 ? 0xd : 0xc;
  158.             buf[y_sub--] |= ( ( wrkbuf[x_sub--] - '0' ) << 4 );
  159.  
  160.         /**************************************************************/
  161.         /* While we have more digits to plug....                      */
  162.         /**************************************************************/
  163.  
  164.             while ( --length > 0 )
  165.             {
  166.         
  167.             /**********************************************************/
  168.             /* Do the low nibble of the BCD byte.                     */
  169.             /**********************************************************/
  170.  
  171.                   buf[y_sub] = wrkbuf[x_sub--] - '0';
  172.                   if (--length <= 0)
  173.                         break;
  174.  
  175.             /**********************************************************/
  176.             /* Now do the high nibble.                                */
  177.             /**********************************************************/
  178.  
  179.                   buf[y_sub--] |= ((wrkbuf[x_sub--] - '0') << 4);
  180.             }
  181.       }
  182.       return rv;
  183. }
  184. /* */
  185. #ifdef TEST
  186.  
  187. typedef struct {
  188.     double value, expect;
  189.     int    length, digits;
  190. } TEST_T;
  191.  
  192. int main(void)
  193.       double rv, value;
  194.       char   wrkbf[25];
  195.       int    rc, x_sub, y_sub, size, len, digits;
  196.       char   format[] = "    %10.3f      %d          %d        %d      ";
  197.       TEST_T testvals[] = {
  198.             {  12345.67,     123.45,   5, 0 },
  199.             {  12345.67,   12345.0,   5, 1 },
  200.             {  12345.67,   12345.67,  4, 3 },
  201.             {  12345.678,   2345.67,  1, 2 },
  202.             { -12345.00,  -12345.00,  8, 2 },
  203.             { -1234.56,  -12345.00,   5, 3 },
  204.             {  1234.567,      0.60,   1, 3 }
  205.       };
  206.  
  207.       size = sizeof( testvals ) / sizeof( TEST_T );
  208.       printf("    Double       Length     Digits    Return\n");
  209.       printf("    Argument     Argument   Argument  Value    Buffer\n");
  210.       printf("    ================================================="
  211.              "=========\n");
  212.  
  213.       for (x_sub = 0; x_sub < size; x_sub++)
  214.       {
  215.             len = testvals[x_sub].length;
  216.             digits = testvals[x_sub].digits;
  217.             value  = testvals[x_sub].value;
  218.  
  219.             rc = double_to_bcd(value, wrkbf, len, digits);
  220.             if (rc > 0)
  221.             {
  222.                   printf( format, value, len, digits, rc );
  223.                   for ( y_sub = 0; y_sub < rc; y_sub++ )
  224.                         printf("%02X ", wrkbf[y_sub]);
  225.                   printf("\n");
  226.             }
  227.       }
  228.       return 0;
  229. }
  230. #endif
  231.