mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
Merge pull request #61 from icu-units/output_skeletons
Implement Precision handling in UsagePrefsHandler::processQuantity
This commit is contained in:
commit
16547f3298
21 changed files with 488 additions and 226 deletions
|
@ -219,6 +219,7 @@
|
|||
<ClCompile Include="number_multiplier.cpp" />
|
||||
<ClCompile Include="number_currencysymbols.cpp" />
|
||||
<ClCompile Include="number_skeletons.cpp" />
|
||||
<ClCompile Include="number_symbolswrapper.cpp" />
|
||||
<ClCompile Include="number_capi.cpp" />
|
||||
<ClCompile Include="string_segment.cpp" />
|
||||
<ClCompile Include="numparse_parsednumber.cpp" />
|
||||
|
@ -487,13 +488,14 @@
|
|||
<ClInclude Include="number_scientific.h" />
|
||||
<ClInclude Include="formatted_string_builder.h" />
|
||||
<ClInclude Include="number_types.h" />
|
||||
<ClCompile Include="number_usageprefs.h" />
|
||||
<ClInclude Include="number_usageprefs.h" />
|
||||
<ClInclude Include="number_utypes.h" />
|
||||
<ClInclude Include="number_utils.h" />
|
||||
<ClInclude Include="number_mapper.h" />
|
||||
<ClInclude Include="number_multiplier.h" />
|
||||
<ClInclude Include="number_currencysymbols.h" />
|
||||
<ClInclude Include="number_skeletons.h" />
|
||||
<ClInclude Include="number_symbolswrapper.h" />
|
||||
<ClInclude Include="string_segment.h" />
|
||||
<ClInclude Include="numparse_impl.h" />
|
||||
<ClInclude Include="numparse_symbols.h" />
|
||||
|
|
|
@ -591,6 +591,9 @@
|
|||
<ClCompile Include="formatted_string_builder.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="number_usageprefs.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="number_utils.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -606,6 +609,9 @@
|
|||
<ClCompile Include="number_skeletons.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="number_symbolswrapper.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="number_capi.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -917,6 +923,9 @@
|
|||
<ClInclude Include="number_utypes.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="number_usageprefs.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="number_utils.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
@ -932,6 +941,9 @@
|
|||
<ClInclude Include="number_skeletons.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="number_symbolswrapper.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="string_segment.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -520,123 +520,6 @@ LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale
|
|||
return LocalizedNumberFormatter(std::move(fMacros), locale);
|
||||
}
|
||||
|
||||
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper& other) {
|
||||
doCopyFrom(other);
|
||||
}
|
||||
|
||||
SymbolsWrapper::SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT {
|
||||
doMoveFrom(std::move(src));
|
||||
}
|
||||
|
||||
SymbolsWrapper& SymbolsWrapper::operator=(const SymbolsWrapper& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
doCleanup();
|
||||
doCopyFrom(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymbolsWrapper& SymbolsWrapper::operator=(SymbolsWrapper&& src) U_NOEXCEPT {
|
||||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
doCleanup();
|
||||
doMoveFrom(std::move(src));
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymbolsWrapper::~SymbolsWrapper() {
|
||||
doCleanup();
|
||||
}
|
||||
|
||||
void SymbolsWrapper::setTo(const DecimalFormatSymbols& dfs) {
|
||||
doCleanup();
|
||||
fType = SYMPTR_DFS;
|
||||
fPtr.dfs = new DecimalFormatSymbols(dfs);
|
||||
}
|
||||
|
||||
void SymbolsWrapper::setTo(const NumberingSystem* ns) {
|
||||
doCleanup();
|
||||
fType = SYMPTR_NS;
|
||||
fPtr.ns = ns;
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper& other) {
|
||||
fType = other.fType;
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
// Memory allocation failures are exposed in copyErrorTo()
|
||||
if (other.fPtr.dfs != nullptr) {
|
||||
fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
|
||||
} else {
|
||||
fPtr.dfs = nullptr;
|
||||
}
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
// Memory allocation failures are exposed in copyErrorTo()
|
||||
if (other.fPtr.ns != nullptr) {
|
||||
fPtr.ns = new NumberingSystem(*other.fPtr.ns);
|
||||
} else {
|
||||
fPtr.ns = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doMoveFrom(SymbolsWrapper&& src) {
|
||||
fType = src.fType;
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
fPtr.dfs = src.fPtr.dfs;
|
||||
src.fPtr.dfs = nullptr;
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
fPtr.ns = src.fPtr.ns;
|
||||
src.fPtr.ns = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doCleanup() {
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
delete fPtr.dfs;
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
delete fPtr.ns;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolsWrapper::isDecimalFormatSymbols() const {
|
||||
return fType == SYMPTR_DFS;
|
||||
}
|
||||
|
||||
bool SymbolsWrapper::isNumberingSystem() const {
|
||||
return fType == SYMPTR_NS;
|
||||
}
|
||||
|
||||
const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
|
||||
U_ASSERT(fType == SYMPTR_DFS);
|
||||
return fPtr.dfs;
|
||||
}
|
||||
|
||||
const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
|
||||
U_ASSERT(fType == SYMPTR_NS);
|
||||
return fPtr.ns;
|
||||
}
|
||||
|
||||
|
||||
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
|
||||
auto results = new UFormattedNumberData();
|
||||
|
|
|
@ -25,9 +25,6 @@ using namespace icu::number;
|
|||
using namespace icu::number::impl;
|
||||
|
||||
|
||||
MicroPropsGenerator::~MicroPropsGenerator() = default;
|
||||
|
||||
|
||||
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
|
||||
: NumberFormatterImpl(macros, true, status) {
|
||||
}
|
||||
|
@ -255,16 +252,14 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
|||
precision = Precision::integer().withMinDigits(2);
|
||||
} else if (isCurrency) {
|
||||
precision = Precision::currency(UCURR_USAGE_STANDARD);
|
||||
} else if (macros.usage.isSet()) {
|
||||
// Bogus Precision - it will get set in the UsagePrefsHandler instead
|
||||
precision = Precision();
|
||||
} else {
|
||||
precision = Precision::maxFraction(6);
|
||||
}
|
||||
UNumberFormatRoundingMode roundingMode;
|
||||
if (macros.roundingMode != kDefaultMode) {
|
||||
roundingMode = macros.roundingMode;
|
||||
} else {
|
||||
// Temporary until ICU 64
|
||||
roundingMode = precision.fRoundingMode;
|
||||
}
|
||||
roundingMode = macros.roundingMode;
|
||||
fMicros.rounder = {precision, roundingMode, currency, status};
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
|
|
|
@ -40,6 +40,9 @@ IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) {
|
|||
}
|
||||
|
||||
void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (fHasError) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
} else if (fUnion.minMaxInt.fMaxInt == -1) {
|
||||
|
|
|
@ -426,6 +426,9 @@ void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// We shouldn't receive any outputUnit for which we haven't already got a
|
||||
// LongNameHandler:
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
|
|
|
@ -92,6 +92,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
|||
int32_t minSig = properties.minimumSignificantDigits;
|
||||
int32_t maxSig = properties.maximumSignificantDigits;
|
||||
double roundingIncrement = properties.roundingIncrement;
|
||||
// Not assigning directly to macros.roundingMode here: we change
|
||||
// roundingMode if and when we also change macros.precision.
|
||||
RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN);
|
||||
bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
|
||||
bool explicitMinMaxSig = minSig != -1 || maxSig != -1;
|
||||
|
@ -145,7 +147,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
|||
precision = Precision::constructCurrency(currencyUsage);
|
||||
}
|
||||
if (!precision.isBogus()) {
|
||||
precision.fRoundingMode = roundingMode;
|
||||
macros.roundingMode = roundingMode;
|
||||
macros.precision = precision;
|
||||
}
|
||||
|
||||
|
@ -239,7 +241,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
|
|||
// TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
|
||||
macros.precision = Precision::constructSignificant(minSig_, maxSig_);
|
||||
}
|
||||
macros.precision.fRoundingMode = roundingMode;
|
||||
macros.roundingMode = roundingMode;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "charstr.h"
|
||||
#include "uassert.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
#include "number_types.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "double-conversion.h"
|
||||
#include "number_roundingutils.h"
|
||||
#include "number_skeletons.h"
|
||||
#include "putilimp.h"
|
||||
#include "string_segment.h"
|
||||
|
||||
using namespace icu;
|
||||
using namespace icu::number;
|
||||
|
@ -19,6 +22,40 @@ using namespace icu::number::impl;
|
|||
|
||||
|
||||
using double_conversion::DoubleToStringConverter;
|
||||
using icu::StringSegment;
|
||||
|
||||
// Most blueprint_helpers live in number_skeletons.cpp. This one is in
|
||||
// number_rounding.cpp for dependency reasons.
|
||||
void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps ¯os,
|
||||
UErrorCode &status) {
|
||||
// Need to do char <-> UChar conversion...
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
CharString buffer;
|
||||
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
|
||||
|
||||
// Utilize DecimalQuantity/decNumber to parse this for us.
|
||||
DecimalQuantity dq;
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
|
||||
if (U_FAILURE(localStatus)) {
|
||||
// throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return;
|
||||
}
|
||||
double increment = dq.toDouble();
|
||||
|
||||
// We also need to figure out how many digits. Do a brute force string operation.
|
||||
int decimalOffset = 0;
|
||||
while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') {
|
||||
decimalOffset++;
|
||||
}
|
||||
if (decimalOffset == segment.length()) {
|
||||
macros.precision = Precision::increment(increment);
|
||||
} else {
|
||||
int32_t fractionLength = segment.length() - decimalOffset - 1;
|
||||
macros.precision = Precision::increment(increment).withMinFraction(fractionLength);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -84,7 +121,7 @@ digits_t roundingutils::doubleFractionLength(double input, int8_t* singleDigit)
|
|||
|
||||
|
||||
Precision Precision::unlimited() {
|
||||
return Precision(RND_NONE, {}, kDefaultMode);
|
||||
return Precision(RND_NONE, {});
|
||||
}
|
||||
|
||||
FractionPrecision Precision::integer() {
|
||||
|
@ -229,7 +266,7 @@ FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac)
|
|||
settings.fMaxSig = -1;
|
||||
PrecisionUnion union_;
|
||||
union_.fracSig = settings;
|
||||
return {RND_FRACTION, union_, kDefaultMode};
|
||||
return {RND_FRACTION, union_};
|
||||
}
|
||||
|
||||
Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
|
||||
|
@ -240,7 +277,7 @@ Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
|
|||
settings.fMaxSig = static_cast<digits_t>(maxSig);
|
||||
PrecisionUnion union_;
|
||||
union_.fracSig = settings;
|
||||
return {RND_SIGNIFICANT, union_, kDefaultMode};
|
||||
return {RND_SIGNIFICANT, union_};
|
||||
}
|
||||
|
||||
Precision
|
||||
|
@ -250,7 +287,7 @@ Precision::constructFractionSignificant(const FractionPrecision &base, int32_t m
|
|||
settings.fMaxSig = static_cast<digits_t>(maxSig);
|
||||
PrecisionUnion union_;
|
||||
union_.fracSig = settings;
|
||||
return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
|
||||
return {RND_FRACTION_SIGNIFICANT, union_};
|
||||
}
|
||||
|
||||
IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
|
||||
|
@ -270,18 +307,18 @@ IncrementPrecision Precision::constructIncrement(double increment, int32_t minFr
|
|||
// NOTE: In C++, we must return the correct value type with the correct union.
|
||||
// It would be invalid to return a RND_FRACTION here because the methods on the
|
||||
// IncrementPrecision type assume that the union is backed by increment data.
|
||||
return {RND_INCREMENT_ONE, union_, kDefaultMode};
|
||||
return {RND_INCREMENT_ONE, union_};
|
||||
} else if (singleDigit == 5) {
|
||||
return {RND_INCREMENT_FIVE, union_, kDefaultMode};
|
||||
return {RND_INCREMENT_FIVE, union_};
|
||||
} else {
|
||||
return {RND_INCREMENT, union_, kDefaultMode};
|
||||
return {RND_INCREMENT, union_};
|
||||
}
|
||||
}
|
||||
|
||||
CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
|
||||
PrecisionUnion union_;
|
||||
union_.currencyUsage = usage;
|
||||
return {RND_CURRENCY, union_, kDefaultMode};
|
||||
return {RND_CURRENCY, union_};
|
||||
}
|
||||
|
||||
|
||||
|
@ -341,6 +378,9 @@ RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl:
|
|||
|
||||
/** This is the method that contains the actual rounding logic. */
|
||||
void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (fPassThrough) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ enum Section {
|
|||
inline bool
|
||||
getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return false;
|
||||
}
|
||||
switch (roundingMode) {
|
||||
case RoundingMode::UNUM_ROUND_UP:
|
||||
// round away from zero
|
||||
|
@ -187,6 +190,9 @@ class RoundingImpl {
|
|||
Precision fPrecision;
|
||||
UNumberFormatRoundingMode fRoundingMode;
|
||||
bool fPassThrough = true; // default value
|
||||
|
||||
// Permits access to fPrecision.
|
||||
friend class UsagePrefsHandler;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -152,21 +152,6 @@ UPRV_BLOCK_MACRO_BEGIN { \
|
|||
} UPRV_BLOCK_MACRO_END
|
||||
|
||||
|
||||
#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \
|
||||
UPRV_BLOCK_MACRO_BEGIN { \
|
||||
UErrorCode conversionStatus = U_ZERO_ERROR; \
|
||||
(dest).appendInvariantChars({FALSE, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \
|
||||
if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \
|
||||
/* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \
|
||||
(status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
|
||||
return; \
|
||||
} else if (U_FAILURE(conversionStatus)) { \
|
||||
(status) = conversionStatus; \
|
||||
return; \
|
||||
} \
|
||||
} UPRV_BLOCK_MACRO_END
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
@ -483,6 +468,7 @@ UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) {
|
|||
MacroProps skeleton::parseSkeleton(
|
||||
const UnicodeString& skeletonString, int32_t& errOffset, UErrorCode& status) {
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
U_ASSERT(kSerializedStemTrie != nullptr);
|
||||
|
||||
// Add a trailing whitespace to the end of the skeleton string to make code cleaner.
|
||||
UnicodeString tempSkeletonString(skeletonString);
|
||||
|
@ -589,6 +575,8 @@ MacroProps skeleton::parseSkeleton(
|
|||
ParseState
|
||||
skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
|
||||
MacroProps& macros, UErrorCode& status) {
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
|
||||
// First check for "blueprint" stems, which start with a "signal char"
|
||||
switch (segment.charAt(0)) {
|
||||
case u'.':
|
||||
|
@ -767,6 +755,7 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
|
|||
|
||||
ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros,
|
||||
UErrorCode& status) {
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
|
||||
///// Required options: /////
|
||||
|
||||
|
@ -995,6 +984,7 @@ blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeS
|
|||
|
||||
void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros,
|
||||
UErrorCode& status) {
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
const UnicodeString stemString = segment.toTempUnicodeString();
|
||||
|
||||
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
|
||||
|
@ -1010,7 +1000,6 @@ void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, Mac
|
|||
}
|
||||
|
||||
// Need to do char <-> UChar conversion...
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
CharString type;
|
||||
SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
|
||||
CharString subType;
|
||||
|
@ -1339,36 +1328,8 @@ bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroPr
|
|||
return true;
|
||||
}
|
||||
|
||||
void blueprint_helpers::parseIncrementOption(const StringSegment& segment, MacroProps& macros,
|
||||
UErrorCode& status) {
|
||||
// Need to do char <-> UChar conversion...
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
CharString buffer;
|
||||
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
|
||||
|
||||
// Utilize DecimalQuantity/decNumber to parse this for us.
|
||||
DecimalQuantity dq;
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
|
||||
if (U_FAILURE(localStatus)) {
|
||||
// throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
|
||||
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
|
||||
return;
|
||||
}
|
||||
double increment = dq.toDouble();
|
||||
|
||||
// We also need to figure out how many digits. Do a brute force string operation.
|
||||
int decimalOffset = 0;
|
||||
while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') {
|
||||
decimalOffset++;
|
||||
}
|
||||
if (decimalOffset == segment.length()) {
|
||||
macros.precision = Precision::increment(increment);
|
||||
} else {
|
||||
int32_t fractionLength = segment.length() - decimalOffset - 1;
|
||||
macros.precision = Precision::increment(increment).withMinFraction(fractionLength);
|
||||
}
|
||||
}
|
||||
// blueprint_helpers::parseIncrementOption lives in number_rounding.cpp for
|
||||
// dependencies reasons.
|
||||
|
||||
void blueprint_helpers::generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb,
|
||||
UErrorCode&) {
|
||||
|
@ -1583,7 +1544,7 @@ bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErr
|
|||
}
|
||||
}
|
||||
|
||||
bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
|
||||
bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) {
|
||||
if (macros.usage.fLength > 0) {
|
||||
sb.append(u"usage/", -1);
|
||||
sb.append(UnicodeString(macros.usage.fUsage, -1, US_INV));
|
||||
|
|
|
@ -355,6 +355,24 @@ struct SeenMacroProps {
|
|||
bool scale = false;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \
|
||||
UPRV_BLOCK_MACRO_BEGIN { \
|
||||
UErrorCode conversionStatus = U_ZERO_ERROR; \
|
||||
(dest).appendInvariantChars({FALSE, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \
|
||||
if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \
|
||||
/* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \
|
||||
(status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
|
||||
return; \
|
||||
} else if (U_FAILURE(conversionStatus)) { \
|
||||
(status) = conversionStatus; \
|
||||
return; \
|
||||
} \
|
||||
} UPRV_BLOCK_MACRO_END
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
|
125
icu4c/source/i18n/number_symbolswrapper.cpp
Normal file
125
icu4c/source/i18n/number_symbolswrapper.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
// © 2020 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "number_microprops.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
|
||||
using namespace icu;
|
||||
using namespace icu::number;
|
||||
using namespace icu::number::impl;
|
||||
|
||||
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
|
||||
doCopyFrom(other);
|
||||
}
|
||||
|
||||
SymbolsWrapper::SymbolsWrapper(SymbolsWrapper &&src) U_NOEXCEPT {
|
||||
doMoveFrom(std::move(src));
|
||||
}
|
||||
|
||||
SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
doCleanup();
|
||||
doCopyFrom(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymbolsWrapper &SymbolsWrapper::operator=(SymbolsWrapper &&src) U_NOEXCEPT {
|
||||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
doCleanup();
|
||||
doMoveFrom(std::move(src));
|
||||
return *this;
|
||||
}
|
||||
|
||||
SymbolsWrapper::~SymbolsWrapper() {
|
||||
doCleanup();
|
||||
}
|
||||
|
||||
void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
|
||||
doCleanup();
|
||||
fType = SYMPTR_DFS;
|
||||
fPtr.dfs = new DecimalFormatSymbols(dfs);
|
||||
}
|
||||
|
||||
void SymbolsWrapper::setTo(const NumberingSystem *ns) {
|
||||
doCleanup();
|
||||
fType = SYMPTR_NS;
|
||||
fPtr.ns = ns;
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
|
||||
fType = other.fType;
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
// Memory allocation failures are exposed in copyErrorTo()
|
||||
if (other.fPtr.dfs != nullptr) {
|
||||
fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
|
||||
} else {
|
||||
fPtr.dfs = nullptr;
|
||||
}
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
// Memory allocation failures are exposed in copyErrorTo()
|
||||
if (other.fPtr.ns != nullptr) {
|
||||
fPtr.ns = new NumberingSystem(*other.fPtr.ns);
|
||||
} else {
|
||||
fPtr.ns = nullptr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doMoveFrom(SymbolsWrapper &&src) {
|
||||
fType = src.fType;
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
fPtr.dfs = src.fPtr.dfs;
|
||||
src.fPtr.dfs = nullptr;
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
fPtr.ns = src.fPtr.ns;
|
||||
src.fPtr.ns = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolsWrapper::doCleanup() {
|
||||
switch (fType) {
|
||||
case SYMPTR_NONE:
|
||||
// No action necessary
|
||||
break;
|
||||
case SYMPTR_DFS:
|
||||
delete fPtr.dfs;
|
||||
break;
|
||||
case SYMPTR_NS:
|
||||
delete fPtr.ns;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolsWrapper::isDecimalFormatSymbols() const {
|
||||
return fType == SYMPTR_DFS;
|
||||
}
|
||||
|
||||
bool SymbolsWrapper::isNumberingSystem() const {
|
||||
return fType == SYMPTR_NS;
|
||||
}
|
||||
|
||||
const DecimalFormatSymbols *SymbolsWrapper::getDecimalFormatSymbols() const {
|
||||
U_ASSERT(fType == SYMPTR_DFS);
|
||||
return fPtr.dfs;
|
||||
}
|
||||
|
||||
const NumberingSystem *SymbolsWrapper::getNumberingSystem() const {
|
||||
U_ASSERT(fType == SYMPTR_NS);
|
||||
return fPtr.ns;
|
||||
}
|
|
@ -262,7 +262,7 @@ class U_I18N_API ModifierStore {
|
|||
*/
|
||||
class U_I18N_API MicroPropsGenerator {
|
||||
public:
|
||||
virtual ~MicroPropsGenerator();
|
||||
virtual ~MicroPropsGenerator() = default;
|
||||
|
||||
/**
|
||||
* Considers the given {@link DecimalQuantity}, optionally mutates it, and
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "number_decimalquantity.h"
|
||||
#include "number_microprops.h"
|
||||
#include "number_roundingutils.h"
|
||||
#include "number_skeletons.h"
|
||||
#include "unicode/char16ptr.h"
|
||||
#include "unicode/currunit.h"
|
||||
#include "unicode/fmtable.h"
|
||||
|
@ -19,6 +20,7 @@
|
|||
|
||||
using namespace icu::number;
|
||||
using namespace icu::number::impl;
|
||||
using icu::StringSegment;
|
||||
|
||||
// Copy constructor
|
||||
Usage::Usage(const Usage &other) : fUsage(nullptr), fLength(other.fLength), fError(other.fError) {
|
||||
|
@ -100,25 +102,44 @@ void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
|
|||
|
||||
quantity.roundToInfinity(); // Enables toDouble
|
||||
const auto routed = fUnitsRouter.route(quantity.toDouble(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
const auto& routedUnits = routed.measures;
|
||||
micros.outputUnit = routedUnits[0]->getUnit();
|
||||
quantity.setToDouble(routedUnits[0]->getNumber().getDouble());
|
||||
|
||||
// TODO(units): here we are always overriding Precision. (1) get precision
|
||||
// from fUnitsRouter, (2) ensure we use the UnitPreference skeleton's
|
||||
// precision only when there isn't an explicit override we prefer to use.
|
||||
// This needs to be handled within
|
||||
// NumberFormatterImpl::macrosToMicroGenerator in number_formatimpl.cpp
|
||||
// TODO: Use precision from `routed` result.
|
||||
Precision precision = Precision::integer().withMinDigits(2);
|
||||
UNumberFormatRoundingMode roundingMode;
|
||||
// Temporary until ICU 64?
|
||||
roundingMode = precision.fRoundingMode;
|
||||
CurrencyUnit currency(u"", status);
|
||||
micros.rounder = {precision, roundingMode, currency, status};
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
UnicodeString precisionSkeleton = routed.precision;
|
||||
if (micros.rounder.fPrecision.isBogus()) {
|
||||
if (precisionSkeleton.length() > 0) {
|
||||
micros.rounder.fPrecision = parseSkeletonToPrecision(precisionSkeleton, status);
|
||||
} else {
|
||||
// We use the same rounding mode as COMPACT notation: known to be a
|
||||
// human-friendly rounding mode: integers, but add a decimal digit
|
||||
// as needed to ensure we have at least 2 significant digits.
|
||||
micros.rounder.fPrecision = Precision::integer().withMinDigits(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Precision UsagePrefsHandler::parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton,
|
||||
UErrorCode status) {
|
||||
if (U_FAILURE(status)) {
|
||||
// As a member of UsagePrefsHandler, which is a friend of Precision, we
|
||||
// get access to the default constructor.
|
||||
return {};
|
||||
}
|
||||
constexpr int32_t kSkelPrefixLen = 20;
|
||||
if (!precisionSkeleton.startsWith(UNICODE_STRING_SIMPLE("precision-increment/"))) {
|
||||
status = U_INVALID_FORMAT_ERROR;
|
||||
return {};
|
||||
}
|
||||
U_ASSERT(precisionSkeleton[kSkelPrefixLen - 1] == u'/');
|
||||
StringSegment segment(precisionSkeleton, false);
|
||||
segment.adjustOffset(kSkelPrefixLen);
|
||||
MacroProps macros;
|
||||
blueprint_helpers::parseIncrementOption(segment, macros, status);
|
||||
return macros.precision;
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -52,6 +52,8 @@ class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory
|
|||
private:
|
||||
UnitsRouter fUnitsRouter;
|
||||
const MicroPropsGenerator *fParent;
|
||||
|
||||
static Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, UErrorCode status);
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
|
|
@ -123,6 +123,7 @@ number_patternstring.cpp
|
|||
number_rounding.cpp
|
||||
number_scientific.cpp
|
||||
number_skeletons.cpp
|
||||
number_symbolswrapper.cpp
|
||||
number_usageprefs.cpp
|
||||
number_utils.cpp
|
||||
numfmt.cpp
|
||||
|
|
|
@ -707,12 +707,8 @@ class U_I18N_API Precision : public UMemory {
|
|||
typedef PrecisionUnion::FractionSignificantSettings FractionSignificantSettings;
|
||||
typedef PrecisionUnion::IncrementSettings IncrementSettings;
|
||||
|
||||
/** The Precision encapsulates the RoundingMode when used within the implementation. */
|
||||
UNumberFormatRoundingMode fRoundingMode;
|
||||
|
||||
Precision(const PrecisionType& type, const PrecisionUnion& union_,
|
||||
UNumberFormatRoundingMode roundingMode)
|
||||
: fType(type), fUnion(union_), fRoundingMode(roundingMode) {}
|
||||
Precision(const PrecisionType& type, const PrecisionUnion& union_)
|
||||
: fType(type), fUnion(union_) {}
|
||||
|
||||
Precision(UErrorCode errorCode) : fType(RND_ERROR) {
|
||||
fUnion.errorCode = errorCode;
|
||||
|
@ -746,8 +742,6 @@ class U_I18N_API Precision : public UMemory {
|
|||
|
||||
static CurrencyPrecision constructCurrency(UCurrencyUsage usage);
|
||||
|
||||
static Precision constructPassThrough();
|
||||
|
||||
// To allow MacroProps/MicroProps to initialize bogus instances:
|
||||
friend struct impl::MacroProps;
|
||||
friend struct impl::MicroProps;
|
||||
|
@ -769,9 +763,7 @@ class U_I18N_API Precision : public UMemory {
|
|||
// To allow access to the skeleton generation code:
|
||||
friend class impl::GeneratorHelpers;
|
||||
|
||||
// TODO(units): revisit when UnitsRouter is changed: do we still need this
|
||||
// once Precision is returned by UnitsRouter? For now, we allow access to
|
||||
// Precision constructor from UsagePrefsHandler:
|
||||
// To allow access to isBogus and the default (bogus) constructor:
|
||||
friend class impl::UsagePrefsHandler;
|
||||
};
|
||||
|
||||
|
|
|
@ -25,7 +25,16 @@ class Measure;
|
|||
namespace units {
|
||||
|
||||
struct RouteResult : UMemory {
|
||||
// A list of measures: a single measure for single units, multiple measures
|
||||
// for mixed units.
|
||||
//
|
||||
// TODO(icu-units/icu#21): figure out the right mixed unit API.
|
||||
MaybeStackVector<Measure> measures;
|
||||
|
||||
// A skeleton string starting with a precision-increment.
|
||||
//
|
||||
// TODO(hugovdm): generalise? or narrow down to only a precision-increment?
|
||||
// or document that other skeleton elements are ignored?
|
||||
UnicodeString precision;
|
||||
|
||||
RouteResult(MaybeStackVector<Measure> measures, UnicodeString precision)
|
||||
|
|
|
@ -869,7 +869,8 @@ library: i18n
|
|||
dayperiodrules
|
||||
listformatter
|
||||
formatting formattable_cnv regex regex_cnv translit
|
||||
double_conversion number_representation number_output numberformatter number_skeletons numberparser
|
||||
double_conversion number_representation number_output numberformatter
|
||||
number_skeletons number_usageprefs numberparser
|
||||
units_extra unitsformatter
|
||||
universal_time_scale
|
||||
uclean_i18n
|
||||
|
@ -989,16 +990,41 @@ group: numberformatter
|
|||
number_decimfmtprops.o
|
||||
number_fluent.o number_formatimpl.o
|
||||
number_grouping.o number_integerwidth.o number_longnames.o
|
||||
number_mapper.o number_modifiers.o number_multiplier.o
|
||||
number_mapper.o number_modifiers.o
|
||||
number_notation.o number_padding.o
|
||||
number_patternmodifier.o number_patternstring.o number_rounding.o
|
||||
number_scientific.o number_usageprefs.o
|
||||
currpinf.o dcfmtsym.o numsys.o
|
||||
number_patternmodifier.o number_patternstring.o
|
||||
number_scientific.o
|
||||
currpinf.o
|
||||
numrange_fluent.o numrange_impl.o
|
||||
deps
|
||||
decnumber double_conversion formattable units unitsformatter
|
||||
number_representation number_output
|
||||
numsys
|
||||
number_usageprefs
|
||||
uclean_i18n common
|
||||
number_symbolswrapper
|
||||
|
||||
group: numsys
|
||||
dcfmtsym.o
|
||||
numsys.o
|
||||
deps
|
||||
currency
|
||||
resourcebundle
|
||||
uclean_i18n
|
||||
|
||||
group: number_usageprefs
|
||||
number_multiplier.o
|
||||
number_usageprefs.o
|
||||
deps
|
||||
number_rounding
|
||||
number_symbolswrapper
|
||||
unitsformatter
|
||||
|
||||
group: number_rounding
|
||||
number_rounding.o
|
||||
deps
|
||||
currency
|
||||
number_representation
|
||||
|
||||
group: number_skeletons
|
||||
# Number skeleton support; separated from numberformatter
|
||||
|
@ -1007,6 +1033,12 @@ group: number_skeletons
|
|||
numberformatter
|
||||
units_extra
|
||||
|
||||
group: number_symbolswrapper
|
||||
number_symbolswrapper.o
|
||||
deps
|
||||
platform
|
||||
numsys
|
||||
|
||||
group: numberparser
|
||||
numparse_affixes.o numparse_compositions.o numparse_currency.o
|
||||
numparse_decimal.o numparse_impl.o numparse_parsednumber.o
|
||||
|
|
|
@ -56,6 +56,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
|
|||
void unitCompoundMeasure();
|
||||
void unitUsage();
|
||||
void unitUsageErrorCodes();
|
||||
void unitUsageSkeletons();
|
||||
void unitCurrency();
|
||||
void unitPercent();
|
||||
void percentParity();
|
||||
|
|
|
@ -77,6 +77,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
|
|||
TESTCASE_AUTO(unitCompoundMeasure);
|
||||
TESTCASE_AUTO(unitUsage);
|
||||
TESTCASE_AUTO(unitUsageErrorCodes);
|
||||
TESTCASE_AUTO(unitUsageSkeletons);
|
||||
TESTCASE_AUTO(unitCurrency);
|
||||
TESTCASE_AUTO(unitPercent);
|
||||
if (!quick) {
|
||||
|
@ -684,8 +685,12 @@ void NumberFormatterApiTest::unitUsage() {
|
|||
|
||||
IcuTestErrorCode status(*this, "unitUsage()");
|
||||
|
||||
LocalizedNumberFormatter formatter = unloc_formatter.locale("en-ZA");
|
||||
FormattedNumber formattedNum = formatter.formatDouble(300, status);
|
||||
LocalizedNumberFormatter formatter;
|
||||
FormattedNumber formattedNum;
|
||||
|
||||
formatter = unloc_formatter.locale("en-ZA");
|
||||
formattedNum = formatter.formatDouble(321, status);
|
||||
status.errIfFailureAndReset("unitUsage() en-ZA road, formatDouble(...)");
|
||||
assertTrue(UnicodeString("unitUsage() en-ZA road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
|
||||
|
@ -701,17 +706,23 @@ void NumberFormatterApiTest::unitUsage() {
|
|||
u"877 km",
|
||||
u"88 km",
|
||||
u"8,8 km",
|
||||
u"877 m",
|
||||
u"88 m",
|
||||
u"8,8 m",
|
||||
u"900 m",
|
||||
u"90 m",
|
||||
u"10 m",
|
||||
u"0 m");
|
||||
|
||||
formatter = unloc_formatter.locale("en-GB");
|
||||
formattedNum = formatter.formatDouble(300, status);
|
||||
formattedNum = formatter.formatDouble(321, status);
|
||||
status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertTrue(UnicodeString("unitUsage() en-GB road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
|
||||
assertEquals("unitUsage() en-GB road", "328 yd", formattedNum.toString(status));
|
||||
status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertEquals("unitUsage() en-GB road", "350 yd", formattedNum.toString(status));
|
||||
status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertFormatDescendingBig(
|
||||
u"unitUsage() en-GB road",
|
||||
u"measure-unit/length-meter usage/road",
|
||||
|
@ -729,11 +740,17 @@ void NumberFormatterApiTest::unitUsage() {
|
|||
u"0 yd");
|
||||
|
||||
formatter = unloc_formatter.locale("en-US");
|
||||
formattedNum = formatter.formatDouble(300, status);
|
||||
formattedNum = formatter.formatDouble(321, status);
|
||||
status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertTrue(UnicodeString("unitUsage() en-US road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
|
||||
assertEquals("unitUsage() en-US road", "984 ft", formattedNum.toString(status));
|
||||
status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertEquals("unitUsage() en-US road", "1,050 ft", formattedNum.toString(status));
|
||||
status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
|
||||
U_ASSERT(status == U_ZERO_ERROR);
|
||||
assertFormatDescendingBig(
|
||||
u"unitUsage() en-US road",
|
||||
u"measure-unit/length-meter usage/road",
|
||||
|
@ -746,9 +763,30 @@ void NumberFormatterApiTest::unitUsage() {
|
|||
u"54 mi",
|
||||
u"5.4 mi",
|
||||
u"0.54 mi",
|
||||
u"288 ft",
|
||||
u"29 ft",
|
||||
u"300 ft",
|
||||
u"30 ft",
|
||||
u"0 ft");
|
||||
|
||||
assertFormatDescendingBig(
|
||||
u"Scientific notation with Usage: possible when using a reasonable Precision",
|
||||
u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name",
|
||||
u"scientific @### usage/default unit/square-meter unit-width-full-name",
|
||||
NumberFormatter::with()
|
||||
.unit(SQUARE_METER)
|
||||
.usage("default")
|
||||
.notation(Notation::scientific())
|
||||
.precision(Precision::minMaxSignificantDigits(1, 4))
|
||||
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
|
||||
Locale("en-ZA"),
|
||||
u"8,765E1 square kilometres",
|
||||
u"8,765E0 square kilometres",
|
||||
u"8,765E1 hectares",
|
||||
u"8,765E0 hectares",
|
||||
u"8,765E3 square metres",
|
||||
u"8,765E2 square metres",
|
||||
u"8,765E1 square metres",
|
||||
u"8,765E0 square metres",
|
||||
u"0E0 square centimetres");
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::unitUsageErrorCodes() {
|
||||
|
@ -772,6 +810,122 @@ void NumberFormatterApiTest::unitUsageErrorCodes() {
|
|||
status.assertSuccess();
|
||||
}
|
||||
|
||||
// Tests for the "skeletons" field in unitPreferenceData, as well as precision
|
||||
// and notation overrides.
|
||||
void NumberFormatterApiTest::unitUsageSkeletons() {
|
||||
IcuTestErrorCode status(*this, "unitUsageSkeletons()");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Default >300m road preference skeletons round to 50m",
|
||||
u"usage/road measure-unit/length-meter",
|
||||
u"usage/road unit/meter",
|
||||
NumberFormatter::with().unit(METER).usage("road"),
|
||||
Locale("en-ZA"),
|
||||
321,
|
||||
u"300 m");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Precision can be overridden: override takes precedence",
|
||||
u"usage/road measure-unit/length-meter @#",
|
||||
u"usage/road unit/meter @#",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.precision(Precision::maxSignificantDigits(2)),
|
||||
Locale("en-ZA"),
|
||||
321,
|
||||
u"320 m");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Compact notation with Usage: bizarre, but possible (short)",
|
||||
u"compact-short usage/road measure-unit/length-meter",
|
||||
u"compact-short usage/road unit/meter",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.notation(Notation::compactShort()),
|
||||
Locale("en-ZA"),
|
||||
987654321,
|
||||
u"988K km");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Compact notation with Usage: bizarre, but possible (short, precision override)",
|
||||
u"compact-short usage/road measure-unit/length-meter @#",
|
||||
u"compact-short usage/road unit/meter @#",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.notation(Notation::compactShort())
|
||||
.precision(Precision::maxSignificantDigits(2)),
|
||||
Locale("en-ZA"),
|
||||
987654321,
|
||||
u"990K km");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Compact notation with Usage: unusual but possible (long)",
|
||||
u"compact-long usage/road measure-unit/length-meter @#",
|
||||
u"compact-long usage/road unit/meter @#",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.notation(Notation::compactLong())
|
||||
.precision(Precision::maxSignificantDigits(2)),
|
||||
Locale("en-ZA"),
|
||||
987654321,
|
||||
u"990 thousand km");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Compact notation with Usage: unusual but possible (long, precision override)",
|
||||
u"compact-long usage/road measure-unit/length-meter @#",
|
||||
u"compact-long usage/road unit/meter @#",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.notation(Notation::compactLong())
|
||||
.precision(Precision::maxSignificantDigits(2)),
|
||||
Locale("en-ZA"),
|
||||
987654321,
|
||||
u"990 thousand km");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Scientific notation, not recommended, requires precision override for road",
|
||||
u"scientific usage/road measure-unit/length-meter",
|
||||
u"scientific usage/road unit/meter",
|
||||
NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()),
|
||||
Locale("en-ZA"),
|
||||
321.45,
|
||||
// Rounding to the nearest "50" is not exponent-adjusted in scientific notation:
|
||||
u"0E2 m");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Scientific notation with Usage: possible when using a reasonable Precision",
|
||||
u"scientific usage/road measure-unit/length-meter @###",
|
||||
u"scientific usage/road unit/meter @###",
|
||||
NumberFormatter::with()
|
||||
.unit(METER)
|
||||
.usage("road")
|
||||
.notation(Notation::scientific())
|
||||
.precision(Precision::maxSignificantDigits(4)),
|
||||
Locale("en-ZA"),
|
||||
321.45,
|
||||
u"3,215E2 m");
|
||||
|
||||
assertFormatSingle(
|
||||
u"Scientific notation with Usage: possible when using a reasonable Precision",
|
||||
u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name",
|
||||
u"scientific usage/default unit/astronomical-unit unit-width-full-name",
|
||||
NumberFormatter::with()
|
||||
.unit(MeasureUnit::forIdentifier("astronomical-unit", status))
|
||||
.usage("default")
|
||||
.notation(Notation::scientific())
|
||||
.unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
|
||||
Locale("en-ZA"),
|
||||
1e20,
|
||||
u"1,5E28 kilometres");
|
||||
|
||||
status.assertSuccess();
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::unitCompoundMeasure() {
|
||||
assertFormatDescending(
|
||||
u"Meters Per Second Short (unit that simplifies) and perUnit method",
|
||||
|
|
Loading…
Add table
Reference in a new issue