home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / common / digitlst.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-08-16  |  17.6 KB  |  535 lines

  1. /*
  2. ********************************************************************************
  3. *                                                                              *
  4. * COPYRIGHT:                                                                   *
  5. *   (C) Copyright Taligent, Inc.,  1997                                        *
  6. *   (C) Copyright International Business Machines Corporation,  1997-1998      *
  7. *   Licensed Material - Program-Property of IBM - All Rights Reserved.         *
  8. *   US Government Users Restricted Rights - Use, duplication, or disclosure    *
  9. *   restricted by GSA ADP Schedule Contract with IBM Corp.                     *
  10. *                                                                              *
  11. ********************************************************************************
  12. *
  13. * File DIGITLST.CPP
  14. *
  15. * Modification History:
  16. *
  17. *   Date        Name        Description
  18. *   03/21/97    clhuang     Converted from java.
  19. *   03/21/97    clhuang     Implemented with new APIs.
  20. *   03/27/97    helena      Updated to pass the simple test after code review.
  21. *   03/31/97    aliu        Moved isLONG_MIN to here, and fixed it.
  22. *   04/15/97    aliu        Changed MAX_COUNT to DBL_DIG.  Changed Digit to char.
  23. *                           Reworked representation by replacing fDecimalAt with
  24. *                           fExponent.
  25. *   04/16/97    aliu        Rewrote set() and getDouble() to use sprintf/atof
  26. *                           to do digit conversion.
  27. *   09/09/97    aliu        Modified for exponential notation support.
  28. *    08/02/98    stephen        Added nearest/even rounding
  29. *                            Fixed bug in fitsIntoLong
  30. ********************************************************************************
  31. */
  32.  
  33. #include "digitlst.h"
  34. #include <stdlib.h>
  35. #include <limits.h>
  36. #include <string.h>
  37. #include <stdio.h>
  38.  
  39. // *****************************************************************************
  40. // class DigitList
  41. // This class handles the transcoding between numeric values and strings of
  42. //  characters.  Only handles as non-negative numbers.  
  43. // *****************************************************************************
  44.  
  45. const char DigitList::kZero = '0';
  46.  
  47. char DigitList::LONG_MIN_REP[LONG_DIGITS];
  48. int32_t  DigitList::LONG_MIN_REP_LENGTH = 0;
  49.  
  50. // -------------------------------------
  51. // default constructor
  52.  
  53. DigitList::DigitList()
  54. {
  55.     clear();
  56. }
  57.  
  58. // -------------------------------------
  59.  
  60. DigitList::~DigitList()
  61. {
  62. }
  63.  
  64. // -------------------------------------
  65. // copy constructor
  66.  
  67. DigitList::DigitList(const DigitList &other)
  68. {
  69.     *this = other;
  70. }
  71.  
  72. // -------------------------------------
  73. // assignment operator
  74.  
  75. DigitList&
  76. DigitList::operator=(const DigitList& other)
  77. {
  78.     if (this != &other)
  79.     {
  80.         fDecimalAt = other.fDecimalAt;
  81.         fCount = other.fCount;
  82.         strncpy(fDigits, other.fDigits, MAX_DIGITS);
  83.     }
  84.     return *this;
  85. }
  86.  
  87. // -------------------------------------
  88.  
  89. bool_t
  90. DigitList::operator==(const DigitList& that) const
  91. {
  92.     return ((this == &that) ||
  93.             (fDecimalAt == that.fDecimalAt &&
  94.              fCount == that.fCount &&
  95.              0 == strncmp(fDigits, that.fDigits, fCount)));
  96. }
  97.  
  98. // -------------------------------------
  99. // Resets the digit list; sets all the digits to zero.
  100.  
  101. void
  102. DigitList::clear()
  103. {
  104.     fDecimalAt = 0;
  105.     fCount = 0;
  106.     for (int32_t i=0; i<MAX_DIGITS; ++i) fDigits[i] = kZero;
  107. }
  108.  
  109. // -------------------------------------
  110. // Appends the digit to the digit list if it's not out of scope.
  111. // Ignores the digit, otherwise.
  112.  
  113. void
  114. DigitList::append(char digit)
  115. {
  116.     // Ignore digits which exceed the precision we can represent
  117.     if (fCount < MAX_DIGITS) fDigits[fCount++] = digit;
  118. }
  119.  
  120. // -------------------------------------
  121.  
  122. /**
  123.  * Currently, getDouble() depends on atof() to do its conversion.
  124.  */
  125. double
  126. DigitList::getDouble() const
  127. {
  128.     if (fCount == 0) return 0.0;
  129.  
  130.     // For the string "." + fDigits + "e" + fDecimalAt.
  131.     char buffer[MAX_DIGITS+32];
  132.     *buffer = '.';
  133.     strncpy(buffer+1, fDigits, fCount);
  134.     sprintf(buffer+fCount+1, "e%d", fDecimalAt);
  135.     return atof(buffer);
  136. }
  137.  
  138. // -------------------------------------
  139.  
  140. int32_t DigitList::getLong() const
  141. {
  142.     // This is 100% accurate in c++ because if we are representing
  143.     // an integral value, we suffer nothing in the conversion to
  144.     // double.  If we are to support 64-bit longs later, this method
  145.     // must be rewritten. [LIU]
  146.     return (int32_t)getDouble();
  147. }
  148.  
  149. /**
  150.  * Return true if the number represented by this object can fit into
  151.  * a long.
  152.  */
  153. bool_t
  154. DigitList::fitsIntoLong(bool_t isPositive, bool_t ignoreNegativeZero)
  155. {
  156.     // Figure out if the result will fit in a long.  We have to
  157.     // first look for nonzero digits after the decimal point;
  158.     // then check the size.  If the digit count is 18 or less, then
  159.     // the value can definitely be represented as a long.  If it is 19
  160.     // then it may be too large.
  161.  
  162.     // Trim trailing zeros.  This does not change the represented value.
  163.     while (fCount > 0 && fDigits[fCount - 1] == '0') --fCount;
  164.  
  165.     if (fCount == 0) {
  166.         // Positive zero fits into a long, but negative zero can only
  167.         // be represented as a double. - bug 4162852
  168.         return isPositive || ignoreNegativeZero;
  169.     }
  170.     
  171.     initializeLONG_MIN_REP();
  172.  
  173.     // If the digit list represents a double or this number is too
  174.     // big for a long.
  175.     if (fDecimalAt < fCount || fDecimalAt > LONG_MIN_REP_LENGTH) return FALSE;
  176.  
  177.     // If number is small enough to fit in a long
  178.     if (fDecimalAt < LONG_MIN_REP_LENGTH) return TRUE;
  179.  
  180.     // At this point we have fDecimalAt == fCount, and fCount == LONG_MIN_REP_LENGTH.
  181.     // The number will overflow if it is larger than LONG_MAX
  182.     // or smaller than LONG_MIN.
  183.     for (int32_t i=0; i<fCount; ++i)
  184.     {
  185.         char dig = fDigits[i], max = LONG_MIN_REP[i];
  186.         if (dig > max) return FALSE;
  187.         if (dig < max) return TRUE;
  188.     }
  189.     
  190.     // At this point the first count digits match.  If fDecimalAt is less
  191.     // than count, then the remaining digits are zero, and we return true.
  192.     if (fCount < fDecimalAt) return TRUE;
  193.  
  194.     // Now we have a representation of Long.MIN_VALUE, without the leading
  195.     // negative sign.  If this represents a positive value, then it does
  196.     // not fit; otherwise it fits.
  197.     return !isPositive;
  198. }
  199.  
  200. // -------------------------------------
  201.  
  202. /**
  203.  * @param maximumDigits The maximum digits to be generated.  If zero,
  204.  * there is no maximum -- generate all digits.
  205.  */
  206. void
  207. DigitList::set(int32_t source, int32_t maximumDigits)
  208. {
  209.     // for now, simple implementation; later, do proper IEEE stuff
  210.     //String stringDigits = Long.toString(source);
  211.     char string [10 + 1];    // maximum digits for a 32-bit signed number is 10 + 1 for sign
  212.     sprintf(string, "%d", source);
  213.  
  214.     char *stringDigits = string;
  215.     // This method does not expect a negative number. However,
  216.     // "source" can be a Long.MIN_VALUE (-9223372036854775808),
  217.     // if the number being formatted is a Long.MIN_VALUE.  In that
  218.     // case, it will be formatted as -Long.MIN_VALUE, a number
  219.     // which is outside the legal range of a long, but which can
  220.     // be represented by DigitList.
  221.     if (stringDigits[0] == '-') 
  222.         stringDigits++;
  223.  
  224.     fCount = fDecimalAt = strlen(stringDigits);
  225.     
  226.     // Don't copy trailing zeros
  227.     while (fCount > 1 && stringDigits[fCount - 1] == '0') 
  228.         --fCount;
  229.     
  230.     //for (int32_t i = 0; i < fCount; ++i)
  231.     //    fDigits[i] = (char) stringDigits[i];
  232.     strncpy(fDigits, stringDigits, fCount);
  233.  
  234.     if(maximumDigits > 0) 
  235.         round(maximumDigits);
  236.  
  237. #if(0)
  238.     // {sfb} old implementation, keep around for now
  239.  
  240.     // Handle the case in which source == LONG_MIN
  241.     set((source >= 0 ? (double)source : -((double)source)),
  242.         maximumDigits > 0 ? maximumDigits : MAX_DIGITS,
  243.         maximumDigits == 0);
  244. #endif
  245. }
  246.  
  247. /**
  248.  * Set the digit list to a representation of the given double value.
  249.  * This method supports both fixed-point and exponential notation.
  250.  * @param source Value to be converted; must not be Inf, -Inf, Nan,
  251.  * or a value <= 0.
  252.  * @param maximumDigits The most fractional or total digits which should
  253.  * be converted.  If total digits, and the value is zero, then
  254.  * there is no maximum -- generate all digits.
  255.  * @param fixedPoint If true, then maximumDigits is the maximum
  256.  * fractional digits to be converted.  If false, total digits.
  257.  */
  258. void
  259. DigitList::set(double source, int32_t maximumDigits, bool_t fixedPoint)
  260. {
  261.     if(source == 0) source = 0;
  262.     // Generate a representation of the form DDDDD, DDDDD.DDDDD, or
  263.     // DDDDDE+/-DDDDD.
  264.     //String rep = Double.toString(source);
  265.     char rep[MAX_DIGITS + 7]; // Extra space for '.', e+NNN, and '\0' (actually +7 is enough)
  266.     sprintf(rep, "%1.*e", MAX_DIGITS - 1, source);
  267.  
  268.     fDecimalAt             = -1;
  269.     fCount                 = 0;
  270.     int32_t exponent     = 0;
  271.     // Number of zeros between decimal point and first non-zero digit after
  272.     // decimal point, for numbers < 1.
  273.     int32_t leadingZerosAfterDecimal = 0;
  274.     bool_t nonZeroDigitSeen = FALSE;
  275.     for (int32_t i=0; i < MAX_DIGITS + 7; ++i) {
  276.         char c = rep[i];
  277.         if (c == '.') {
  278.             fDecimalAt = fCount;
  279.         }
  280.         else if (c == 'e' || c == 'E') {
  281.             // Parse an exponent of the form /[eE][+-]?[0-9]*/
  282.             //exponent = Integer.valueOf(rep.substring(i+1)).intValue();
  283.             i += 1;                 // adjust for 'e'
  284.             bool_t negExp = rep[i] == '-';
  285.             if (negExp || rep[i] == '+') {
  286.                 ++i;
  287.             }
  288.             while ((c = rep[i++]) >= '0' && c <= '9') {
  289.                 exponent = 10*exponent + c - '0';
  290.             }
  291.             if (negExp) {
  292.                 exponent = -exponent;
  293.             }
  294.             break;
  295.         }
  296.         else if (fCount < MAX_DIGITS) {
  297.             if ( ! nonZeroDigitSeen) {
  298.                 nonZeroDigitSeen = (c != '0');
  299.                 if ( ! nonZeroDigitSeen && fDecimalAt != -1) 
  300.                     ++leadingZerosAfterDecimal;
  301.             }
  302.     
  303.             if (nonZeroDigitSeen) 
  304.                 fDigits[fCount++] = (char)c;
  305.         }
  306.     }
  307.     if (fDecimalAt == -1) 
  308.         fDecimalAt = fCount;
  309.     fDecimalAt += exponent - leadingZerosAfterDecimal;
  310.  
  311.     if (fixedPoint)
  312.     {
  313.         // The negative of the exponent represents the number of leading
  314.         // zeros between the decimal and the first non-zero digit, for
  315.         // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2).  If this
  316.         // is more than the maximum fraction digits, then we have an underflow
  317.         // for the printed representation.
  318.         if (-fDecimalAt > maximumDigits) {
  319.             // Handle an underflow to zero when we round something like
  320.             // 0.0009 to 2 fractional digits.
  321.             fCount = 0;
  322.             return;
  323.         } else if (-fDecimalAt == maximumDigits) {
  324.             // If we round 0.0009 to 3 fractional digits, then we have to
  325.             // create a new one digit in the least significant location.
  326.             if (shouldRoundUp(0)) {
  327.                 fCount = 1;
  328.                 ++fDecimalAt;
  329.                 fDigits[0] = (char)'1';
  330.             } else {
  331.                 fCount = 0;
  332.             }
  333.             return;
  334.         }
  335.     }
  336.  
  337.     // Eliminate trailing zeros.
  338.     while (fCount > 1 && fDigits[fCount - 1] == '0')
  339.         --fCount;
  340.  
  341.     /*if (DEBUG)
  342.     {
  343.         System.out.print("Before rounding 0.");
  344.         for (int i=0; i<fCount; ++i) System.out.print((char)digits[i]);
  345.         System.out.println("x10^" + fDecimalAt);
  346.     }*/
  347.  
  348.     // Eliminate digits beyond maximum digits to be displayed.
  349.     // Round up if appropriate.  Do NOT round in the special
  350.     // case where maximumDigits == 0 and fixedPoint is FALSE.
  351.     if (fixedPoint || maximumDigits > 0) {
  352.         round(fixedPoint ? (maximumDigits + fDecimalAt) : maximumDigits);
  353.     }
  354.  
  355.     /*if (DEBUG)
  356.     {
  357.         System.out.print("After rounding 0.");
  358.         for (int i=0; i<fCount; ++i) System.out.print((char)digits[i]);
  359.         System.out.println("x10^" + fDecimalAt);
  360.     }*/
  361. }
  362.  
  363. // -------------------------------------
  364.  
  365. /**
  366.  * Round the representation to the given number of digits.
  367.  * @param maximumDigits The maximum number of digits to be shown.
  368.  * Upon return, count will be less than or equal to maximumDigits.
  369.  */
  370. void 
  371. DigitList::round(int32_t maximumDigits)
  372. {
  373.     // Eliminate digits beyond maximum digits to be displayed.
  374.     // Round up if appropriate.
  375.     if (maximumDigits >= 0 && maximumDigits < fCount)
  376.     {
  377.         if (shouldRoundUp(maximumDigits)) {
  378.             // Rounding up involved incrementing digits from LSD to MSD.
  379.             // In most cases this is simple, but in a worst case situation
  380.             // (9999..99) we have to adjust the decimalAt value.
  381.             for (;;)
  382.             {
  383.                 --maximumDigits;
  384.                 if (maximumDigits < 0)
  385.                 {
  386.                     // We have all 9's, so we increment to a single digit
  387.                     // of one and adjust the exponent.
  388.                     fDigits[0] = (char) '1';
  389.                     ++fDecimalAt;
  390.                     maximumDigits = 0; // Adjust the count
  391.                     break;
  392.                 }
  393.  
  394.                 ++fDigits[maximumDigits];
  395.                 if (fDigits[maximumDigits] <= '9') break;
  396.                 // fDigits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
  397.             }
  398.             ++maximumDigits; // Increment for use as count
  399.         }
  400.         fCount = maximumDigits;
  401.  
  402.         // Eliminate trailing zeros.
  403.         while (fCount > 1 && fDigits[fCount-1] == '0') {
  404.             --fCount;
  405.         }
  406.     }
  407. }
  408.  
  409. /**
  410.  * Return true if truncating the representation to the given number
  411.  * of digits will result in an increment to the last digit.  This
  412.  * method implements half-even rounding, the default rounding mode.
  413.  * [bnf]
  414.  * @param maximumDigits the number of digits to keep, from 0 to
  415.  * <code>count-1</code>.  If 0, then all digits are rounded away, and
  416.  * this method returns true if a one should be generated (e.g., formatting
  417.  * 0.09 with "#.#").
  418.  * @return true if digit <code>maximumDigits-1</code> should be
  419.  * incremented
  420.  */
  421. bool_t DigitList::shouldRoundUp(int32_t maximumDigits) {
  422.     bool_t increment = FALSE;
  423.     // Implement IEEE half-even rounding
  424.     if (fDigits[maximumDigits] > '5') {
  425.         return TRUE;
  426.     } else if (fDigits[maximumDigits] == '5' ) {
  427.         for (int i=maximumDigits+1; i<fCount; ++i) {
  428.             if (fDigits[i] != '0') {
  429.                 return TRUE;
  430.             }
  431.         }
  432.         return maximumDigits > 0 && (fDigits[maximumDigits-1] % 2 != 0);
  433.     }
  434.     return FALSE;
  435. }
  436.  
  437. // -------------------------------------
  438.  
  439. // In the Java implementation, we need a separate set(long) because 64-bit longs
  440. // have too much precision to fit into a 64-bit double.  In C++, longs can just
  441. // be passed to set(double) as long as they are 32 bits in size.  We currently
  442. // don't implement 64-bit longs in C++, although the code below would work for
  443. // that with slight modifications. [LIU]
  444.  
  445. //  void
  446. //  DigitList::set(long source)
  447. //  {
  448. //      // handle the special case of zero using a standard exponent of 0.
  449. //      // mathematically, the exponent can be any value.
  450. //      if (source == 0)
  451. //      {
  452. //          fcount = 0;
  453. //          fDecimalAt = 0;
  454. //          return;
  455. //      }
  456.  
  457. //      // we don't accept negative numbers, with the exception of long_min.
  458. //      // long_min is treated specially by being represented as long_max+1,
  459. //      // which is actually an impossible signed long value, so there is no
  460. //      // ambiguity.  we do this for convenience, so digitlist can easily
  461. //      // represent the digits of a long.
  462. //      bool islongmin = (source == long_min);
  463. //      if (islongmin)
  464. //      {
  465. //          source = -(source + 1); // that is, long_max
  466. //          islongmin = true;
  467. //      }
  468. //      sprintf(fdigits, "%d", source);
  469.  
  470. //      // now we need to compute the exponent.  it's easy in this case; it's
  471. //      // just the same as the count.  e.g., 0.123 * 10^3 = 123.
  472. //      fcount = strlen(fdigits);
  473. //      fDecimalAt = fcount;
  474.  
  475. //      // here's how we represent long_max + 1.  note that we always know
  476. //      // that the last digit of long_max will not be 9, because long_max
  477. //      // is of the form (2^n)-1.
  478. //      if (islongmin) ++fdigits[fcount-1];
  479.  
  480. //      // finally, we trim off trailing zeros.  we don't alter fDecimalAt,
  481. //      // so this has no effect on the represented value.  we know the first
  482. //      // digit is non-zero (see code above), so we only have to check down
  483. //      // to fdigits[1].
  484. //      while (fcount > 1 && fdigits[fcount-1] == kzero) --fcount;
  485. //  }
  486.  
  487. /**
  488.  * Return true if this object represents the value zero.  Anything with
  489.  * no digits, or all zero digits, is zero, regardless of fDecimalAt.
  490.  */
  491. bool_t
  492. DigitList::isZero() const
  493. {
  494.     for (int32_t i=0; i<fCount; ++i) if (fDigits[i] != kZero) return FALSE;
  495.     return TRUE;
  496. }
  497.  
  498. /**
  499.  * We represent LONG_MIN internally as LONG_MAX + 1.  This is actually an impossible
  500.  * value, for positive long integers, so we are safe in doing so.
  501.  */
  502. bool_t
  503. DigitList::isLONG_MIN() const
  504. {
  505.     initializeLONG_MIN_REP();
  506.  
  507.     if (fCount != LONG_MIN_REP_LENGTH) return FALSE;
  508.  
  509.     for (int32_t i = 0; i < LONG_MIN_REP_LENGTH; ++i)
  510.     {
  511.         if (fDigits[i] != LONG_MIN_REP[i+1]) return FALSE;
  512.     }
  513.  
  514.     return TRUE;
  515. }
  516.  
  517. // Initialize the LONG_MIN representation buffer.  Note that LONG_MIN
  518. // is stored as LONG_MAX+1 (LONG_MIN without the negative sign).
  519.  
  520. void
  521. DigitList::initializeLONG_MIN_REP()
  522. {
  523.     if (LONG_MIN_REP_LENGTH == 0)
  524.     {
  525.         // THIS ASSUMES A 32-BIT LONG_MIN VALUE
  526.         char buf[LONG_DIGITS];
  527.         sprintf(buf, "%d", LONG_MIN);
  528.         LONG_MIN_REP_LENGTH = strlen(buf) - 1;
  529.         // assert(LONG_MIN_REP_LENGTH == LONG_DIGITS);
  530.         for (int32_t i=1; i<=LONG_MIN_REP_LENGTH; ++i) LONG_MIN_REP[i-1] = buf[i];
  531.     }
  532. }
  533.  
  534. //eof
  535.