blob: 5f28391ef10783b2f7c16b19d3478b15b5c4dff3 [file] [log] [blame]
/*
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
* file name: precisison.cpp
*/
#include <math.h>
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "digitlst.h"
#include "fmtableimp.h"
#include "precision.h"
#include "putilimp.h"
#include "visibledigits.h"
U_NAMESPACE_BEGIN
static const int32_t gPower10[] = {1, 10, 100, 1000};
FixedPrecision::FixedPrecision()
: fExactOnly(FALSE), fFailIfOverMax(FALSE), fRoundingMode(DecimalFormat::kRoundHalfEven) {
fMin.setIntDigitCount(1);
fMin.setFracDigitCount(0);
}
UBool
FixedPrecision::isRoundingRequired(
int32_t upperExponent, int32_t lowerExponent) const {
int32_t leastSigAllowed = fMax.getLeastSignificantInclusive();
int32_t maxSignificantDigits = fSignificant.getMax();
int32_t roundDigit;
if (maxSignificantDigits == INT32_MAX) {
roundDigit = leastSigAllowed;
} else {
int32_t limitDigit = upperExponent - maxSignificantDigits;
roundDigit =
limitDigit > leastSigAllowed ? limitDigit : leastSigAllowed;
}
return (roundDigit > lowerExponent);
}
DigitList &
FixedPrecision::round(
DigitList &value, int32_t exponent, UErrorCode &status) const {
if (U_FAILURE(status)) {
return value;
}
value .fContext.status &= ~DEC_Inexact;
if (!fRoundingIncrement.isZero()) {
if (exponent == 0) {
value.quantize(fRoundingIncrement, status);
} else {
DigitList adjustedIncrement(fRoundingIncrement);
adjustedIncrement.shiftDecimalRight(exponent);
value.quantize(adjustedIncrement, status);
}
if (U_FAILURE(status)) {
return value;
}
}
int32_t leastSig = fMax.getLeastSignificantInclusive();
if (leastSig == INT32_MIN) {
value.round(fSignificant.getMax());
} else {
value.roundAtExponent(
exponent + leastSig,
fSignificant.getMax());
}
if (fExactOnly && (value.fContext.status & DEC_Inexact)) {
status = U_FORMAT_INEXACT_ERROR;
} else if (fFailIfOverMax) {
// Smallest interval for value stored in interval
DigitInterval interval;
value.getSmallestInterval(interval);
if (fMax.getIntDigitCount() < interval.getIntDigitCount()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
}
}
return value;
}
DigitInterval &
FixedPrecision::getIntervalForZero(DigitInterval &interval) const {
interval = fMin;
if (fSignificant.getMin() > 0) {
interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
}
interval.shrinkToFitWithin(fMax);
return interval;
}
DigitInterval &
FixedPrecision::getInterval(
int32_t upperExponent, DigitInterval &interval) const {
if (fSignificant.getMin() > 0) {
interval.expandToContainDigit(
upperExponent - fSignificant.getMin());
}
interval.expandToContain(fMin);
interval.shrinkToFitWithin(fMax);
return interval;
}
DigitInterval &
FixedPrecision::getInterval(
const DigitList &value, DigitInterval &interval) const {
if (value.isZero()) {
interval = fMin;
if (fSignificant.getMin() > 0) {
interval.expandToContainDigit(interval.getIntDigitCount() - fSignificant.getMin());
}
} else {
value.getSmallestInterval(interval);
if (fSignificant.getMin() > 0) {
interval.expandToContainDigit(
value.getUpperExponent() - fSignificant.getMin());
}
interval.expandToContain(fMin);
}
interval.shrinkToFitWithin(fMax);
return interval;
}
UBool
FixedPrecision::isFastFormattable() const {
return (fMin.getFracDigitCount() == 0 && fSignificant.isNoConstraints() && fRoundingIncrement.isZero() && !fFailIfOverMax);
}
UBool
FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) {
if (value.isNaN()) {
digits.setNaN();
return TRUE;
}
if (value.isInfinite()) {
digits.setInfinite();
if (!value.isPositive()) {
digits.setNegative();
}
return TRUE;
}
return FALSE;
}
VisibleDigits &
FixedPrecision::initVisibleDigits(
DigitList &value,
VisibleDigits &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
digits.clear();
if (handleNonNumeric(value, digits)) {
return digits;
}
if (!value.isPositive()) {
digits.setNegative();
}
value.setRoundingMode(fRoundingMode);
round(value, 0, status);
getInterval(value, digits.fInterval);
digits.fExponent = value.getLowerExponent();
value.appendDigitsTo(digits.fDigits, status);
return digits;
}
VisibleDigits &
FixedPrecision::initVisibleDigits(
int64_t value,
VisibleDigits &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
if (!fRoundingIncrement.isZero()) {
// If we have round increment, use digit list.
DigitList digitList;
digitList.set(value);
return initVisibleDigits(digitList, digits, status);
}
// Try fast path
if (initVisibleDigits(value, 0, digits, status)) {
digits.fAbsDoubleValue = fabs((double) value);
digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
return digits;
}
// Oops have to use digit list
DigitList digitList;
digitList.set(value);
return initVisibleDigits(digitList, digits, status);
}
VisibleDigits &
FixedPrecision::initVisibleDigits(
double value,
VisibleDigits &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
digits.clear();
if (uprv_isNaN(value)) {
digits.setNaN();
return digits;
}
if (uprv_isPositiveInfinity(value)) {
digits.setInfinite();
return digits;
}
if (uprv_isNegativeInfinity(value)) {
digits.setInfinite();
digits.setNegative();
return digits;
}
if (!fRoundingIncrement.isZero()) {
// If we have round increment, use digit list.
DigitList digitList;
digitList.set(value);
return initVisibleDigits(digitList, digits, status);
}
// Try to find n such that value * 10^n is an integer
int32_t n = -1;
double scaled;
for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) {
scaled = value * gPower10[i];
if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) {
break;
}
if (scaled == floor(scaled)) {
n = i;
break;
}
}
// Try fast path
if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) {
digits.fAbsDoubleValue = fabs(value);
digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits();
// Adjust for negative 0 becuase when we cast to an int64,
// negative 0 becomes positive 0.
if (scaled == 0.0 && uprv_isNegative(scaled)) {
digits.setNegative();
}
return digits;
}
// Oops have to use digit list
DigitList digitList;
digitList.set(value);
return initVisibleDigits(digitList, digits, status);
}
UBool
FixedPrecision::initVisibleDigits(
int64_t mantissa,
int32_t exponent,
VisibleDigits &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return TRUE;
}
digits.clear();
// Precompute fAbsIntValue if it is small enough, but we don't know yet
// if it will be valid.
UBool absIntValueComputed = FALSE;
if (mantissa > -1000000000000000000LL /* -1e18 */
&& mantissa < 1000000000000000000LL /* 1e18 */) {
digits.fAbsIntValue = mantissa;
if (digits.fAbsIntValue < 0) {
digits.fAbsIntValue = -digits.fAbsIntValue;
}
int32_t i = 0;
int32_t maxPower10Exp = UPRV_LENGTHOF(gPower10) - 1;
for (; i > exponent + maxPower10Exp; i -= maxPower10Exp) {
digits.fAbsIntValue /= gPower10[maxPower10Exp];
}
digits.fAbsIntValue /= gPower10[i - exponent];
absIntValueComputed = TRUE;
}
if (mantissa == 0) {
getIntervalForZero(digits.fInterval);
digits.fAbsIntValueSet = absIntValueComputed;
return TRUE;
}
// be sure least significant digit is non zero
while (mantissa % 10 == 0) {
mantissa /= 10;
++exponent;
}
if (mantissa < 0) {
digits.fDigits.append((char) -(mantissa % -10), status);
mantissa /= -10;
digits.setNegative();
}
while (mantissa) {
digits.fDigits.append((char) (mantissa % 10), status);
mantissa /= 10;
}
if (U_FAILURE(status)) {
return TRUE;
}
digits.fExponent = exponent;
int32_t upperExponent = exponent + digits.fDigits.length();
if (fFailIfOverMax && upperExponent > fMax.getIntDigitCount()) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return TRUE;
}
UBool roundingRequired =
isRoundingRequired(upperExponent, exponent);
if (roundingRequired) {
if (fExactOnly) {
status = U_FORMAT_INEXACT_ERROR;
return TRUE;
}
return FALSE;
}
digits.fInterval.setLeastSignificantInclusive(exponent);
digits.fInterval.setMostSignificantExclusive(upperExponent);
getInterval(upperExponent, digits.fInterval);
// The intValue we computed above is only valid if our visible digits
// doesn't exceed the maximum integer digits allowed.
digits.fAbsIntValueSet = absIntValueComputed && !digits.isOverMaxDigits();
return TRUE;
}
VisibleDigitsWithExponent &
FixedPrecision::initVisibleDigitsWithExponent(
DigitList &value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
digits.clear();
initVisibleDigits(value, digits.fMantissa, status);
return digits;
}
VisibleDigitsWithExponent &
FixedPrecision::initVisibleDigitsWithExponent(
double value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
digits.clear();
initVisibleDigits(value, digits.fMantissa, status);
return digits;
}
VisibleDigitsWithExponent &
FixedPrecision::initVisibleDigitsWithExponent(
int64_t value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
digits.clear();
initVisibleDigits(value, digits.fMantissa, status);
return digits;
}
ScientificPrecision::ScientificPrecision() : fMinExponentDigits(1) {
}
DigitList &
ScientificPrecision::round(DigitList &value, UErrorCode &status) const {
if (U_FAILURE(status)) {
return value;
}
int32_t exponent = value.getScientificExponent(
fMantissa.fMin.getIntDigitCount(), getMultiplier());
return fMantissa.round(value, exponent, status);
}
int32_t
ScientificPrecision::toScientific(DigitList &value) const {
return value.toScientific(
fMantissa.fMin.getIntDigitCount(), getMultiplier());
}
int32_t
ScientificPrecision::getMultiplier() const {
int32_t maxIntDigitCount = fMantissa.fMax.getIntDigitCount();
if (maxIntDigitCount == INT32_MAX) {
return 1;
}
int32_t multiplier =
maxIntDigitCount - fMantissa.fMin.getIntDigitCount() + 1;
return (multiplier < 1 ? 1 : multiplier);
}
VisibleDigitsWithExponent &
ScientificPrecision::initVisibleDigitsWithExponent(
DigitList &value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
digits.clear();
if (FixedPrecision::handleNonNumeric(value, digits.fMantissa)) {
return digits;
}
value.setRoundingMode(fMantissa.fRoundingMode);
int64_t exponent = toScientific(round(value, status));
fMantissa.initVisibleDigits(value, digits.fMantissa, status);
FixedPrecision exponentPrecision;
exponentPrecision.fMin.setIntDigitCount(fMinExponentDigits);
exponentPrecision.initVisibleDigits(exponent, digits.fExponent, status);
digits.fHasExponent = TRUE;
return digits;
}
VisibleDigitsWithExponent &
ScientificPrecision::initVisibleDigitsWithExponent(
double value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
DigitList digitList;
digitList.set(value);
return initVisibleDigitsWithExponent(digitList, digits, status);
}
VisibleDigitsWithExponent &
ScientificPrecision::initVisibleDigitsWithExponent(
int64_t value,
VisibleDigitsWithExponent &digits,
UErrorCode &status) const {
if (U_FAILURE(status)) {
return digits;
}
DigitList digitList;
digitList.set(value);
return initVisibleDigitsWithExponent(digitList, digits, status);
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */