~ubuntu-branches/ubuntu/gutsy/icu/gutsy-updates

« back to all changes in this revision

Viewing changes to source/common/digitlst.cpp

  • Committer: Package Import Robot
  • Author(s): Jay Berkenbilt
  • Date: 2005-11-19 11:29:31 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20051119112931-vcizkrp10tli4enw
Tags: 3.4-3
Explicitly build with g++ 3.4.  The current ICU fails its test suite
with 4.0 but not with 3.4.  Future versions should work properly with
4.0.

Show diffs side-by-side

added added

removed removed

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