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