Home | History | Annotate | Download | only in i18n
      1 /*
      2  * Copyright (C) 2015, International Business Machines
      3  * Corporation and others.  All Rights Reserved.
      4  *
      5  * file name: precisison.cpp
      6  */
      7 
      8 #include <math.h>
      9 
     10 #include "unicode/utypes.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include "digitlst.h"
     15 #include "fmtableimp.h"
     16 #include "precision.h"
     17 #include "putilimp.h"
     18 #include "visibledigits.h"
     19 
     20 U_NAMESPACE_BEGIN
     21 
     22 static const int32_t gPower10[] = {1, 10, 100, 1000};
     23 
     24 FixedPrecision::FixedPrecision()
     25         : fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) {
     26     fMin.setIntDigitCount(1);
     27     fMin.setFracDigitCount(0);
     28 }
     29 
     30 UBool
     31 FixedPrecision::isRoundingRequired(
     32         int32_t upperExponent, int32_t lowerExponent) const {
     33     int32_t leastSigAllowed = fMax.getLeastSignificantInclusive();
     34     int32_t maxSignificantDigits = fSignificant.getMax();
     35     int32_t roundDigit;
     36     if (maxSignificantDigits == INT32_MAX) {
     37         roundDigit = leastSigAllowed;
     38     } else {
     39         int32_t limitDigit = upperExponent - maxSignificantDigits;
     40         roundDigit =
     41                 limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed;
     42     }
     43     return (roundDigit > lowerExponent);
     44 }
     45 
     46 DigitList &
     47 FixedPrecision::round(
     48         DigitList &value, int32_t exponent, UErrorCode &status) const {
     49     if (U_FAILURE(status)) {
     50         return value;
     51     }
     52     value .fContext.status &= ~DEC_Inexact;
     53     if (!fRoundingIncrement.isZero()) {
     54         if (exponent == 0) {
     55             value.quantize(fRoundingIncrement, status);
     56         } else {
     57             DigitList adjustedIncrement(fRoundingIncrement);
     58             adjustedIncrement.shiftDecimalRight(exponent);
     59             value.quantize(adjustedIncrement, status);
     60         }
     61         if (U_FAILURE(status)) {
     62             return value;
     63         }
     64     }
     65     int32_t leastSig = fMax.getLeastSignificantInclusive();
     66     if (leastSig == INT32_MIN) {
     67         value.round(fSignificant.getMax());
     68     } else {
     69         value.roundAtExponent(
     70                 exponent + leastSig,
     71                 fSignificant.getMax());
     72     }
     73     if (fExactOnly && (value.fContext.status & DEC_Inexact)) {
     74         status = U_FORMAT_INEXACT_ERROR;
     75     } else if (fFailIfOverMax) {
     76         // Smallest interval for value stored in interval
     77         DigitInterval interval;
     78         value.getSmallestInterval(interval);
     79         if (fMax.getIntDigitCount() < interval.getIntDigitCount()) {
     80             status = U_ILLEGAL_ARGUMENT_ERROR;
     81         }
     82     }
     83     return value;
     84 }
     85 
     86 DigitInterval &
     87 FixedPrecision::getIntervalForZero(DigitInterval &interval) const {
     88     interval = fMin;
     89     if (fSignificant.getMin() > 0) {
     90         interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
     91     }
     92     interval.shrinkToFitWithin(fMax);
     93     return interval;
     94 }
     95 
     96 DigitInterval &
     97 FixedPrecision::getInterval(
     98         int32_t upperExponent, DigitInterval &interval) const {
     99     if (fSignificant.getMin() > 0) {
    100         interval.expandToContainDigit(
    101                 upperExponent - fSignificant.getMin());
    102     }
    103     interval.expandToContain(fMin);
    104     interval.shrinkToFitWithin(fMax);
    105     return interval;
    106 }
    107 
    108 DigitInterval &
    109 FixedPrecision::getInterval(
    110         const DigitList &value, DigitInterval &interval) const {
    111     if (value.isZero()) {
    112         interval = fMin;
    113         if (fSignificant.getMin() > 0) {
    114             interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
    115         }
    116     } else {
    117         value.getSmallestInterval(interval);
    118         if (fSignificant.getMin() > 0) {
    119             interval.expandToContainDigit(
    120                     value.getUpperExponent() - fSignificant.getMin());
    121         }
    122         interval.expandToContain(fMin);
    123     }
    124     interval.shrinkToFitWithin(fMax);
    125     return interval;
    126 }
    127 
    128 UBool
    129 FixedPrecision::isFastFormattable() const {
    130     return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax);
    131 }
    132 
    133 UBool
    134 FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) {
    135     if (value.isNaN()) {
    136         digits.setNaN();
    137         return TRUE;
    138     }
    139     if (value.isInfinite()) {
    140         digits.setInfinite();
    141         if (!value.isPositive()) {
    142             digits.setNegative();
    143         }
    144         return TRUE;
    145     }
    146     return FALSE;
    147 }
    148 
    149 VisibleDigits &
    150 FixedPrecision::initVisibleDigits(
    151         DigitList &value,
    152         VisibleDigits &digits,
    153         UErrorCode &status) const {
    154     if (U_FAILURE(status)) {
    155         return digits;
    156     }
    157     digits.clear();
    158     if (handleNonNumeric(value, digits)) {
    159         return digits;
    160     }
    161     if (!value.isPositive()) {
    162         digits.setNegative();
    163     }
    164     value.setRoundingMode(fRoundingMode);
    165     round(value, 0, status);
    166     getInterval(value, digits.fInterval);
    167     digits.fExponent = value.getLowerExponent();
    168     value.appendDigitsTo(digits.fDigits, status);
    169     return digits;
    170 }
    171 
    172 VisibleDigits &
    173 FixedPrecision::initVisibleDigits(
    174         int64_t value,
    175         VisibleDigits &digits,
    176         UErrorCode &status) const {
    177     if (U_FAILURE(status)) {
    178         return digits;
    179     }
    180     if (!fRoundingIncrement.isZero()) {
    181         // If we have round increment, use digit list.
    182         DigitList digitList;
    183         digitList.set(value);
    184         return initVisibleDigits(digitList, digits, status);
    185     }
    186     // Try fast path
    187     if (initVisibleDigits(value, 0, digits, status)) {
    188         digits.fAbsDoubleValue = fabs((double) value);
    189         digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
    190         return digits;
    191     }
    192     // Oops have to use digit list
    193     DigitList digitList;
    194     digitList.set(value);
    195     return initVisibleDigits(digitList, digits, status);
    196 }
    197 
    198 VisibleDigits &
    199 FixedPrecision::initVisibleDigits(
    200         double value,
    201         VisibleDigits &digits,
    202         UErrorCode &status) const {
    203     if (U_FAILURE(status)) {
    204         return digits;
    205     }
    206     digits.clear();
    207     if (uprv_isNaN(value)) {
    208         digits.setNaN();
    209         return digits;
    210     }
    211     if (uprv_isPositiveInfinity(value)) {
    212         digits.setInfinite();
    213         return digits;
    214     }
    215     if (uprv_isNegativeInfinity(value)) {
    216         digits.setInfinite();
    217         digits.setNegative();
    218         return digits;
    219     }
    220     if (!fRoundingIncrement.isZero()) {
    221         // If we have round increment, use digit list.
    222         DigitList digitList;
    223         digitList.set(value);
    224         return initVisibleDigits(digitList, digits, status);
    225     }
    226     // Try to find n such that value * 10^n is an integer
    227     int32_t n = -1;
    228     double scaled;
    229     for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) {
    230         scaled = value * gPower10[i];
    231         if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) {
    232             break;
    233         }
    234         if (scaled == floor(scaled)) {
    235             n = i;
    236             break;
    237         }
    238     }
    239     // Try fast path
    240     if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) {
    241         digits.fAbsDoubleValue = fabs(value);
    242         digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
    243         // Adjust for negative 0 becuase when we cast to an int64,
    244         // negative 0 becomes positive 0.
    245         if (scaled == 0.0 && uprv_isNegative(scaled)) {
    246             digits.setNegative();
    247         }
    248         return digits;
    249     }
    250 
    251     // Oops have to use digit list
    252     DigitList digitList;
    253     digitList.set(value);
    254     return initVisibleDigits(digitList, digits, status);
    255 }
    256 
    257 UBool
    258 FixedPrecision::initVisibleDigits(
    259         int64_t mantissa,
    260         int32_t exponent,
    261         VisibleDigits &digits,
    262         UErrorCode &status) const {
    263     if (U_FAILURE(status)) {
    264         return TRUE;
    265     }
    266     digits.clear();
    267 
    268     // Precompute fAbsIntValue if it is small enough, but we don't know yet
    269     // if it will be valid.
    270     UBool absIntValueComputed = FALSE;
    271     if (mantissa > -1000000000000000000LL /* -1e18 */
    272             && mantissa < 1000000000000000000LL /* 1e18 */) {
    273         digits.fAbsIntValue = mantissa;
    274         if (digits.fAbsIntValue < 0) {
    275             digits.fAbsIntValue = -digits.fAbsIntValue;
    276         }
    277         int32_t i = 0;
    278         int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1;
    279         for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) {
    280             digits.fAbsIntValue /= gPower10[maxPower10Exp];
    281         }
    282         digits.fAbsIntValue /= gPower10[i - exponent];
    283         absIntValueComputed = TRUE;
    284     }
    285     if (mantissa == 0) {
    286         getIntervalForZero(digits.fInterval);
    287         digits.fAbsIntValueSet = absIntValueComputed;
    288         return TRUE;
    289     }
    290     // be sure least significant digit is non zero
    291     while (mantissa % 10 == 0) {
    292         mantissa /= 10;
    293         ++exponent;
    294     }
    295     if (mantissa < 0) {
    296         digits.fDigits.append((char) -(mantissa % -10), status);
    297         mantissa /= -10;
    298         digits.setNegative();
    299     }
    300     while (mantissa) {
    301         digits.fDigits.append((char) (mantissa % 10), status);
    302         mantissa /= 10;
    303     }
    304     if (U_FAILURE(status)) {
    305         return TRUE;
    306     }
    307     digits.fExponent = exponent;
    308     int32_t upperExponent = exponent + digits.fDigits.length();
    309     if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) {
    310         status = U_ILLEGAL_ARGUMENT_ERROR;
    311         return TRUE;
    312     }
    313     UBool roundingRequired =
    314             isRoundingRequired(upperExponent, exponent);
    315     if (roundingRequired) {
    316         if (fExactOnly) {
    317             status = U_FORMAT_INEXACT_ERROR;
    318             return TRUE;
    319         }
    320         return FALSE;
    321     }
    322     digits.fInterval.setLeastSignificantInclusive(exponent);
    323     digits.fInterval.setMostSignificantExclusive(upperExponent);
    324     getInterval(upperExponent, digits.fInterval);
    325 
    326     // The intValue we computed above is only valid if our visible digits
    327     // doesn't exceed the maximum integer digits allowed.
    328     digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits();
    329     return TRUE;
    330 }
    331 
    332 VisibleDigitsWithExponent &
    333 FixedPrecision::initVisibleDigitsWithExponent(
    334         DigitList &value,
    335         VisibleDigitsWithExponent &digits,
    336         UErrorCode &status) const {
    337     digits.clear();
    338     initVisibleDigits(value, digits.fMantissa, status);
    339     return digits;
    340 }
    341 
    342 VisibleDigitsWithExponent &
    343 FixedPrecision::initVisibleDigitsWithExponent(
    344         double value,
    345         VisibleDigitsWithExponent &digits,
    346         UErrorCode &status) const {
    347     digits.clear();
    348     initVisibleDigits(value, digits.fMantissa, status);
    349     return digits;
    350 }
    351 
    352 VisibleDigitsWithExponent &
    353 FixedPrecision::initVisibleDigitsWithExponent(
    354         int64_t value,
    355         VisibleDigitsWithExponent &digits,
    356         UErrorCode &status) const {
    357     digits.clear();
    358     initVisibleDigits(value, digits.fMantissa, status);
    359     return digits;
    360 }
    361 
    362 ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) {
    363 }
    364 
    365 DigitList &
    366 ScientificPrecision::round(DigitList &value, UErrorCode &status) const {
    367     if (U_FAILURE(status)) {
    368         return value;
    369     }
    370     int32_t exponent = value.getScientificExponent(
    371             fMantissa.fMin.getIntDigitCount(), getMultiplier());
    372     return fMantissa.round(value, exponent, status);
    373 }
    374 
    375 int32_t
    376 ScientificPrecision::toScientific(DigitList &value) const {
    377     return value.toScientific(
    378             fMantissa.fMin.getIntDigitCount(), getMultiplier());
    379 }
    380 
    381 int32_t
    382 ScientificPrecision::getMultiplier() const {
    383     int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount();
    384     if (maxIntDigitCount == INT32_MAX) {
    385         return 1;
    386     }
    387     int32_t multiplier =
    388         maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1;
    389     return (multiplier < 1 ? 1 : multiplier);
    390 }
    391 
    392 VisibleDigitsWithExponent &
    393 ScientificPrecision::initVisibleDigitsWithExponent(
    394         DigitList &value,
    395         VisibleDigitsWithExponent &digits,
    396         UErrorCode &status) const {
    397     if (U_FAILURE(status)) {
    398         return digits;
    399     }
    400     digits.clear();
    401     if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) {
    402         return digits;
    403     }
    404     value.setRoundingMode(fMantissa.fRoundingMode);
    405     int64_t exponent = toScientific(round(value, status));
    406     fMantissa.initVisibleDigits(value, digits.fMantissa, status);
    407     FixedPrecision exponentPrecision;
    408     exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits);
    409     exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status);
    410     digits.fHasExponent = TRUE;
    411     return digits;
    412 }
    413 
    414 VisibleDigitsWithExponent &
    415 ScientificPrecision::initVisibleDigitsWithExponent(
    416         double value,
    417         VisibleDigitsWithExponent &digits,
    418         UErrorCode &status) const {
    419     if (U_FAILURE(status)) {
    420         return digits;
    421     }
    422     DigitList digitList;
    423     digitList.set(value);
    424     return initVisibleDigitsWithExponent(digitList, digits, status);
    425 }
    426 
    427 VisibleDigitsWithExponent &
    428 ScientificPrecision::initVisibleDigitsWithExponent(
    429         int64_t value,
    430         VisibleDigitsWithExponent &digits,
    431         UErrorCode &status) const {
    432     if (U_FAILURE(status)) {
    433         return digits;
    434     }
    435     DigitList digitList;
    436     digitList.set(value);
    437     return initVisibleDigitsWithExponent(digitList, digits, status);
    438 }
    439 
    440 
    441 U_NAMESPACE_END
    442 #endif /* #if !UCONFIG_NO_FORMATTING */
    443