* caller becomes responsible for deleting the array
*/
inline T *orphanOrClone(int32_t length, int32_t &resultCapacity);
+
+protected:
+ // Resizes the array to the size of src, then copies the contents of src.
+ void copyFrom(const MaybeStackArray &src, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (this->resize(src.capacity, 0) == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T));
+ }
+
private:
T *ptr;
int32_t capacity;
// Convention: bottom 4 bits for field, top 4 bits for field category.
// Field category 0 implies the number category so that the number field
// literals can be directly passed as a Field type.
- // See the helper functions in "StringBuilderFieldUtils" below.
// Exported as U_I18N_API so it can be used by other exports on Windows.
struct U_I18N_API Field {
uint8_t bits;
<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" />
<ClCompile Include="ufieldpositer.cpp" />
<ClCompile Include="ulocdata.cpp" />
<ClCompile Include="umsg.cpp" />
- <ClCompile Include="unitconverter.cpp" />
- <ClCompile Include="complexunitsconverter.cpp" />
- <ClCompile Include="unitsrouter.cpp" />
- <ClCompile Include="unitsdata.cpp" />
+ <ClCompile Include="units_complexconverter.cpp" />
+ <ClCompile Include="units_converter.cpp" />
+ <ClCompile Include="units_data.cpp" />
+ <ClCompile Include="units_router.cpp" />
<ClCompile Include="unum.cpp" />
<ClCompile Include="unumsys.cpp" />
<ClCompile Include="upluralrules.cpp" />
<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" />
<ClInclude Include="numparse_utils.h" />
<ClInclude Include="numrange_impl.h" />
<ClInclude Include="formattedval_impl.h" />
- <ClInclude Include="unitconverter.h" />
- <ClInclude Include="complexunitsconverter.h" />
- <ClInclude Include="unitsrouter.h" />
- <ClInclude Include="unitsdata.h" />
+ <ClInclude Include="units_complexconverter.h" />
+ <ClInclude Include="units_converter.h" />
+ <ClInclude Include="units_data.h" />
+ <ClInclude Include="units_router.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="i18n.rc" />
<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>
<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>
<ClCompile Include="erarules.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="unitconverter.cpp">
+ <ClCompile Include="units_complexconverter.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="complexunitsconverter.cpp">
+ <ClCompile Include="units_converter.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="unitsrouter.cpp">
+ <ClCompile Include="units_data.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="unitsdata.cpp">
+ <ClCompile Include="units_router.cpp">
<Filter>formatting</Filter>
</ClCompile>
</ItemGroup>
<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>
<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>
<ClInclude Include="umsg_imp.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="unitconveter.h">
+ <ClInclude Include="units_complexconverter.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="complexunitsconverter.h">
+ <ClInclude Include="units_converter.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="unitsrouter.h">
+ <ClInclude Include="units_data.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="unitsdata.h">
+ <ClInclude Include="units_router.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="vzone.h">
<ClCompile Include="ufieldpositer.cpp" />
<ClCompile Include="ulocdata.cpp" />
<ClCompile Include="umsg.cpp" />
- <ClCompile Include="unitconverter.cpp" />
- <ClCompile Include="complexunitsconverter.cpp" />
- <ClCompile Include="unitsrouter.cpp" />
- <ClCompile Include="unitsdata.cpp" />
+ <ClCompile Include="units_complexconverter.cpp" />
+ <ClCompile Include="units_converter.cpp" />
+ <ClCompile Include="units_data.cpp" />
+ <ClCompile Include="units_router.cpp" />
<ClCompile Include="unum.cpp" />
<ClCompile Include="unumsys.cpp" />
<ClCompile Include="upluralrules.cpp" />
<ClInclude Include="numparse_utils.h" />
<ClInclude Include="numrange_impl.h" />
<ClInclude Include="formattedval_impl.h" />
- <ClInclude Include="unitconverter.h" />
- <ClInclude Include="complexunitsconverter.h" />
- <ClInclude Include="unitsrouter.h" />
- <ClInclude Include="unitsdata.h" />
+ <ClInclude Include="units_complexconverter.h" />
+ <ClInclude Include="units_converter.h" />
+ <ClInclude Include="units_data.h" />
+ <ClInclude Include="units_router.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="i18n.rc" />
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();
using namespace icu::number::impl;
-MicroPropsGenerator::~MicroPropsGenerator() = default;
-
-
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
: NumberFormatterImpl(macros, true, status) {
}
|| !(isPercent || isPermille)
|| isCompactNotation
);
+ bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) &&
+ macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED;
// Select the numbering system.
LocalPointer<const NumberingSystem> nsLocal;
// Unit Preferences and Conversions as our first step
if (macros.usage.isSet()) {
if (!isCldrUnit) {
- // We only support "usage" when the input unit is a CLDR Unit.
+ // We only support "usage" when the input unit is specified, and is
+ // a CLDR Unit.
status = U_ILLEGAL_ARGUMENT_ERROR;
return nullptr;
}
new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fUsage, chain, status);
fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
chain = fUsagePrefsHandler.getAlias();
+ } else if (isMixedUnit) {
+ auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status);
+ fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status);
+ chain = fUnitConversionHandler.getAlias();
}
// Multiplier
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;
resolvePluralRules(macros.rules, macros.locale, status), chain, status),
status);
chain = fLongNameMultiplexer.getAlias();
+ } else if (isMixedUnit) {
+ fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(),
+ status);
+ MixedUnitLongNameHandler::forMeasureUnit(
+ macros.locale, macros.unit, unitWidth,
+ resolvePluralRules(macros.rules, macros.locale, status), chain,
+ fMixedUnitLongNameHandler.getAlias(), status);
+ chain = fMixedUnitLongNameHandler.getAlias();
} else {
fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
LongNameHandler::forMeasureUnit(macros.locale, macros.unit, macros.perUnit, unitWidth,
// Other fields possibly used by the number formatting pipeline:
// TODO: Convert more of these LocalPointers to value objects to reduce the number of news?
LocalPointer<const UsagePrefsHandler> fUsagePrefsHandler;
+ LocalPointer<const UnitConversionHandler> fUnitConversionHandler;
LocalPointer<const DecimalFormatSymbols> fSymbols;
LocalPointer<const PluralRules> fRules;
LocalPointer<const ParsedPatternInfo> fPatternInfo;
LocalPointer<MutablePatternModifier> fPatternModifier;
LocalPointer<ImmutablePatternModifier> fImmutablePatternModifier;
LocalPointer<LongNameHandler> fLongNameHandler;
+ // TODO: use a common base class that enables fLongNameHandler,
+ // fLongNameMultiplexer, and fMixedUnitLongNameHandler to be merged into one
+ // member?
+ LocalPointer<MixedUnitLongNameHandler> fMixedUnitLongNameHandler;
LocalPointer<const LongNameMultiplexer> fLongNameMultiplexer;
LocalPointer<const CompactHandler> fCompactHandler;
}
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) {
* `per` forms.
*/
constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1;
+// Number of keys in the array populated by PluralTableSink.
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 2;
static int32_t getIndex(const char* pluralKeyword, UErrorCode& status) {
}
}
+// Selects a string out of the `strings` array which corresponds to the
+// specified plural form, with fallback to the OTHER form.
+//
+// The `strings` array must have ARRAY_LENGTH items: one corresponding to each
+// of the plural forms, plus a display name ("dnam") and a "per" form.
static UnicodeString getWithPlural(
const UnicodeString* strings,
StandardPlural::Form plural,
// NOTE: outArray MUST have room for all StandardPlural values. No bounds checking is performed.
-// Populates outArray with `locale`-specific values for `unit` through use of
-// PluralTableSink, reading from resources *unitsNarrow* and *unitsShort* (for
-// width UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width
-// UNUM_UNIT_WIDTH_SHORT). For other widths, it would read just "units".
-//
-// outArray must be of fixed length ARRAY_LENGTH.
+/**
+ * Populates outArray with `locale`-specific values for `unit` through use of
+ * PluralTableSink. Only the set of basic units are supported!
+ *
+ * Reading from resources *unitsNarrow* and *unitsShort* (for width
+ * UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width
+ * UNUM_UNIT_WIDTH_SHORT). For other widths, it reads just "units".
+ *
+ * @param unit must have a type and subtype (i.e. it must be a unit listed in
+ * gTypes and gSubTypes in measunit.cpp).
+ * @param outArray must be of fixed length ARRAY_LENGTH.
+ */
void getMeasureData(const Locale &locale, const MeasureUnit &unit, const UNumberUnitWidth &width,
UnicodeString *outArray, UErrorCode &status) {
PluralTableSink sink(outArray);
} // namespace
-// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
void LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef,
const MeasureUnit &perUnit, const UNumberUnitWidth &width,
const PluralRules *rules, const MicroPropsGenerator *parent,
LongNameHandler *fillIn, UErrorCode &status) {
- if (fillIn == nullptr) {
- status = U_INTERNAL_PROGRAM_ERROR;
- return;
- }
+ // Not valid for mixed units that aren't built-in units, and there should
+ // not be any built-in mixed units!
+ U_ASSERT(uprv_strlen(unitRef.getType()) > 0 || unitRef.getComplexity(status) != UMEASURE_UNIT_MIXED);
+ U_ASSERT(fillIn != nullptr);
if (uprv_strlen(unitRef.getType()) == 0 || uprv_strlen(perUnit.getType()) == 0) {
- // TODO(ICU-20941): Unsanctioned unit. Not yet fully supported. Set an error code.
+ // TODO(ICU-20941): Unsanctioned unit. Not yet fully supported. Set an
+ // error code. Once we support not-built-in units here, unitRef may be
+ // anything, but if not built-in, perUnit has to be "none".
status = U_UNSUPPORTED_ERROR;
return;
}
MeasureUnit unit = unitRef;
if (uprv_strcmp(perUnit.getType(), "none") != 0) {
- // Compound unit: first try to simplify (e.g., meters per second is its own unit).
+ // Compound unit: first try to simplify (e.g. "meter per second" is a
+ // built-in unit).
bool isResolved = false;
MeasureUnit resolved = MeasureUnit::resolveUnitPerUnit(unit, perUnit, &isResolved);
if (isResolved) {
status);
}
-// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
void LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit,
const MeasureUnit &perUnit, const UNumberUnitWidth &width,
const PluralRules *rules, const MicroPropsGenerator *parent,
return &fModifiers[plural];
}
+void MixedUnitLongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &mixedUnit,
+ const UNumberUnitWidth &width, const PluralRules *rules,
+ const MicroPropsGenerator *parent,
+ MixedUnitLongNameHandler *fillIn, UErrorCode &status) {
+ U_ASSERT(mixedUnit.getComplexity(status) == UMEASURE_UNIT_MIXED);
+ U_ASSERT(fillIn != nullptr);
+
+ LocalArray<MeasureUnit> individualUnits =
+ mixedUnit.splitToSingleUnits(fillIn->fMixedUnitCount, status);
+ fillIn->fMixedUnitData.adoptInstead(new UnicodeString[fillIn->fMixedUnitCount * ARRAY_LENGTH]);
+ for (int32_t i = 0; i < fillIn->fMixedUnitCount; i++) {
+ // Grab data for each of the components.
+ UnicodeString *unitData = &fillIn->fMixedUnitData[i * ARRAY_LENGTH];
+ getMeasureData(loc, individualUnits[i], width, unitData, status);
+ }
+
+ UListFormatterWidth listWidth = ULISTFMT_WIDTH_SHORT;
+ if (width == UNUM_UNIT_WIDTH_NARROW) {
+ listWidth = ULISTFMT_WIDTH_NARROW;
+ } else if (width == UNUM_UNIT_WIDTH_FULL_NAME) {
+ // This might be the same as SHORT in most languages:
+ listWidth = ULISTFMT_WIDTH_WIDE;
+ }
+ fillIn->fListFormatter.adoptInsteadAndCheckErrorCode(
+ ListFormatter::createInstance(loc, ULISTFMT_TYPE_UNITS, listWidth, status), status);
+ fillIn->rules = rules;
+ fillIn->parent = parent;
+
+ // We need a localised NumberFormatter for the integers of the bigger units
+ // (providing Arabic numerals, for example).
+ fillIn->fIntegerFormatter = NumberFormatter::withLocale(loc);
+}
+
+void MixedUnitLongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,
+ UErrorCode &status) const {
+ U_ASSERT(fMixedUnitCount > 1);
+ if (parent != nullptr) {
+ parent->processQuantity(quantity, micros, status);
+ }
+ micros.modOuter = getMixedUnitModifier(quantity, micros, status);
+}
+
+const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &quantity,
+ MicroProps µs,
+ UErrorCode &status) const {
+ // TODO(icu-units#21): mixed units without usage() is not yet supported.
+ // That should be the only reason why this happens, so delete this whole if
+ // once fixed:
+ if (micros.mixedMeasuresCount == 0) {
+ status = U_UNSUPPORTED_ERROR;
+ return µs.helpers.emptyWeakModifier;
+ }
+ U_ASSERT(micros.mixedMeasuresCount > 0);
+ // mixedMeasures does not contain the last value:
+ U_ASSERT(fMixedUnitCount == micros.mixedMeasuresCount + 1);
+ U_ASSERT(fListFormatter.isValid());
+
+ // Algorithm:
+ //
+ // For the mixed-units measurement of: "3 yard, 1 foot, 2.6 inch", we should
+ // find "3 yard" and "1 foot" in micros.mixedMeasures.
+ //
+ // Obtain long-names with plural forms corresponding to measure values:
+ // * {0} yards, {0} foot, {0} inches
+ //
+ // Format the integer values appropriately and modify with the format
+ // strings:
+ // - 3 yards, 1 foot
+ //
+ // Use ListFormatter to combine, with one placeholder:
+ // - 3 yards, 1 foot and {0} inches
+ //
+ // Return a SimpleModifier for this pattern, letting the rest of the
+ // pipeline take care of the remaining inches.
+
+ LocalArray<UnicodeString> outputMeasuresList(new UnicodeString[fMixedUnitCount], status);
+ if (U_FAILURE(status)) {
+ return µs.helpers.emptyWeakModifier;
+ }
+
+ for (int32_t i = 0; i < micros.mixedMeasuresCount; i++) {
+ DecimalQuantity fdec;
+ fdec.setToLong(micros.mixedMeasures[i]);
+ StandardPlural::Form pluralForm = utils::getStandardPlural(rules, fdec);
+
+ UnicodeString simpleFormat =
+ getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], pluralForm, status);
+ SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status);
+
+ UnicodeString num;
+ auto appendable = UnicodeStringAppendable(num);
+ fIntegerFormatter.formatDecimalQuantity(fdec, status).appendTo(appendable, status);
+ compiledFormatter.format(num, outputMeasuresList[i], status);
+ }
+
+ UnicodeString *finalSimpleFormats = &fMixedUnitData[(fMixedUnitCount - 1) * ARRAY_LENGTH];
+ StandardPlural::Form finalPlural = utils::getPluralSafe(micros.rounder, rules, quantity, status);
+ UnicodeString finalSimpleFormat = getWithPlural(finalSimpleFormats, finalPlural, status);
+ SimpleFormatter finalFormatter(finalSimpleFormat, 0, 1, status);
+ finalFormatter.format(UnicodeString(u"{0}"), outputMeasuresList[fMixedUnitCount - 1], status);
+
+ // Combine list into a "premixed" pattern
+ UnicodeString premixedFormatPattern;
+ fListFormatter->format(outputMeasuresList.getAlias(), fMixedUnitCount, premixedFormatPattern,
+ status);
+ SimpleFormatter premixedCompiled(premixedFormatPattern, 0, 1, status);
+ if (U_FAILURE(status)) {
+ return µs.helpers.emptyWeakModifier;
+ }
+
+ // Return a SimpleModifier for the "premixed" pattern
+ micros.helpers.mixedUnitModifier =
+ SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, finalPlural});
+ return µs.helpers.mixedUnitModifier;
+}
+
+const Modifier *MixedUnitLongNameHandler::getModifier(Signum /*signum*/,
+ StandardPlural::Form /*plural*/) const {
+ // TODO(units): investigate this method when investigating where
+ // LongNameHandler::getModifier() gets used. To be sure it remains
+ // unreachable:
+ UPRV_UNREACHABLE;
+ return nullptr;
+}
+
LongNameMultiplexer *
LongNameMultiplexer::forMeasureUnits(const Locale &loc, const MaybeStackVector<MeasureUnit> &units,
const UNumberUnitWidth &width, const PluralRules *rules,
return nullptr;
}
U_ASSERT(units.length() > 0);
+ if (result->fHandlers.resize(units.length()) == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
result->fMeasureUnits.adoptInstead(new MeasureUnit[units.length()]);
for (int32_t i = 0, length = units.length(); i < length; i++) {
- // Create empty new LongNameHandler:
- LongNameHandler *lnh =
- result->fLongNameHandlers.emplaceBackAndCheckErrorCode(status);
- result->fMeasureUnits[i] = *units[i];
- // Fill in LongNameHandler:
- LongNameHandler::forMeasureUnit(loc, *units[i],
- MeasureUnit(), // TODO(units): deal with COMPOUND and MIXED units
- width, rules, NULL, lnh, status);
+ const MeasureUnit& unit = *units[i];
+ result->fMeasureUnits[i] = unit;
+ if (unit.getComplexity(status) == UMEASURE_UNIT_MIXED) {
+ MixedUnitLongNameHandler *mlnh = result->fMixedUnitHandlers.createAndCheckErrorCode(status);
+ MixedUnitLongNameHandler::forMeasureUnit(loc, unit, width, rules, NULL, mlnh, status);
+ result->fHandlers[i] = mlnh;
+ } else {
+ LongNameHandler *lnh = result->fLongNameHandlers.createAndCheckErrorCode(status);
+ LongNameHandler::forMeasureUnit(loc, unit, MeasureUnit(), width, rules, NULL, lnh, status);
+ result->fHandlers[i] = lnh;
+ }
if (U_FAILURE(status)) {
return nullptr;
}
fParent->processQuantity(quantity, micros, status);
// Call the correct LongNameHandler based on outputUnit
- for (int i = 0; i < fLongNameHandlers.length(); i++) {
+ for (int i = 0; i < fHandlers.getCapacity(); i++) {
if (fMeasureUnits[i] == micros.outputUnit) {
- fLongNameHandlers[i]->processQuantity(quantity, micros, status);
+ fHandlers[i]->processQuantity(quantity, micros, status);
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;
#define __NUMBER_LONGNAMES_H__
#include "cmemory.h"
+#include "unicode/listformatter.h"
#include "unicode/uversion.h"
#include "number_utils.h"
#include "number_modifiers.h"
forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, const PluralRules *rules,
const MicroPropsGenerator *parent, UErrorCode &status);
+ /**
+ * Construct a localized LongNameHandler for the specified MeasureUnit.
+ *
+ * Compound units can be constructed via `unit` and `perUnit`. Both of these
+ * must then be built-in units.
+ *
+ * Mixed units are not supported, use MixedUnitLongNameHandler::forMeasureUnit.
+ *
+ * This function uses a fillIn intead of returning a pointer, because we
+ * want to fill in instances in a MemoryPool (which cannot adopt pointers it
+ * didn't create itself).
+ *
+ * @param loc The desired locale.
+ * @param unit The measure unit to construct a LongNameHandler for. If
+ * `perUnit` is also defined, `unit` must not be a mixed unit.
+ * @param perUnit If `unit` is a mixed unit, `perUnit` must be "none".
+ * @param width Specifies the desired unit rendering.
+ * @param rules Does not take ownership.
+ * @param parent Does not take ownership.
+ * @param fillIn Required.
+ */
static void forMeasureUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
const UNumberUnitWidth &width, const PluralRules *rules,
const MicroPropsGenerator *parent, LongNameHandler *fillIn,
UErrorCode &status);
+ /**
+ * Selects the plural-appropriate Modifier from the set of fModifiers based
+ * on the plural form.
+ */
void
processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE;
+ // TODO(units): investigate whether we might run into Mixed Unit trouble
+ // with this. This override for ModifierStore::getModifier does not support
+ // mixed units: investigate under which circumstances it gets called (check
+ // both ImmutablePatternModifier and in NumberRangeFormatterImpl).
const Modifier* getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE;
private:
+ // A set of pre-computed modifiers, one for each plural form.
SimpleModifier fModifiers[StandardPlural::Form::COUNT];
// Not owned
const PluralRules *rules;
LongNameHandler() : rules(nullptr), parent(nullptr) {
}
- friend class MemoryPool<LongNameHandler>; // To enable emplaceBack();
+ // Enables MemoryPool<LongNameHandler>::emplaceBack(): requires access to
+ // the private constructors.
+ friend class MemoryPool<LongNameHandler>;
+
+ // Allow macrosToMicroGenerator to call the private default constructor.
friend class NumberFormatterImpl;
+ // Fills in LongNameHandler fields for formatting compound units identified
+ // via `unit` and `perUnit`. Both `unit` and `perUnit` need to be built-in
+ // units (for which data exists).
static void forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
const UNumberUnitWidth &width, const PluralRules *rules,
const MicroPropsGenerator *parent, LongNameHandler *fillIn,
UErrorCode &status);
+ // Sets fModifiers to use the patterns from `simpleFormats`.
void simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, UErrorCode &status);
+
+ // Sets fModifiers to a combination of `leadFormats` (one per plural form)
+ // and `trailFormat` appended to each.
+ //
+ // With a leadFormat of "{0}m" and a trailFormat of "{0}/s", it produces a
+ // pattern of "{0}m/s" by inserting the leadFormat pattern into trailFormat.
void multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat,
Field field, UErrorCode &status);
};
-const int MAX_PREFS_COUNT = 10;
+// Similar to LongNameHandler, but only for MIXED units.
+class MixedUnitLongNameHandler : public MicroPropsGenerator, public ModifierStore, public UMemory {
+ public:
+ /**
+ * Construct a localized MixedUnitLongNameHandler for the specified
+ * MeasureUnit. It must be a MIXED unit.
+ *
+ * This function uses a fillIn intead of returning a pointer, because we
+ * want to fill in instances in a MemoryPool (which cannot adopt pointers it
+ * didn't create itself).
+ *
+ * @param loc The desired locale.
+ * @param mixedUnit The mixed measure unit to construct a
+ * MixedUnitLongNameHandler for.
+ * @param width Specifies the desired unit rendering.
+ * @param rules Does not take ownership.
+ * @param parent Does not take ownership.
+ * @param fillIn Required.
+ */
+ static void forMeasureUnit(const Locale &loc, const MeasureUnit &mixedUnit,
+ const UNumberUnitWidth &width, const PluralRules *rules,
+ const MicroPropsGenerator *parent, MixedUnitLongNameHandler *fillIn,
+ UErrorCode &status);
+
+ /**
+ * Produces a plural-appropriate Modifier for a mixed unit: `quantity` is
+ * taken as the final smallest unit, while the larger unit values must be
+ * provided via `micros.mixedMeasures`.
+ */
+ void processQuantity(DecimalQuantity &quantity, MicroProps µs,
+ UErrorCode &status) const U_OVERRIDE;
+
+ // Required for ModifierStore. And ModifierStore is required by
+ // SimpleModifier constructor's last parameter. We assert his will never get
+ // called though.
+ const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE;
+
+ private:
+ // Not owned
+ const PluralRules *rules;
+ // Not owned
+ const MicroPropsGenerator *parent;
+
+ // Total number of units in the MeasureUnit this LongNameHandler was
+ // configured for: for "foot-and-inch", this will be 2. (If not a mixed unit,
+ // this will be 1.)
+ int32_t fMixedUnitCount = 1;
+ // If this LongNameHandler is for a mixed unit, this stores unit data for
+ // each of the individual units. For each unit, it stores ARRAY_LENGTH
+ // strings, as returned by getMeasureData. (Each unit with index `i` has
+ // ARRAY_LENGTH strings starting at index `i*ARRAY_LENGTH` in this array.)
+ LocalArray<UnicodeString> fMixedUnitData;
+ // A localized NumberFormatter used to format the integer-valued bigger
+ // units of Mixed Unit measurements.
+ LocalizedNumberFormatter fIntegerFormatter;
+ // A localised list formatter for joining mixed units together.
+ LocalPointer<ListFormatter> fListFormatter;
+
+ MixedUnitLongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent)
+ : rules(rules), parent(parent) {
+ }
+
+ MixedUnitLongNameHandler() : rules(nullptr), parent(nullptr) {
+ }
+
+ // Allow macrosToMicroGenerator to call the private default constructor.
+ friend class NumberFormatterImpl;
+
+ // Enables MemoryPool<LongNameHandler>::emplaceBack(): requires access to
+ // the private constructors.
+ friend class MemoryPool<MixedUnitLongNameHandler>;
+
+ // Fills in LongNameHandler fields for formatting mixed units. Each unit in
+ // a mixed unit must be a built-in unit.
+ static void forMixedUnit(const Locale &loc, const MeasureUnit &unit, const UNumberUnitWidth &width,
+ const PluralRules *rules, const MicroPropsGenerator *parent,
+ MixedUnitLongNameHandler *fillIn, UErrorCode &status);
+
+ // For a mixed unit, returns a Modifier that takes only one parameter: the
+ // smallest and final unit of the set. The bigger units' values and labels
+ // get baked into this Modifier, together with the unit label of the final
+ // unit.
+ const Modifier *getMixedUnitModifier(DecimalQuantity &quantity, MicroProps µs,
+ UErrorCode &status) const;
+};
/**
* A MicroPropsGenerator that multiplexes between different LongNameHandlers,
- * depending on the outputUnit (micros.helpers.outputUnit should be set earlier
- * in the chain).
+ * depending on the outputUnit.
+ *
+ * See processQuantity() for the input requirements.
*/
class LongNameMultiplexer : public MicroPropsGenerator, public UMemory {
public:
- // FIXME: docstring?
+ // Produces a multiplexer for LongNameHandlers, one for each unit in
+ // `units`. An individual unit might be a mixed unit.
static LongNameMultiplexer *forMeasureUnits(const Locale &loc,
const MaybeStackVector<MeasureUnit> &units,
const UNumberUnitWidth &width, const PluralRules *rules,
const MicroPropsGenerator *parent, UErrorCode &status);
+ // The output unit must be provided via `micros.outputUnit`, it must match
+ // one of the units provided to the factory function.
void processQuantity(DecimalQuantity &quantity, MicroProps µs,
UErrorCode &status) const U_OVERRIDE;
* earlier MicroPropsGenerators in the chain, LongNameMultiplexer keeps the
* parent link, while the LongNameHandlers are given no parents.
*/
- MaybeStackVector<LongNameHandler> fLongNameHandlers;
+ MemoryPool<LongNameHandler> fLongNameHandlers;
+ MemoryPool<MixedUnitLongNameHandler> fMixedUnitHandlers;
+ // Unowned pointers to instances owned by MaybeStackVectors.
+ MaybeStackArray<MicroPropsGenerator *, 8> fHandlers;
+ // Each MeasureUnit corresponds to the same-index MicroPropsGenerator
+ // pointed to in fHandlers.
LocalArray<MeasureUnit> fMeasureUnits;
+
const MicroPropsGenerator *fParent;
LongNameMultiplexer(const MicroPropsGenerator *parent) : fParent(parent) {
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;
precision = Precision::constructCurrency(currencyUsage);
}
if (!precision.isBogus()) {
- precision.fRoundingMode = roundingMode;
+ macros.roundingMode = roundingMode;
macros.precision = precision;
}
// TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
macros.precision = Precision::constructSignificant(minSig_, maxSig_);
}
- macros.precision.fRoundingMode = roundingMode;
+ macros.roundingMode = roundingMode;
}
}
U_NAMESPACE_BEGIN namespace number {
namespace impl {
+/**
+ * A copyable container for the integer values of mixed unit measurements.
+ *
+ * If memory allocation fails during copying, no values are copied and status is
+ * set to U_MEMORY_ALLOCATION_ERROR.
+ */
+class IntMeasures : public MaybeStackArray<int64_t, 2> {
+ public:
+ /**
+ * Default constructor initializes with internal T[stackCapacity] buffer.
+ *
+ * Stack Capacity: most mixed units are expected to consist of two or three
+ * subunits, so one or two integer measures should be enough.
+ */
+ IntMeasures() : MaybeStackArray<int64_t, 2>() {
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * If memory allocation fails during copying, no values are copied and
+ * status is set to U_MEMORY_ALLOCATION_ERROR.
+ */
+ IntMeasures(const IntMeasures &other) : MaybeStackArray<int64_t, 2>() {
+ this->operator=(other);
+ }
+
+ // Assignment operator
+ IntMeasures &operator=(const IntMeasures &rhs) {
+ if (this == &rhs) {
+ return *this;
+ }
+ copyFrom(rhs, status);
+ return *this;
+ }
+
+ /** Move constructor */
+ IntMeasures(IntMeasures &&src) = default;
+
+ /** Move assignment */
+ IntMeasures &operator=(IntMeasures &&src) = default;
+
+ UErrorCode status = U_ZERO_ERROR;
+};
+
// TODO(units): generated by MicroPropsGenerator, but inherits from it too. Do
// we want to better document why? There's an explanation for processQuantity:
// * As MicroProps is the "base instance", this implementation of
// Note: This struct has no direct ownership of the following pointers.
const DecimalFormatSymbols* symbols;
+
+ // Pointers to Modifiers provided by the number formatting pipeline (when
+ // the value is known):
+
+ // A Modifier provided by LongNameHandler, used for currency long names and
+ // units. If there is no LongNameHandler needed, this should be an
+ // EmptyModifier. (This is typically the third modifier applied.)
const Modifier* modOuter;
+ // A Modifier for short currencies and compact notation. (This is typically
+ // the second modifier applied.)
const Modifier* modMiddle = nullptr;
+ // A Modifier provided by ScientificHandler, used for scientific notation.
+ // This is typically the first modifier applied.
const Modifier* modInner;
// The following "helper" fields may optionally be used during the MicroPropsGenerator.
// They live here to retain memory.
struct {
+ // The ScientificModifier for which ScientificHandler is responsible.
+ // ScientificHandler::processQuantity() modifies this Modifier.
ScientificModifier scientificModifier;
+ // EmptyModifier used for modOuter
EmptyModifier emptyWeakModifier{false};
+ // EmptyModifier used for modInner
EmptyModifier emptyStrongModifier{true};
MultiplierFormatHandler multiplier;
+ // A Modifier used for Mixed Units. When formatting mixed units,
+ // LongNameHandler assigns this Modifier.
+ SimpleModifier mixedUnitModifier;
} helpers;
- // The MeasureUnit with which the output measurement is represented.
+ // The MeasureUnit with which the output is represented. May also have
+ // UMEASURE_UNIT_MIXED complexity, in which case mixedMeasures comes into
+ // play.
MeasureUnit outputUnit;
+ // In the case of mixed units, this is the set of integer-only units
+ // *preceding* the final unit.
+ IntMeasures mixedMeasures;
+ // Number of mixedMeasures that have been populated
+ int32_t mixedMeasuresCount = 0;
+
MicroProps() = default;
MicroProps(const MicroProps& other) = default;
#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;
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 {
Precision Precision::unlimited() {
- return Precision(RND_NONE, {}, kDefaultMode);
+ return Precision(RND_NONE, {});
}
FractionPrecision Precision::integer() {
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) {
settings.fMaxSig = static_cast<digits_t>(maxSig);
PrecisionUnion union_;
union_.fracSig = settings;
- return {RND_SIGNIFICANT, union_, kDefaultMode};
+ return {RND_SIGNIFICANT, union_};
}
Precision
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) {
// 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_};
}
/** 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;
}
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
Precision fPrecision;
UNumberFormatRoundingMode fRoundingMode;
bool fPassThrough = true; // default value
+
+ // Permits access to fPrecision.
+ friend class UsagePrefsHandler;
+
+ // Permits access to fPrecision.
+ friend class UnitConversionHandler;
};
} 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
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);
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'.':
ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
+ U_ASSERT(U_SUCCESS(status));
///// Required options: /////
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)
}
// Need to do char <-> UChar conversion...
- U_ASSERT(U_SUCCESS(status));
CharString type;
SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
CharString subType;
return;
}
- // TODO(ICU-20941): Clean this up.
+ // Mixed units can only be represented by a full MeasureUnit instances, so
+ // we ignore macros.perUnit.
+ if (fullUnit.complexity == UMEASURE_UNIT_MIXED) {
+ macros.unit = std::move(fullUnit).build(status);
+ return;
+ }
+
+ // TODO(ICU-20941): Clean this up (see also
+ // https://github.com/icu-units/icu/issues/35).
for (int32_t i = 0; i < fullUnit.units.length(); i++) {
SingleUnitImpl* subUnit = fullUnit.units[i];
if (subUnit->dimensionality > 0) {
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&) {
} else if (utils::unitIsPermille(macros.unit)) {
sb.append(u"permille", -1);
return true;
- } else {
+ } else if (uprv_strcmp(macros.unit.getType(), "") != 0) {
sb.append(u"measure-unit/", -1);
blueprint_helpers::generateMeasureUnitOption(macros.unit, sb, status);
return true;
+ } else {
+ // TODO(icu-units#35): add support for not-built-in units.
+ status = U_UNSUPPORTED_ERROR;
+ return false;
}
}
}
}
-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));
void parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
+/**
+ * Parses unit identifiers like "meter-per-second" and "foot-and-inch", as
+ * specified via a "unit/" concise skeleton.
+ */
void parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
void parseUnitUsageOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status);
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
--- /dev/null
+// © 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;
+}
*/
class U_I18N_API MicroPropsGenerator {
public:
- virtual ~MicroPropsGenerator();
+ virtual ~MicroPropsGenerator() = default;
/**
* Considers the given {@link DecimalQuantity}, optionally mutates it, and
#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"
#include "unicode/platform.h"
#include "unicode/unum.h"
#include "unicode/urename.h"
+#include "units_data.h"
+using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
+using icu::StringSegment;
+using icu::units::ConversionRates;
// Copy constructor
Usage::Usage(const Usage &other) : fUsage(nullptr), fLength(other.fLength), fError(other.fError) {
return *this;
}
-// Move constructor - can it be improved by taking over src's "this" instead of
-// copying contents? Swapping pointers makes sense for heap objects but not for
-// stack objects.
-// *this = std::move(src);
+// Move constructor
Usage::Usage(Usage &&src) U_NOEXCEPT : fUsage(src.fUsage), fLength(src.fLength), fError(src.fError) {
// Take ownership away from src if necessary
src.fUsage = nullptr;
fUsage[fLength] = 0;
}
+void mixedMeasuresToMicros(const MaybeStackVector<Measure> &measures, DecimalQuantity *quantity,
+ MicroProps *micros, UErrorCode status) {
+ micros->mixedMeasuresCount = measures.length() - 1;
+ if (micros->mixedMeasuresCount > 0) {
+#ifdef U_DEBUG
+ U_ASSERT(micros->outputUnit.getComplexity(status) == UMEASURE_UNIT_MIXED);
+ U_ASSERT(U_SUCCESS(status));
+ // Check that we received measurements with the expected MeasureUnits:
+ int32_t singleUnitsCount;
+ LocalArray<MeasureUnit> singleUnits =
+ micros->outputUnit.splitToSingleUnits(singleUnitsCount, status);
+ U_ASSERT(U_SUCCESS(status));
+ U_ASSERT(measures.length() == singleUnitsCount);
+ for (int32_t i = 0; i < measures.length(); i++) {
+ U_ASSERT(measures[i]->getUnit() == singleUnits[i]);
+ }
+#endif
+ // Mixed units: except for the last value, we pass all values to the
+ // LongNameHandler via micros->mixedMeasures.
+ if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) {
+ if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ }
+ for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) {
+ micros->mixedMeasures[i] = measures[i]->getNumber().getInt64();
+ }
+ } else {
+ micros->mixedMeasuresCount = 0;
+ }
+ // The last value (potentially the only value) gets passed on via quantity.
+ quantity->setToDouble(measures[measures.length() - 1]->getNumber().getDouble());
+}
+
UsagePrefsHandler::UsagePrefsHandler(const Locale &locale,
- const MeasureUnit inputUnit,
+ const MeasureUnit &inputUnit,
const StringPiece usage,
const MicroPropsGenerator *parent,
UErrorCode &status)
quantity.roundToInfinity(); // Enables toDouble
const auto routed = fUnitsRouter.route(quantity.toDouble(), status);
- 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;
}
+ const MaybeStackVector<Measure>& routedUnits = routed.measures;
+ micros.outputUnit = routed.outputUnit.copy(status).build(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ mixedMeasuresToMicros(routedUnits, &quantity, µs, status);
+
+ 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;
+}
+
+UnitConversionHandler::UnitConversionHandler(const MeasureUnit &unit, const MicroPropsGenerator *parent,
+ UErrorCode &status)
+ : fOutputUnit(unit), fParent(parent) {
+ MeasureUnitImpl temp;
+ const MeasureUnitImpl &outputUnit = MeasureUnitImpl::forMeasureUnit(unit, temp, status);
+ const MeasureUnitImpl *inputUnit = &outputUnit;
+ MaybeStackVector<MeasureUnitImpl> singleUnits;
+ U_ASSERT(outputUnit.complexity == UMEASURE_UNIT_MIXED);
+ // When we wish to support unit conversion, replace the above assert with this if:
+ // if (outputUnit.complexity == UMEASURE_UNIT_MIXED) {
+ {
+ singleUnits = outputUnit.extractIndividualUnits(status);
+ U_ASSERT(singleUnits.length() > 0);
+ inputUnit = singleUnits[0];
+ }
+ // TODO: this should become an initOnce thing? Review with other
+ // ConversionRates usages.
+ ConversionRates conversionRates(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ fUnitConverter.adoptInsteadAndCheckErrorCode(
+ new ComplexUnitsConverter(*inputUnit, outputUnit, conversionRates, status), status);
+}
+
+void UnitConversionHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,
+ UErrorCode &status) const {
+ fParent->processQuantity(quantity, micros, status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ quantity.roundToInfinity(); // Enables toDouble
+ MaybeStackVector<Measure> measures = fUnitConverter->convert(quantity.toDouble(), status);
+ micros.outputUnit = fOutputUnit;
+ if (U_FAILURE(status)) {
+ return;
+ }
+
+ mixedMeasuresToMicros(measures, &quantity, µs, status);
+
+ // TODO: add tests to explore behaviour that may suggest a more
+ // human-centric default rounder?
+ // if (micros.rounder.fPrecision.isBogus()) {
+ // micros.rounder.fPrecision = Precision::integer().withMinDigits(2);
+ // }
}
#endif /* #if !UCONFIG_NO_FORMATTING */
#include "cmemory.h"
#include "number_types.h"
+#include "unicode/listformatter.h"
+#include "unicode/localpointer.h"
#include "unicode/locid.h"
#include "unicode/measunit.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
-#include "unitsrouter.h"
+#include "units_converter.h"
+#include "units_router.h"
U_NAMESPACE_BEGIN
-namespace number {
-namespace impl {
+using ::icu::units::ComplexUnitsConverter;
using ::icu::units::UnitsRouter;
+namespace number {
+namespace impl {
+
/**
* A MicroPropsGenerator which uses UnitsRouter to produce output converted to a
* MeasureUnit appropriate for a particular localized usage: see
*/
class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory {
public:
- UsagePrefsHandler(const Locale &locale, const MeasureUnit inputUnit, const StringPiece usage,
+ UsagePrefsHandler(const Locale &locale, const MeasureUnit &inputUnit, const StringPiece usage,
const MicroPropsGenerator *parent, UErrorCode &status);
/**
- * Obtains the appropriate output value, MeasurementUnit and
+ * Obtains the appropriate output value, MeasureUnit and
* rounding/precision behaviour from the UnitsRouter.
+ *
+ * The output unit is passed on to the LongNameHandler via
+ * micros.outputUnit.
*/
void processQuantity(DecimalQuantity &quantity, MicroProps µs,
UErrorCode &status) const U_OVERRIDE;
private:
UnitsRouter fUnitsRouter;
const MicroPropsGenerator *fParent;
+
+ static Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, UErrorCode status);
+};
+
+} // namespace impl
+} // namespace number
+
+// Export explicit template instantiations of LocalPointerBase and LocalPointer.
+// This is required when building DLLs for Windows. (See datefmt.h,
+// collationiterator.h, erarules.h and others for similar examples.)
+//
+// Note: These need to be outside of the number::impl namespace, or Clang will
+// generate a compile error.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+#if defined(_MSC_VER)
+// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!=
+#pragma warning(push)
+#pragma warning(disable: 4661)
+#endif
+template class U_I18N_API LocalPointerBase<ComplexUnitsConverter>;
+template class U_I18N_API LocalPointer<ComplexUnitsConverter>;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+#endif
+
+namespace number {
+namespace impl {
+
+/**
+ * A MicroPropsGenerator which converts a measurement from a simple MeasureUnit
+ * to a Mixed MeasureUnit.
+ */
+class U_I18N_API UnitConversionHandler : public MicroPropsGenerator, public UMemory {
+ public:
+ /**
+ * Constructor.
+ *
+ * @param unit Specifies both the input and output MeasureUnit: if it is a
+ * MIXED unit, the input MeasureUnit will be just the biggest unit of
+ * the sequence.
+ * @param parent The parent MicroPropsGenerator.
+ * @param status Receives status.
+ */
+ UnitConversionHandler(const MeasureUnit &unit, const MicroPropsGenerator *parent,
+ UErrorCode &status);
+
+ /**
+ * Obtains the appropriate output values from the Unit Converter.
+ */
+ void processQuantity(DecimalQuantity &quantity, MicroProps µs,
+ UErrorCode &status) const U_OVERRIDE;
+ private:
+ MeasureUnit fOutputUnit;
+ LocalPointer<ComplexUnitsConverter> fUnitConverter;
+ const MicroPropsGenerator *fParent;
};
} // namespace impl
collationtailoring.cpp
collationweights.cpp
compactdecimalformat.cpp
-complexunitsconverter.cpp
coptccal.cpp
cpdtrans.cpp
csdetect.cpp
number_rounding.cpp
number_scientific.cpp
number_skeletons.cpp
+number_symbolswrapper.cpp
number_usageprefs.cpp
number_utils.cpp
numfmt.cpp
umsg.cpp
unesctrn.cpp
uni2name.cpp
-unitconverter.cpp
-unitsdata.cpp
-unitsrouter.cpp
+units_data.cpp
+units_complexconverter.cpp
+units_converter.cpp
+units_router.cpp
unum.cpp
unumsys.cpp
upluralrules.cpp
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;
static CurrencyPrecision constructCurrency(UCurrencyUsage usage);
- static Precision constructPassThrough();
-
// To allow MacroProps/MicroProps to initialize bogus instances:
friend struct impl::MacroProps;
friend struct impl::MicroProps;
// 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;
};
#include <cmath>
#include "cmemory.h"
-#include "complexunitsconverter.h"
#include "uarrsort.h"
#include "uassert.h"
#include "unicode/fmtable.h"
#include "unicode/localpointer.h"
#include "unicode/measunit.h"
#include "unicode/measure.h"
-#include "unitconverter.h"
+#include "units_complexconverter.h"
+#include "units_converter.h"
U_NAMESPACE_BEGIN
namespace units {
U_ASSERT(units_.length() != 0);
+ // Save the desired order of output units before we sort units_
+ for (int32_t i = 0; i < units_.length(); i++) {
+ outputUnits_.emplaceBackAndCheckErrorCode(status, units_[i]->copy(status).build(status));
+ }
+
// NOTE:
// This comparator is used to sort the units in a descending order. Therefore, we return -1 if
// the left is bigger than right and so on.
}
MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UErrorCode &status) const {
+ // TODO(icu-units#63): test negative numbers!
+ // TODO(hugovdm): return an error for "foot-and-foot"?
MaybeStackVector<Measure> result;
for (int i = 0, n = unitConverters_.length(); i < n; ++i) {
quantity = (*unitConverters_[i]).convert(quantity);
if (i < n - 1) {
- int64_t newQuantity = floor(quantity);
- Formattable formattableNewQuantity(newQuantity);
+ // The double type has 15 decimal digits of precision. For choosing
+ // whether to use the current unit or the next smaller unit, we
+ // therefore nudge up the number with which the thresholding
+ // decision is made. However after the thresholding, we use the
+ // original values to ensure unbiased accuracy (to the extent of
+ // double's capabilities).
+ int64_t roundedQuantity = floor(quantity * (1 + DBL_EPSILON));
+ Formattable formattableNewQuantity(roundedQuantity);
// NOTE: Measure would own its MeasureUnit.
MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
// Keep the residual of the quantity.
// For example: `3.6 feet`, keep only `0.6 feet`
- quantity -= newQuantity;
+ //
+ // When the calculation is near enough +/- DBL_EPSILON, we round to
+ // zero. (We also ensure no negative values here.)
+ if ((quantity - roundedQuantity) / quantity < DBL_EPSILON) {
+ quantity = 0;
+ } else {
+ quantity -= roundedQuantity;
+ }
} else { // LAST ELEMENT
Formattable formattableQuantity(quantity);
}
}
+ MaybeStackVector<Measure> orderedResult;
+ int32_t unitsCount = outputUnits_.length();
+ U_ASSERT(unitsCount == units_.length());
+ Measure **arr = result.getAlias();
+ // O(N^2) is fine: mixed units' unitsCount is usually 2 or 3.
+ for (int32_t i = 0; i < unitsCount; i++) {
+ for (int32_t j = i; j < unitsCount; j++) {
+ // Find the next expected unit, and swap it into place.
+ if (result[j]->getUnit() == *outputUnits_[i]) {
+ if (j != i) {
+ Measure *tmp = arr[j];
+ arr[j] = arr[i];
+ arr[i] = tmp;
+ }
+ }
+ }
+ }
+
return result;
}
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
-#ifndef __COMPLEXUNITSCONVERTER_H__
-#define __COMPLEXUNITSCONVERTER_H__
+#ifndef __UNITS_COMPLEXCONVERTER_H__
+#define __UNITS_COMPLEXCONVERTER_H__
#include "cmemory.h"
#include "measunit_impl.h"
#include "unicode/errorcode.h"
#include "unicode/measure.h"
-#include "unitconverter.h"
-#include "unitsdata.h"
+#include "units_converter.h"
+#include "units_data.h"
U_NAMESPACE_BEGIN
+// Export explicit template instantiations of MaybeStackArray, MemoryPool and
+// MaybeStackVector. This is required when building DLLs for Windows. (See
+// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
+//
+// Note: These need to be outside of the units namespace, or Clang will generate
+// a compile error.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+template class U_I18N_API MaybeStackArray<units::UnitConverter*, 8>;
+template class U_I18N_API MemoryPool<units::UnitConverter, 8>;
+template class U_I18N_API MaybeStackVector<units::UnitConverter, 8>;
+template class U_I18N_API MaybeStackArray<MeasureUnitImpl*, 8>;
+template class U_I18N_API MemoryPool<MeasureUnitImpl, 8>;
+template class U_I18N_API MaybeStackVector<MeasureUnitImpl, 8>;
+template class U_I18N_API MaybeStackArray<MeasureUnit*, 8>;
+template class U_I18N_API MemoryPool<MeasureUnit, 8>;
+template class U_I18N_API MaybeStackVector<MeasureUnit, 8>;
+#endif
+
namespace units {
/**
* single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
* instances of the `UnitConverter` to perform the conversion.
*/
-class U_I18N_API ComplexUnitsConverter : UMemory {
+class U_I18N_API ComplexUnitsConverter : public UMemory {
public:
/**
* Constructor of `ComplexUnitsConverter`.
private:
MaybeStackVector<UnitConverter> unitConverters_;
+ // Individual units of mixed units, sorted big to small
MaybeStackVector<MeasureUnitImpl> units_;
+ // Individual units of mixed units, sorted in desired output order
+ MaybeStackVector<MeasureUnit> outputUnits_;
};
} // namespace units
U_NAMESPACE_END
-#endif //__COMPLEXUNITSCONVERTER_H__
+#endif //__UNITS_COMPLEXCONVERTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */
#include "unicode/errorcode.h"
#include "unicode/localpointer.h"
#include "unicode/stringpiece.h"
-#include "unitconverter.h"
+#include "units_converter.h"
#include <algorithm>
#include <cmath>
#include <stdlib.h>
result = 1.0 / result;
}
- // TODO: remove the multiplication by 1.000,000,000,001 after using `decNumber`
-
- // Multiply the result by 1.000,000,000,001 to fix the deterioration from using `double` (the
- // deterioration is around 15 to 17 decimal digit).
- return result * 1.000000000001;
+ return result;
}
} // namespace units
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
-#ifndef __UNITCONVERTER_H__
-#define __UNITCONVERTER_H__
+#ifndef __UNITS_CONVERTER_H__
+#define __UNITS_CONVERTER_H__
#include "cmemory.h"
#include "measunit_impl.h"
#include "unicode/errorcode.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
-#include "unitconverter.h"
-#include "unitsdata.h"
+#include "units_converter.h"
+#include "units_data.h"
U_NAMESPACE_BEGIN
namespace units {
/**
* Represents the conversion rate between `source` and `target`.
*/
-struct ConversionRate : public UMemory {
+struct U_I18N_API ConversionRate : public UMemory {
const MeasureUnitImpl source;
const MeasureUnitImpl target;
double factorNum = 1;
} // namespace units
U_NAMESPACE_END
-#endif //__UNITCONVERTER_H__
+#endif //__UNITS_CONVERTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */
#include "uassert.h"
#include "unicode/unistr.h"
#include "unicode/ures.h"
-#include "unitsdata.h"
+#include "units_data.h"
#include "uresimp.h"
#include "util.h"
#include <utility>
} else if (uprv_strcmp(desired.usage.data(), "default") != 0) {
desired.usage.truncate(0).append("default", status);
} else {
- // TODO(icu-units/icu#36): reconsider consistency of errors.
- // Currently this U_MISSING_RESOURCE_ERROR propagates when a
- // U_NUMBER_SKELETON_SYNTAX_ERROR might be much more intuitive.
+ // "default" is not supposed to be missing for any valid category.
status = U_MISSING_RESOURCE_ERROR;
return -1;
}
idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
}
if (!foundRegion) {
+ // "001" is not supposed to be missing for any valid usage.
status = U_MISSING_RESOURCE_ERROR;
return -1;
}
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
-#ifndef __GETUNITSDATA_H__
-#define __GETUNITSDATA_H__
+#ifndef __UNITS_DATA_H__
+#define __UNITS_DATA_H__
#include <limits>
* Categories are found in `unitQuantities` in the `units` resource (see
* `units.txt`).
*
- * TODO(hugovdm): if we give unitsdata.cpp access to the functionality of
- * `extractCompoundBaseUnit` which is currently in unitconverter.cpp, we could
+ * TODO(hugovdm): if we give units_data.cpp access to the functionality of
+ * `extractCompoundBaseUnit` which is currently in units_converter.cpp, we could
* support all units for which there is a category. Does it make sense to move
- * that function to unitsdata.cpp?
+ * that function to units_data.cpp?
*/
CharString U_I18N_API getUnitCategory(const char *baseUnitIdentifier, UErrorCode &status);
CharString offset;
};
+} // namespace units
+
+// Export explicit template instantiations of MaybeStackArray, MemoryPool and
+// MaybeStackVector. This is required when building DLLs for Windows. (See
+// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
+//
+// Note: These need to be outside of the units namespace, or Clang will generate
+// a compile error.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+template class U_I18N_API MaybeStackArray<units::ConversionRateInfo*, 8>;
+template class U_I18N_API MemoryPool<units::ConversionRateInfo, 8>;
+template class U_I18N_API MaybeStackVector<units::ConversionRateInfo, 8>;
+#endif
+
+namespace units {
+
/**
* Returns ConversionRateInfo for all supported conversions.
*
bool *foundRegion) const;
};
+} // namespace units
+
+// Export explicit template instantiations of MaybeStackArray, MemoryPool and
+// MaybeStackVector. This is required when building DLLs for Windows. (See
+// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
+//
+// Note: These need to be outside of the units namespace, or Clang will generate
+// a compile error.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+template class U_I18N_API MaybeStackArray<units::UnitPreferenceMetadata*, 8>;
+template class U_I18N_API MemoryPool<units::UnitPreferenceMetadata, 8>;
+template class U_I18N_API MaybeStackVector<units::UnitPreferenceMetadata, 8>;
+template class U_I18N_API MaybeStackArray<units::UnitPreference*, 8>;
+template class U_I18N_API MemoryPool<units::UnitPreference, 8>;
+template class U_I18N_API MaybeStackVector<units::UnitPreference, 8>;
+#endif
+
+namespace units {
+
/**
* Unit Preferences information for various locales and usages.
*/
} // namespace units
U_NAMESPACE_END
-#endif //__GETUNITSDATA_H__
+#endif //__UNITS_DATA_H__
#endif /* #if !UCONFIG_NO_FORMATTING */
#include "number_decimalquantity.h"
#include "resource.h"
#include "unicode/measure.h"
-#include "unitsdata.h"
-#include "unitsrouter.h"
+#include "units_data.h"
+#include "units_router.h"
U_NAMESPACE_BEGIN
namespace units {
RouteResult UnitsRouter::route(double quantity, UErrorCode &status) const {
for (int i = 0, n = converterPreferences_.length(); i < n; i++) {
const auto &converterPreference = *converterPreferences_[i];
-
- if (converterPreference.converter.greaterThanOrEqual(quantity, converterPreference.limit)) {
- return RouteResult(converterPreference.converter.convert(quantity, status), //
- converterPreference.precision //
- );
+ if (converterPreference.converter.greaterThanOrEqual(quantity * (1 + DBL_EPSILON),
+ converterPreference.limit)) {
+ return RouteResult(converterPreference.converter.convert(quantity, status),
+ converterPreference.precision,
+ converterPreference.targetUnit.copy(status));
}
}
// In case of the `quantity` does not fit in any converter limit, use the last converter.
const auto &lastConverterPreference = (*converterPreferences_[converterPreferences_.length() - 1]);
- return RouteResult(lastConverterPreference.converter.convert(quantity, status), //
- lastConverterPreference.precision //
- );
+ return RouteResult(lastConverterPreference.converter.convert(quantity, status),
+ lastConverterPreference.precision,
+ lastConverterPreference.targetUnit.copy(status));
}
const MaybeStackVector<MeasureUnit> *UnitsRouter::getOutputUnits() const {
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
-#ifndef __UNITSROUTER_H__
-#define __UNITSROUTER_H__
+#ifndef __UNITS_ROUTER_H__
+#define __UNITS_ROUTER_H__
#include <limits>
#include "cmemory.h"
-#include "complexunitsconverter.h"
#include "measunit_impl.h"
#include "unicode/measunit.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
-#include "unitsdata.h"
+#include "units_complexconverter.h"
+#include "units_data.h"
U_NAMESPACE_BEGIN
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)
- : measures(std::move(measures)), precision(std::move(precision)) {}
+ // The output unit for this RouteResult. This may be a MIXED unit - for
+ // example: "yard-and-foot-and-inch", for which `measures` will have three
+ // elements.
+ MeasureUnitImpl outputUnit;
+
+ RouteResult(MaybeStackVector<Measure> measures, UnicodeString precision, MeasureUnitImpl outputUnit)
+ : measures(std::move(measures)), precision(std::move(precision)), outputUnit(std::move(outputUnit)) {}
};
/**
double limit;
UnicodeString precision;
+ // The output unit for this ConverterPreference. This may be a MIXED unit -
+ // for example: "yard-and-foot-and-inch".
+ MeasureUnitImpl targetUnit;
+
// In case there is no limit, the limit will be -inf.
ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
double limit, UnicodeString precision, const ConversionRates &ratesInfo,
UErrorCode &status)
: converter(source, complexTarget, ratesInfo, status), limit(limit),
- precision(std::move(precision)) {}
+ precision(std::move(precision)), targetUnit(complexTarget.copy(status)) {}
};
+} // namespace units
+
+// Export explicit template instantiations of MaybeStackArray, MemoryPool and
+// MaybeStackVector. This is required when building DLLs for Windows. (See
+// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
+//
+// Note: These need to be outside of the units namespace, or Clang will generate
+// a compile error.
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+template class U_I18N_API MaybeStackArray<units::ConverterPreference*, 8>;
+template class U_I18N_API MemoryPool<units::ConverterPreference, 8>;
+template class U_I18N_API MaybeStackVector<units::ConverterPreference, 8>;
+#endif
+
+namespace units {
+
/**
* `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to
* one of the complex units based on the limits.
} // namespace units
U_NAMESPACE_END
-#endif //__UNITSROUTER_H__
+#endif //__UNITS_ROUTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */
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
standardplural.o plurrule.o
deps
# FormattedNumber internals:
- number_representation format formatted_value_sbimpl
+ number_representation format formatted_value_sbimpl units
# PluralRules internals:
unifiedcache
- # Unit Formatting
- units
group: numberformatter
# ICU 60+ NumberFormatter API
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
+ listformatter number_representation number_output
+ numsys
+ number_usageprefs
+ number_rounding
+ number_symbolswrapper
uclean_i18n common
+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
number_skeletons.o number_capi.o number_asformat.o numrange_capi.o
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
stringenumeration errorcode
group: unitsformatter
- unitsdata.o unitconverter.o complexunitsconverter.o unitsrouter.o
+ units_data.o units_converter.o units_complexconverter.o units_router.o
deps
resourcebundle units_extra double_conversion number_representation formattable sort
numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \
formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \
-unitsdatatest.o unitstest.o unitsroutertest.o
+units_data_test.o units_router_test.o units_test.o
DEPS = $(OBJECTS:.o=.d)
<ClCompile Include="formattedvaluetest.cpp" />
<ClCompile Include="localebuildertest.cpp" />
<ClCompile Include="localematchertest.cpp" />
- <ClCompile Include="unitsdatatest.cpp" />
- <ClCompile Include="unitstest.cpp" />
- <ClCompile Include="unitsroutertest.cpp" />
+ <ClCompile Include="units_data_test.cpp" />
+ <ClCompile Include="units_router_test.cpp" />
+ <ClCompile Include="units_test.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="colldata.h" />
<ClCompile Include="localematchertest.cpp">
<Filter>locales & resources</Filter>
</ClCompile>
- <ClCompile Include="unitsdatatest.cpp">
+ <ClCompile Include="units_data_test.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="unitstest.cpp">
+ <ClCompile Include="units_router_test.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="unitsroutertest.cpp">
+ <ClCompile Include="units_test.cpp">
<Filter>formatting</Filter>
</ClCompile>
</ItemGroup>
void TestNumericTimeSomeSpecialFormats();
void TestIdentifiers();
void TestInvalidIdentifiers();
+ void TestParseToBuiltIn();
void TestKilogramIdentifier();
void TestCompoundUnitOperations();
void TestDimensionlessBehaviour();
TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
TESTCASE_AUTO(TestIdentifiers);
TESTCASE_AUTO(TestInvalidIdentifiers);
+ TESTCASE_AUTO(TestParseToBuiltIn);
TESTCASE_AUTO(TestKilogramIdentifier);
TESTCASE_AUTO(TestCompoundUnitOperations);
TESTCASE_AUTO(TestDimensionlessBehaviour);
// and falls back to fr for the "other" form.
IcuTestErrorCode errorCode(*this, "TestIndividualPluralFallback");
MeasureFormat mf("fr_CA", UMEASFMT_WIDTH_SHORT, errorCode);
+ if (errorCode.errIfFailureAndReset("MeasureFormat mf(...) failed.")) {
+ return;
+ }
LocalPointer<Measure> twoDeg(
new Measure(2.0, MeasureUnit::createGenericTemperature(errorCode), errorCode), errorCode);
+ if (errorCode.errIfFailureAndReset("Creating twoDeg failed.")) {
+ return;
+ }
UnicodeString expected = UNICODE_STRING_SIMPLE("2\\u00B0").unescape();
UnicodeString actual;
- assertEquals("2 deg temp in fr_CA", expected, mf.format(twoDeg.orphan(), actual, errorCode), TRUE);
+ // Formattable adopts the pointer
+ mf.format(Formattable(twoDeg.orphan()), actual, errorCode);
+ if (errorCode.errIfFailureAndReset("mf.format(...) failed.")) {
+ return;
+ }
+ assertEquals("2 deg temp in fr_CA", expected, actual, TRUE);
+ errorCode.errIfFailureAndReset("mf.format failed");
}
void MeasureFormatTest::Test20332_PersonUnits() {
}
}
+void MeasureFormatTest::TestParseToBuiltIn() {
+ IcuTestErrorCode status(*this, "TestParseToBuiltIn()");
+ const struct TestCase {
+ const char *identifier;
+ MeasureUnit expectedBuiltIn;
+ } cases[] = {
+ {"meter-per-second-per-second", MeasureUnit::getMeterPerSecondSquared()},
+ {"meter-per-second-second", MeasureUnit::getMeterPerSecondSquared()},
+ {"centimeter-centimeter", MeasureUnit::getSquareCentimeter()},
+ {"square-foot", MeasureUnit::getSquareFoot()},
+ {"pow2-inch", MeasureUnit::getSquareInch()},
+ {"milligram-per-deciliter", MeasureUnit::getMilligramPerDeciliter()},
+ {"pound-force-per-pow2-inch", MeasureUnit::getPoundPerSquareInch()},
+ {"yard-pow2-yard", MeasureUnit::getCubicYard()},
+ {"square-yard-yard", MeasureUnit::getCubicYard()},
+ };
+
+ for (auto &cas : cases) {
+ MeasureUnit fromIdent = MeasureUnit::forIdentifier(cas.identifier, status);
+ status.assertSuccess();
+ assertEquals("forIdentifier returns a normal built-in unit when it exists",
+ cas.expectedBuiltIn.getOffset(), fromIdent.getOffset());
+ assertEquals("type", cas.expectedBuiltIn.getType(), fromIdent.getType());
+ assertEquals("subType", cas.expectedBuiltIn.getSubtype(), fromIdent.getSubtype());
+ }
+}
+
// Kilogram is a "base unit", although it's also "gram" with a kilo- prefix.
// This tests that it is handled in the preferred manner.
void MeasureFormatTest::TestKilogramIdentifier() {
void unitMeasure();
void unitCompoundMeasure();
void unitUsage();
+ void unitUsageErrorCodes();
+ void unitUsageSkeletons();
void unitCurrency();
void unitPercent();
void percentParity();
void localPointerCAPI();
void toObject();
void toDecimalNumber();
+ void microPropsInternals();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
#include <memory>
#include "unicode/unum.h"
#include "unicode/numberformatter.h"
+#include "unicode/testlog.h"
+#include "unicode/utypes.h"
#include "number_asformat.h"
#include "number_types.h"
#include "number_utils.h"
-#include "numbertest.h"
-#include "unicode/utypes.h"
#include "number_utypes.h"
+#include "number_microprops.h"
+#include "numbertest.h"
using number::impl::UFormattedNumberData;
TESTCASE_AUTO(unitMeasure);
TESTCASE_AUTO(unitCompoundMeasure);
TESTCASE_AUTO(unitUsage);
+ TESTCASE_AUTO(unitUsageErrorCodes);
+ TESTCASE_AUTO(unitUsageSkeletons);
TESTCASE_AUTO(unitCurrency);
TESTCASE_AUTO(unitPercent);
if (!quick) {
TESTCASE_AUTO(localPointerCAPI);
TESTCASE_AUTO(toObject);
TESTCASE_AUTO(toDecimalNumber);
+ TESTCASE_AUTO(microPropsInternals);
TESTCASE_AUTO_END;
}
}
void NumberFormatterApiTest::unitMeasure() {
+ IcuTestErrorCode status(*this, "unitMeasure()");
+
assertFormatDescending(
u"Meters Short and unit() method",
u"measure-unit/length-meter",
Locale("es-MX"),
5,
u"5 a\u00F1os");
-}
-void NumberFormatterApiTest::unitUsage() {
- UnlocalizedNumberFormatter unloc_formatter =
- NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
+ // TODO(icu-units#35): skeleton generation.
+ assertFormatSingle(
+ u"Mixed unit",
+ nullptr,
+ u"unit/yard-and-foot-and-inch",
+ NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
+ Locale("en-US"),
+ 3.65,
+ "3 yd, 1 ft, 11.4 in");
- IcuTestErrorCode status(*this, "unitUsage()");
+ // TODO(icu-units#35): skeleton generation.
+ assertFormatSingle(
+ u"Mixed unit, Scientific",
+ nullptr,
+ u"unit/yard-and-foot-and-inch E0",
+ NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status))
+ .notation(Notation::scientific()),
+ Locale("en-US"),
+ 3.65,
+ "3 yd, 1 ft, 1.14E1 in");
- LocalizedNumberFormatter formatter = unloc_formatter.locale("en-ZA");
- FormattedNumber formattedNum = formatter.formatDouble(300, status);
- assertTrue(UnicodeString("unitUsage() en-ZA road, got outputUnit: \"") +
- formattedNum.getOutputUnit(status).getIdentifier() + "\"",
- MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
- assertEquals("unitUsage() en-ZA road", "300 m", formattedNum.toString(status));
- assertFormatDescendingBig(
- u"unitUsage() en-ZA road",
- u"measure-unit/length-meter usage/road",
- u"unit/meter usage/road",
- unloc_formatter,
- Locale("en-ZA"),
- u"87\u00A0650 km",
- u"8\u00A0765 km",
- u"877 km",
- u"88 km",
- u"8,8 km",
- u"877 m",
- u"88 m",
- u"8,8 m",
- u"0 m");
+ // TODO(icu-units#35): skeleton generation.
+ assertFormatSingle(
+ u"Mixed Unit (Narrow Version)",
+ nullptr,
+ u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
+ NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
+ .unitWidth(UNUM_UNIT_WIDTH_NARROW),
+ Locale("en-US"),
+ 4.28571,
+ u"4t 285kg 710g");
- formatter = unloc_formatter.locale("en-GB");
- formattedNum = formatter.formatDouble(300, status);
- 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));
- assertFormatDescendingBig(
- u"unitUsage() en-GB road",
- u"measure-unit/length-meter usage/road",
- u"unit/meter usage/road",
- unloc_formatter,
- Locale("en-GB"),
- u"54,463 mi",
- u"5,446 mi",
- u"545 mi",
- u"54 mi",
- u"5.4 mi",
- u"0.54 mi",
- u"96 yd",
- u"9.6 yd",
- u"0 yd");
+ // TODO(icu-units#35): skeleton generation.
+ assertFormatSingle(
+ u"Mixed Unit (Short Version)",
+ nullptr,
+ u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
+ NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
+ .unitWidth(UNUM_UNIT_WIDTH_SHORT),
+ Locale("en-US"),
+ 4.28571,
+ u"4 t, 285 kg, 710 g");
- formatter = unloc_formatter.locale("en-US");
- formattedNum = formatter.formatDouble(300, status);
- 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));
- assertFormatDescendingBig(
- u"unitUsage() en-US road",
- u"measure-unit/length-meter usage/road",
- u"unit/meter usage/road",
- unloc_formatter,
+ // TODO(icu-units#35): skeleton generation.
+ assertFormatSingle(
+ u"Mixed Unit (Full Name Version)",
+ nullptr,
+ u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
+ NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
+ .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
Locale("en-US"),
- u"54,463 mi",
- u"5,446 mi",
- u"545 mi",
- u"54 mi",
- u"5.4 mi",
- u"0.54 mi",
- u"288 ft",
- u"29 ft",
- u"0 ft");
+ 4.28571,
+ u"4 metric tons, 285 kilograms, 710 grams");
+
+// // TODO(icu-units#73): deal with this "1 foot 12 inches" problem.
+// // At the time of writing, this test would pass, but is commented out
+// // because it reflects undesired behaviour:
+// assertFormatSingle(
+// u"Demonstrating the \"1 foot 12 inches\" problem",
+// nullptr,
+// u"unit/foot-and-inch",
+// NumberFormatter::with()
+// .unit(MeasureUnit::forIdentifier("foot-and-inch", status))
+// .precision(Precision::maxSignificantDigits(4))
+// .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
+// Locale("en-US"),
+// 1.9999,
+// // This is undesireable but current behaviour:
+// u"1 foot, 12 inches");
}
void NumberFormatterApiTest::unitCompoundMeasure() {
+ IcuTestErrorCode status(*this, "unitCompoundMeasure()");
+
assertFormatDescending(
u"Meters Per Second Short (unit that simplifies) and perUnit method",
u"measure-unit/length-meter per-measure-unit/duration-second",
u"0.008765 m/s",
u"0 m/s");
+ // TODO(icu-units#35): does not normalize as desired: while "unit/*" does
+ // get split into unit/perUnit, ".unit(*)" and "measure-unit/*" don't:
+ assertFormatSingle(
+ u"Built-in unit, meter-per-second",
+ u"measure-unit/speed-meter-per-second",
+ u"~unit/meter-per-second",
+ NumberFormatter::with().unit(MeasureUnit::getMeterPerSecond()),
+ Locale("en-GB"),
+ 2.4,
+ u"2.4 m/s");
+
assertFormatDescending(
u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
u"measure-unit/mass-pound per-measure-unit/area-square-mile",
// u"0.08765 J/fur",
// u"0.008765 J/fur",
// u"0 J/fur");
+
+ // TODO(icu-units#59): THIS UNIT TEST DEMONSTRATES UNDESIREABLE BEHAVIOUR!
+ // When specifying built-in types, one can give both a unit and a perUnit.
+ // Resolving to a built-in unit does not always work.
+ //
+ // (Unit-testing philosophy: do we leave this enabled to demonstrate current
+ // behaviour, and changing behaviour in the future? Or comment it out to
+ // avoid asserting this is "correct"?)
+ assertFormatSingle(
+ u"DEMONSTRATING BAD BEHAVIOUR, TODO(icu-units#59)",
+ u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
+ u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
+ NumberFormatter::with()
+ .unit(MeasureUnit::getMeterPerSecond())
+ .perUnit(MeasureUnit::getSecond()),
+ Locale("en-GB"),
+ 2.4,
+ "2.4 m/s/s");
+
+ // Testing the rejection of invalid specifications
+
+ // If .unit() is not given a built-in type, .perUnit() is not allowed
+ // (because .unit is now flexible enough to handle compound units,
+ // .perUnit() is supported for backward compatibility).
+ LocalizedNumberFormatter nf = NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier("furlong-pascal", status))
+ .perUnit(METER)
+ .locale("en-GB");
+ status.assertSuccess(); // Error is only returned once we try to format.
+ FormattedNumber num = nf.formatDouble(2.4, status);
+ if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
+ errln(UnicodeString("Expected failure, got: \"") +
+ nf.formatDouble(2.4, status).toString(status) + "\".");
+ status.assertSuccess();
+ }
+
+ // .perUnit() may only be passed a built-in type, "square-second" is not a
+ // built-in type.
+ nf = NumberFormatter::with()
+ .unit(MeasureUnit::getMeter())
+ .perUnit(MeasureUnit::forIdentifier("square-second", status))
+ .locale("en-GB");
+ status.assertSuccess(); // Error is only returned once we try to format.
+ num = nf.formatDouble(2.4, status);
+ if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
+ errln(UnicodeString("Expected failure, got: \"") +
+ nf.formatDouble(2.4, status).toString(status) + "\".");
+ status.assertSuccess();
+ }
+}
+
+void NumberFormatterApiTest::unitUsage() {
+ IcuTestErrorCode status(*this, "unitUsage()");
+ UnlocalizedNumberFormatter unloc_formatter;
+ LocalizedNumberFormatter formatter;
+ FormattedNumber formattedNum;
+ UnicodeString uTestCase;
+
+ unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
+
+ uTestCase = u"unitUsage() en-ZA road";
+ formatter = unloc_formatter.locale("en-ZA");
+ formattedNum = formatter.formatDouble(321, status);
+ status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble");
+ assertTrue(
+ uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+ MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
+ assertEquals(uTestCase, "300 m", formattedNum.toString(status));
+ {
+ static const UFieldPosition expectedFieldPositions[] = {
+ {UNUM_INTEGER_FIELD, 0, 3},
+ {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
+ assertNumberFieldPositions(
+ (uTestCase + u" field positions").getTerminatedBuffer(),
+ formattedNum,
+ expectedFieldPositions,
+ UPRV_LENGTHOF(expectedFieldPositions));
+ }
+ assertFormatDescendingBig(
+ uTestCase.getTerminatedBuffer(),
+ u"measure-unit/length-meter usage/road",
+ u"unit/meter usage/road",
+ unloc_formatter,
+ Locale("en-ZA"),
+ u"87\u00A0650 km",
+ u"8\u00A0765 km",
+ u"876 km", // 6.5 rounds down, 7.5 rounds up.
+ u"88 km",
+ u"8,8 km",
+ u"900 m",
+ u"90 m",
+ u"10 m",
+ u"0 m");
+
+ uTestCase = u"unitUsage() en-GB road";
+ formatter = unloc_formatter.locale("en-GB");
+ formattedNum = formatter.formatDouble(321, status);
+ status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
+ assertTrue(
+ uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+ MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
+ status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
+ assertEquals(uTestCase, "350 yd", formattedNum.toString(status));
+ status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
+ {
+ static const UFieldPosition expectedFieldPositions[] = {
+ {UNUM_INTEGER_FIELD, 0, 3},
+ {UNUM_MEASURE_UNIT_FIELD, 4, 6}};
+ assertNumberFieldPositions(
+ (uTestCase + u" field positions").getTerminatedBuffer(),
+ formattedNum,
+ expectedFieldPositions,
+ UPRV_LENGTHOF(expectedFieldPositions));
+ }
+ assertFormatDescendingBig(
+ uTestCase.getTerminatedBuffer(),
+ u"measure-unit/length-meter usage/road",
+ u"unit/meter usage/road",
+ unloc_formatter,
+ Locale("en-GB"),
+ u"54,463 mi",
+ u"5,446 mi",
+ u"545 mi",
+ u"54 mi",
+ u"5.4 mi",
+ u"0.54 mi",
+ u"96 yd",
+ u"9.6 yd",
+ u"0 yd");
+
+ uTestCase = u"unitUsage() en-US road";
+ formatter = unloc_formatter.locale("en-US");
+ formattedNum = formatter.formatDouble(321, status);
+ status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
+ assertTrue(
+ uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+ MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
+ status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
+ assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status));
+ status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
+ {
+ static const UFieldPosition expectedFieldPositions[] = {
+ {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
+ {UNUM_INTEGER_FIELD, 0, 5},
+ {UNUM_MEASURE_UNIT_FIELD, 6, 8}};
+ assertNumberFieldPositions(
+ (uTestCase + u" field positions").getTerminatedBuffer(),
+ formattedNum,
+ expectedFieldPositions,
+ UPRV_LENGTHOF(expectedFieldPositions));
+ }
+ assertFormatDescendingBig(
+ uTestCase.getTerminatedBuffer(),
+ u"measure-unit/length-meter usage/road",
+ u"unit/meter usage/road",
+ unloc_formatter,
+ Locale("en-US"),
+ u"54,463 mi",
+ u"5,446 mi",
+ u"545 mi",
+ u"54 mi",
+ u"5.4 mi",
+ u"0.54 mi",
+ u"300 ft",
+ u"30 ft",
+ u"0 ft");
+
+ unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram());
+ uTestCase = u"unitUsage() en-GB person";
+ formatter = unloc_formatter.locale("en-GB");
+ formattedNum = formatter.formatDouble(80, status);
+ status.errIfFailureAndReset("unitUsage() en-GB person formatDouble");
+ assertTrue(
+ uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
+ MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status));
+ status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)");
+ assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status));
+ status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)");
+ {
+ static const UFieldPosition expectedFieldPositions[] = {
+ // // Desired output: TODO(icu-units#67)
+ // {UNUM_INTEGER_FIELD, 0, 2},
+ // {UNUM_MEASURE_UNIT_FIELD, 3, 5},
+ // {ULISTFMT_LITERAL_FIELD, 5, 6},
+ // {UNUM_INTEGER_FIELD, 7, 8},
+ // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
+ // {UNUM_FRACTION_FIELD, 9, 10},
+ // {UNUM_MEASURE_UNIT_FIELD, 11, 13}};
+
+ // Current output: rather no fields than wrong fields
+ {UNUM_INTEGER_FIELD, 7, 8},
+ {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
+ {UNUM_FRACTION_FIELD, 9, 10},
+ };
+ assertNumberFieldPositions(
+ (uTestCase + u" field positions").getTerminatedBuffer(),
+ formattedNum,
+ expectedFieldPositions,
+ UPRV_LENGTHOF(expectedFieldPositions));
+ }
+ assertFormatDescending(
+ uTestCase.getTerminatedBuffer(),
+ u"measure-unit/mass-kilogram usage/person",
+ u"unit/kilogram usage/person",
+ unloc_formatter,
+ Locale("en-GB"),
+ u"13,802 st, 7.2 lb",
+ u"1,380 st, 3.5 lb",
+ u"138 st, 0.35 lb",
+ u"13 st, 11 lb",
+ u"1 st, 5.3 lb",
+ u"1 lb, 15 oz",
+ u"0 lb, 3.1 oz",
+ u"0 lb, 0.31 oz",
+ u"0 lb, 0 oz");
+
+ assertFormatDescending(
+ uTestCase.getTerminatedBuffer(),
+ u"usage/person unit-width-narrow measure-unit/mass-kilogram",
+ u"usage/person unit-width-narrow unit/kilogram",
+ unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW),
+ Locale("en-GB"),
+ u"13,802st 7.2lb",
+ u"1,380st 3.5lb",
+ u"138st 0.35lb",
+ u"13st 11lb",
+ u"1st 5.3lb",
+ u"1lb 15oz",
+ u"0lb 3.1oz",
+ u"0lb 0.31oz",
+ u"0lb 0oz");
+
+ assertFormatDescending(
+ uTestCase.getTerminatedBuffer(),
+ u"usage/person unit-width-short measure-unit/mass-kilogram",
+ u"usage/person unit-width-short unit/kilogram",
+ unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT),
+ Locale("en-GB"),
+ u"13,802 st, 7.2 lb",
+ u"1,380 st, 3.5 lb",
+ u"138 st, 0.35 lb",
+ u"13 st, 11 lb",
+ u"1 st, 5.3 lb",
+ u"1 lb, 15 oz",
+ u"0 lb, 3.1 oz",
+ u"0 lb, 0.31 oz",
+ u"0 lb, 0 oz");
+
+ assertFormatDescending(
+ uTestCase.getTerminatedBuffer(),
+ u"usage/person unit-width-full-name measure-unit/mass-kilogram",
+ u"usage/person unit-width-full-name unit/kilogram",
+ unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
+ Locale("en-GB"),
+ u"13,802 stone, 7.2 pounds",
+ u"1,380 stone, 3.5 pounds",
+ u"138 stone, 0.35 pounds",
+ u"13 stone, 11 pounds",
+ u"1 stone, 5.3 pounds",
+ u"1 pound, 15 ounces",
+ u"0 pounds, 3.1 ounces",
+ u"0 pounds, 0.31 ounces",
+ u"0 pounds, 0 ounces");
+
+ 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() {
+ IcuTestErrorCode status(*this, "unitUsageErrorCodes()");
+ UnlocalizedNumberFormatter unloc_formatter;
+
+ unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status);
+ // This gives an error, because foobar is an invalid unit:
+ status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR);
+
+ unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status);
+ // This does not give an error, because usage is not looked up yet.
+ status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage");
+ unloc_formatter.locale("en-GB").formatInt(1, status);
+ // Lacking a unit results in a failure. The skeleton is "incomplete", but we
+ // support adding the unit via the fluent API, so it is not an error until
+ // we build the formatting pipeline itself.
+ status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
+ // Adding the unit as part of the fluent chain leads to success.
+ unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status);
+ 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, // 0.45 rounds down, 0.55 rounds up.
+ u"3,214E2 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::unitCurrency() {
"9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
}
+void NumberFormatterApiTest::microPropsInternals() {
+ // Verify copy construction and assignment operators.
+ int64_t testValues[2] = {4, 61};
+
+ MicroProps mp;
+ assertEquals("capacity", 2, mp.mixedMeasures.getCapacity());
+ mp.mixedMeasures[0] = testValues[0];
+ mp.mixedMeasures[1] = testValues[1];
+ MicroProps copyConstructed(mp);
+ MicroProps copyAssigned;
+ int64_t *resizeResult = mp.mixedMeasures.resize(4, 4);
+ assertTrue("Resize success", resizeResult != NULL);
+ copyAssigned = mp;
+
+ assertTrue("MicroProps success status", U_SUCCESS(mp.mixedMeasures.status));
+ assertTrue("Copy Constructed success status", U_SUCCESS(copyConstructed.mixedMeasures.status));
+ assertTrue("Copy Assigned success status", U_SUCCESS(copyAssigned.mixedMeasures.status));
+ assertEquals("Original values[0]", testValues[0], mp.mixedMeasures[0]);
+ assertEquals("Original values[1]", testValues[1], mp.mixedMeasures[1]);
+ assertEquals("Copy Constructed[0]", testValues[0], copyConstructed.mixedMeasures[0]);
+ assertEquals("Copy Constructed[1]", testValues[1], copyConstructed.mixedMeasures[1]);
+ assertEquals("Copy Assigned[0]", testValues[0], copyAssigned.mixedMeasures[0]);
+ assertEquals("Copy Assigned[1]", testValues[1], copyAssigned.mixedMeasures[1]);
+ assertEquals("Original capacity", 4, mp.mixedMeasures.getCapacity());
+ assertEquals("Copy Constructed capacity", 2, copyConstructed.mixedMeasures.getCapacity());
+ assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
+}
void NumberFormatterApiTest::assertFormatDescending(
const char16_t* umessage,
#if !UCONFIG_NO_FORMATTING
-#include "unitsdata.h"
+#include "units_data.h"
#include "intltest.h"
using namespace ::icu::units;
#include "intltest.h"
#include "unicode/unistr.h"
-#include "unitsrouter.h"
+#include "units_router.h"
class UnitsRouterTest : public IntlTest {
#include "unicode/unistr.h"
#include "unicode/unum.h"
#include "unicode/ures.h"
-#include "unitconverter.h"
-#include "unitsdata.h"
-#include "unitsrouter.h"
+#include "units_complexconverter.h"
+#include "units_converter.h"
+#include "units_data.h"
+#include "units_router.h"
#include "uparse.h"
#include "uresimp.h"
void testUnitConstantFreshness();
void testConversionCapability();
void testConversions();
+ void testComplexUnitsConverter();
void testComplexUnitConverterSorting();
void testPreferences();
void testSiPrefixes();
TESTCASE_AUTO(testUnitConstantFreshness);
TESTCASE_AUTO(testConversionCapability);
TESTCASE_AUTO(testConversions);
+ TESTCASE_AUTO(testComplexUnitsConverter);
TESTCASE_AUTO(testComplexUnitConverterSorting);
TESTCASE_AUTO(testPreferences);
TESTCASE_AUTO(testSiPrefixes);
addSingleFactorConstant(constant, 1, POSITIVE, factor, status);
if (status.errDataIfFailureAndReset(
"addSingleFactorConstant(<%s>, ...).\n\n"
- "If U_INVALID_FORMAT_ERROR, please check that \"icu4c/source/i18n/unitconverter.cpp\" "
+ "If U_INVALID_FORMAT_ERROR, please check that \"icu4c/source/i18n/units_converter.cpp\" "
"has all constants? Is \"%s\" a new constant?\n",
constant, constant)) {
continue;
}
DecimalQuantity dqVal;
UErrorCode parseStatus = U_ZERO_ERROR;
- // TODO(units): unify with strToDouble() in unitconverter.cpp
+ // TODO(units): unify with strToDouble() in units_converter.cpp
dqVal.setToDecNumber(val.toStringPiece(), parseStatus);
if (!U_SUCCESS(parseStatus)) {
// Not simple to parse, skip validating this constant's value. (We
}
/**
- * Trims whitespace (spaces only) off of the specified string.
+ * Trims whitespace off of the specified string.
* @param field is two pointers pointing at the start and end of the string.
* @return A StringPiece with initial and final space characters trimmed off.
*/
StringPiece trimField(char *(&field)[2]) {
- char *start = field[0];
- while (start < field[1] && (start[0]) == ' ') {
- start++;
+ const char *start = field[0];
+ start = u_skipWhitespace(start);
+ if (start >= field[1]) {
+ start = field[1];
}
- int32_t length = (int32_t)(field[1] - start);
- while (length > 0 && (start[length - 1]) == ' ') {
- length--;
+ const char *end = field[1];
+ while ((start < end) && U_IS_INV_WHITESPACE(*(end - 1))) {
+ end--;
}
+ int32_t length = (int32_t)(end - start);
return StringPiece(start, length);
}
return;
}
+ CharString sourceIdent(x, status);
MeasureUnitImpl sourceUnit = MeasureUnitImpl::forIdentifier(x, status);
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", x.length(), x.data())) {
return;
}
+ CharString targetIdent(y, status);
MeasureUnitImpl targetUnit = MeasureUnitImpl::forIdentifier(y, status);
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", y.length(), y.data())) {
return;
// Convertibility:
auto convertibility = extractConvertibility(sourceUnit, targetUnit, *ctx->conversionRates, status);
if (status.errIfFailureAndReset("extractConvertibility(<%s>, <%s>, ...)",
- sourceUnit.identifier.data(), targetUnit.identifier.data())) {
+ sourceIdent.data(), targetIdent.data())) {
return;
}
CharString msg;
msg.append("convertible: ", status)
- .append(sourceUnit.identifier.data(), status)
+ .append(sourceIdent.data(), status)
.append(" -> ", status)
- .append(targetUnit.identifier.data(), status);
+ .append(targetIdent.data(), status);
if (status.errIfFailureAndReset("msg construction")) {
return;
}
// Conversion:
UnitConverter converter(sourceUnit, targetUnit, *ctx->conversionRates, status);
if (status.errIfFailureAndReset("constructor: UnitConverter(<%s>, <%s>, status)",
- sourceUnit.identifier.data(), targetUnit.identifier.data())) {
+ sourceIdent.data(), targetIdent.data())) {
return;
}
double got = converter.convert(1000);
/**
* Runs data-driven unit tests for unit conversion. It looks for the test cases
- * in source/test/testdata/units/unitsTest.txt, which originates in CLDR.
+ * in source/test/testdata/cldr/units/unitsTest.txt, which originates in CLDR.
*/
void UnitsTest::testConversions() {
const char *filename = "unitsTest.txt";
}
CharString path(sourceTestDataPath, errorCode);
- path.appendPathPart("units", errorCode);
+ path.appendPathPart("cldr/units", errorCode);
path.appendPathPart(filename, errorCode);
ConversionRates rates(errorCode);
}
}
+void UnitsTest::testComplexUnitsConverter() {
+ IcuTestErrorCode status(*this, "UnitsTest::testComplexUnitConversions");
+ ConversionRates rates(status);
+ MeasureUnit input = MeasureUnit::getFoot();
+ MeasureUnit output = MeasureUnit::forIdentifier("foot-and-inch", status);
+ MeasureUnitImpl tempInput, tempOutput;
+ const MeasureUnitImpl &inputImpl = MeasureUnitImpl::forMeasureUnit(input, tempInput, status);
+ const MeasureUnitImpl &outputImpl = MeasureUnitImpl::forMeasureUnit(output, tempOutput, status);
+ auto converter = ComplexUnitsConverter(inputImpl, outputImpl, rates, status);
+
+ // Significantly less than 2.0.
+ MaybeStackVector<Measure> measures = converter.convert(1.9999, status);
+ assertEquals("measures length", 2, measures.length());
+ assertEquals("1.9999: measures[0] value", 1.0, measures[0]->getNumber().getDouble(status));
+ assertEquals("1.9999: measures[0] unit", MeasureUnit::getFoot().getIdentifier(),
+ measures[0]->getUnit().getIdentifier());
+ assertEqualsNear("1.9999: measures[1] value", 11.9988, measures[1]->getNumber().getDouble(status), 0.0001);
+ assertEquals("1.9999: measures[1] unit", MeasureUnit::getInch().getIdentifier(),
+ measures[1]->getUnit().getIdentifier());
+
+ // TODO: consider factoring out the set of tests to make this function more
+ // data-driven, *after* dealing appropriately with the memory leaks that can
+ // be demonstrated by this code.
+
+ // TODO: reusing measures results in a leak.
+ // A minimal nudge under 2.0.
+ MaybeStackVector<Measure> measures2 = converter.convert((2.0 - DBL_EPSILON), status);
+ assertEquals("measures length", 2, measures2.length());
+ assertEquals("1 - eps: measures[0] value", 2.0, measures2[0]->getNumber().getDouble(status));
+ assertEquals("1 - eps: measures[0] unit", MeasureUnit::getFoot().getIdentifier(),
+ measures2[0]->getUnit().getIdentifier());
+ assertEquals("1 - eps: measures[1] value", 0.0, measures2[1]->getNumber().getDouble(status));
+ assertEquals("1 - eps: measures[1] unit", MeasureUnit::getInch().getIdentifier(),
+ measures2[1]->getUnit().getIdentifier());
+
+ // Testing precision with meter and light-year. 1e-16 light years is
+ // 0.946073 meters, and double precision can provide only ~15 decimal
+ // digits, so we don't expect to get anything less than 1 meter.
+
+ // An epsilon's nudge under one light-year: should give 1 ly, 0 m.
+ input = MeasureUnit::getLightYear();
+ output = MeasureUnit::forIdentifier("light-year-and-meter", status);
+ // TODO: reusing tempInput and tempOutput results in a leak.
+ MeasureUnitImpl tempInput3, tempOutput3;
+ const MeasureUnitImpl &inputImpl3 = MeasureUnitImpl::forMeasureUnit(input, tempInput3, status);
+ const MeasureUnitImpl &outputImpl3 = MeasureUnitImpl::forMeasureUnit(output, tempOutput3, status);
+ // TODO: reusing converter results in a leak.
+ ComplexUnitsConverter converter3 = ComplexUnitsConverter(inputImpl3, outputImpl3, rates, status);
+ // TODO: reusing measures results in a leak.
+ MaybeStackVector<Measure> measures3 = converter3.convert((2.0 - DBL_EPSILON), status);
+ assertEquals("measures length", 2, measures3.length());
+ assertEquals("light-year test: measures[0] value", 2.0, measures3[0]->getNumber().getDouble(status));
+ assertEquals("light-year test: measures[0] unit", MeasureUnit::getLightYear().getIdentifier(),
+ measures3[0]->getUnit().getIdentifier());
+ assertEquals("light-year test: measures[1] value", 0.0, measures3[1]->getNumber().getDouble(status));
+ assertEquals("light-year test: measures[1] unit", MeasureUnit::getMeter().getIdentifier(),
+ measures3[1]->getUnit().getIdentifier());
+
+ // 1e-15 light years is 9.46073 meters (calculated using "bc" and the CLDR
+ // conversion factor). With double-precision maths, we get 10.5. In this
+ // case, we're off by almost 1 meter.
+ MaybeStackVector<Measure> measures4 = converter3.convert((1.0 + 1e-15), status);
+ assertEquals("measures length", 2, measures4.length());
+ assertEquals("light-year test: measures[0] value", 1.0, measures4[0]->getNumber().getDouble(status));
+ assertEquals("light-year test: measures[0] unit", MeasureUnit::getLightYear().getIdentifier(),
+ measures4[0]->getUnit().getIdentifier());
+ assertEqualsNear("light-year test: measures[1] value", 10,
+ measures4[1]->getNumber().getDouble(status), 1);
+ assertEquals("light-year test: measures[1] unit", MeasureUnit::getMeter().getIdentifier(),
+ measures4[1]->getUnit().getIdentifier());
+
+ // 2e-16 light years is 1.892146 meters. We consider this in the noise, and
+ // thus expect a 0. (This test fails when 2e-16 is increased to 4e-16.)
+ MaybeStackVector<Measure> measures5 = converter3.convert((1.0 + 2e-16), status);
+ assertEquals("measures length", 2, measures5.length());
+ assertEquals("light-year test: measures[0] value", 1.0, measures5[0]->getNumber().getDouble(status));
+ assertEquals("light-year test: measures[0] unit", MeasureUnit::getLightYear().getIdentifier(),
+ measures5[0]->getUnit().getIdentifier());
+ assertEquals("light-year test: measures[1] value", 0.0,
+ measures5[1]->getNumber().getDouble(status));
+ assertEquals("light-year test: measures[1] unit", MeasureUnit::getMeter().getIdentifier(),
+ measures5[1]->getUnit().getIdentifier());
+
+ // TODO(icu-units#63): test negative numbers!
+}
+
void UnitsTest::testComplexUnitConverterSorting() {
IcuTestErrorCode status(*this, "UnitsTest::testComplexUnitConverterSorting");
auto measures = complexConverter.convert(10.0, status);
U_ASSERT(measures.length() == 2);
- assertEquals("Sorted Data", "foot", measures[0]->getUnit().getIdentifier());
- assertEquals("Sorted Data", "inch", measures[1]->getUnit().getIdentifier());
+ assertEquals("inch-and-foot unit 0", "inch", measures[0]->getUnit().getIdentifier());
+ assertEquals("inch-and-foot unit 1", "foot", measures[1]->getUnit().getIdentifier());
- assertEqualsNear("Sorted Data", 32, measures[0]->getNumber().getInt64(), 0.00001);
- assertEqualsNear("Sorted Data", 9.7008, measures[1]->getNumber().getDouble(), 0.0001);
+ assertEqualsNear("inch-and-foot value 0", 9.7008, measures[0]->getNumber().getDouble(), 0.0001);
+ assertEqualsNear("inch-and-foot value 1", 32, measures[1]->getNumber().getInt64(), 0.00001);
}
/**
}
/**
- * Runs data-driven unit tests for unit preferences.
+ * Runs data-driven unit tests for unit preferences. It looks for the test cases
+ * in source/test/testdata/cldr/units/unitPreferencesTest.txt, which originates
+ * in CLDR.
*/
void UnitsTest::testPreferences() {
const char *filename = "unitPreferencesTest.txt";
}
CharString path(sourceTestDataPath, errorCode);
- path.appendPathPart("units", errorCode);
+ path.appendPathPart("cldr/units", errorCode);
path.appendPathPart(filename, errorCode);
parsePreferencesTests(path.data(), ';', fields, maxFields, unitPreferencesTestDataLineFn, this,
+++ /dev/null
-# This file is a copy of common/testData/units/unitPreferencesTest.txt from CLDR.
-# WIP/TODO(hugovdm): determine a good update procedure and document it.
-#
-# Test data for unit preferences
-# Copyright © 1991-2020 Unicode, Inc.
-# For terms of use, see http://www.unicode.org/copyright.html
-# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
-#
-# Format:
-# Quantity; Usage; Region; Input (r); Input (d); Input Unit; Output (r); Output (d); Output Unit
-#
-# Use: Convert the Input amount & unit according to the Usage and Region.
-# The result should match the Output amount and unit.
-# Both rational (r) and double64 (d) forms of the input and output amounts are supplied so that implementations
-# have two options for testing based on the precision in their implementations. For example:
-# 3429 / 12500; 0.27432; meter;
-# The Output amount and Unit are repeated for mixed units. In such a case, only the smallest unit will have
-# both a rational and decimal amount; the others will have a single integer value, such as:
-# length; person-height; CA; 3429 / 12500; 0.27432; meter; 2; foot; 54 / 5; 10.8; inch
-# The input and output units are unit identifers; in particular, the output does not have further processing:
-# • no localization
-# • no adjustment for pluralization
-# • no formatted with the skeleton
-# • no suppression of zero values (for secondary -and- units such as pound in stone-and-pound)
-#
-# Generation: Set GENERATE_TESTS in TestUnits.java, and look at TestUnitPreferences results.
-
-area; default; 001; 1100000; 1100000.0; square-meter; 11 / 10; 1.1; square-kilometer
-area; default; 001; 1000000; 1000000.0; square-meter; 1; 1.0; square-kilometer
-area; default; 001; 900000; 900000.0; square-meter; 90; 90.0; hectare
-area; default; 001; 10000; 10000.0; square-meter; 1; 1.0; hectare
-area; default; 001; 9000; 9000.0; square-meter; 9000; 9000.0; square-meter
-area; default; 001; 1; 1.0; square-meter; 1; 1.0; square-meter
-area; default; 001; 9 / 10; 0.9; square-meter; 9000; 9000.0; square-centimeter
-area; default; 001; 1 / 10000; 1.0E-4; square-meter; 1; 1.0; square-centimeter
-area; default; 001; 9 / 100000; 9.0E-5; square-meter; 9 / 10; 0.9; square-centimeter
-
-area; default; GB; 222577103232 / 78125; 2848986.9213696; square-meter; 11 / 10; 1.1; square-mile
-area; default; GB; 40468564224 / 15625; 2589988.110336; square-meter; 1; 1.0; square-mile
-area; default; GB; 182108539008 / 78125; 2330989.2993024; square-meter; 576; 576.0; acre
-area; default; GB; 316160658 / 78125; 4046.8564224; square-meter; 1; 1.0; acre
-area; default; GB; 1422722961 / 390625; 3642.17078016; square-meter; 39204; 39204.0; square-foot
-area; default; GB; 145161 / 1562500; 0.09290304; square-meter; 1; 1.0; square-foot
-area; default; GB; 1306449 / 15625000; 0.083612736; square-meter; 648 / 5; 129.6; square-inch
-area; default; GB; 16129 / 25000000; 6.4516E-4; square-meter; 1; 1.0; square-inch
-area; default; GB; 145161 / 250000000; 5.80644E-4; square-meter; 9 / 10; 0.9; square-inch
-
-area; geograph; 001; 1100000; 1100000.0; square-meter; 11 / 10; 1.1; square-kilometer
-area; geograph; 001; 1000000; 1000000.0; square-meter; 1; 1.0; square-kilometer
-area; geograph; 001; 900000; 900000.0; square-meter; 9 / 10; 0.9; square-kilometer
-
-area; geograph; GB; 222577103232 / 78125; 2848986.9213696; square-meter; 11 / 10; 1.1; square-mile
-area; geograph; GB; 40468564224 / 15625; 2589988.110336; square-meter; 1; 1.0; square-mile
-area; geograph; GB; 182108539008 / 78125; 2330989.2993024; square-meter; 9 / 10; 0.9; square-mile
-
-area; land; 001; 11000; 11000.0; square-meter; 11 / 10; 1.1; hectare
-area; land; 001; 10000; 10000.0; square-meter; 1; 1.0; hectare
-area; land; 001; 9000; 9000.0; square-meter; 9 / 10; 0.9; hectare
-
-area; land; GB; 1738883619 / 390625; 4451.54206464; square-meter; 11 / 10; 1.1; acre
-area; land; GB; 316160658 / 78125; 4046.8564224; square-meter; 1; 1.0; acre
-area; land; GB; 1422722961 / 390625; 3642.17078016; square-meter; 9 / 10; 0.9; acre
-
-concentration; blood-glucose; AG; 662435483600000000000000; 6.624354836E23; item-per-cubic-meter; 11 / 10; 1.1; millimole-per-liter
-concentration; blood-glucose; AG; 602214076000000000000000; 6.02214076E23; item-per-cubic-meter; 1; 1.0; millimole-per-liter
-concentration; blood-glucose; AG; 541992668400000000000000; 5.419926684E23; item-per-cubic-meter; 9 / 10; 0.9; millimole-per-liter
-
-concentration; default; 001; 11 / 10; 1.1; item-per-cubic-meter; 11 / 10; 1.1; item-per-cubic-meter
-concentration; default; 001; 1; 1.0; item-per-cubic-meter; 1; 1.0; item-per-cubic-meter
-concentration; default; 001; 9 / 10; 0.9; item-per-cubic-meter; 9 / 10; 0.9; item-per-cubic-meter
-
-consumption; default; 001; 11 / 1000000000; 1.1E-8; cubic-meter-per-meter; 11 / 10; 1.1; liter-per-100-kilometer
-consumption; default; 001; 1 / 100000000; 1.0E-8; cubic-meter-per-meter; 1; 1.0; liter-per-100-kilometer
-consumption; default; 001; 9 / 1000000000; 9.0E-9; cubic-meter-per-meter; 9 / 10; 0.9; liter-per-100-kilometer
-
-consumption; vehicle-fuel; 001; 11 / 1000000000; 1.1E-8; cubic-meter-per-meter; 11 / 10; 1.1; liter-per-100-kilometer
-consumption; vehicle-fuel; 001; 1 / 100000000; 1.0E-8; cubic-meter-per-meter; 1; 1.0; liter-per-100-kilometer
-consumption; vehicle-fuel; 001; 9 / 1000000000; 9.0E-9; cubic-meter-per-meter; 9 / 10; 0.9; liter-per-100-kilometer
-
-consumption; vehicle-fuel; BR; 11 / 10000000; 1.1E-6; cubic-meter-per-meter; 11 / 10; 1.1; liter-per-kilometer
-consumption; vehicle-fuel; BR; 1 / 1000000; 1.0E-6; cubic-meter-per-meter; 1; 1.0; liter-per-kilometer
-consumption; vehicle-fuel; BR; 9 / 10000000; 9.0E-7; cubic-meter-per-meter; 9 / 10; 0.9; liter-per-kilometer
-
-consumption-inverse; default; 001; 110000000; 1.1E8; meter-per-cubic-meter; 11 / 10; 1.1; kilometer-per-centiliter
-consumption-inverse; default; 001; 100000000; 1.0E8; meter-per-cubic-meter; 1; 1.0; kilometer-per-centiliter
-consumption-inverse; default; 001; 90000000; 9.0E7; meter-per-cubic-meter; 9 / 10; 0.9; kilometer-per-centiliter
-
-consumption-inverse; vehicle-fuel; 001; 110000000; 1.1E8; meter-per-cubic-meter; 11 / 10; 1.1; kilometer-per-centiliter
-consumption-inverse; vehicle-fuel; 001; 100000000; 1.0E8; meter-per-cubic-meter; 1; 1.0; kilometer-per-centiliter
-consumption-inverse; vehicle-fuel; 001; 90000000; 9.0E7; meter-per-cubic-meter; 9 / 10; 0.9; kilometer-per-centiliter
-
-consumption-inverse; vehicle-fuel; US; 52800000000 / 112903; 467658.0781732992; meter-per-cubic-meter; 11 / 10; 1.1; mile-per-gallon
-consumption-inverse; vehicle-fuel; US; 48000000000 / 112903; 425143.707430272; meter-per-cubic-meter; 1; 1.0; mile-per-gallon
-consumption-inverse; vehicle-fuel; US; 43200000000 / 112903; 382629.3366872448; meter-per-cubic-meter; 9 / 10; 0.9; mile-per-gallon
-
-consumption-inverse; vehicle-fuel; CA; 177027840000 / 454609; 389406.8089281118; meter-per-cubic-meter; 11 / 10; 1.1; mile-per-gallon-imperial
-consumption-inverse; vehicle-fuel; CA; 160934400000 / 454609; 354006.1899346471; meter-per-cubic-meter; 1; 1.0; mile-per-gallon-imperial
-consumption-inverse; vehicle-fuel; CA; 144840960000 / 454609; 318605.5709411824; meter-per-cubic-meter; 9 / 10; 0.9; mile-per-gallon-imperial
-
-duration; default; 001; 95040; 95040.0; second; 11 / 10; 1.1; day
-duration; default; 001; 86400; 86400.0; second; 1; 1.0; day
-duration; default; 001; 77760; 77760.0; second; 108 / 5; 21.6; hour
-duration; default; 001; 3600; 3600.0; second; 1; 1.0; hour
-duration; default; 001; 3240; 3240.0; second; 54; 54.0; minute
-duration; default; 001; 60; 60.0; second; 1; 1.0; minute
-duration; default; 001; 54; 54.0; second; 54; 54.0; second
-duration; default; 001; 1; 1.0; second; 1; 1.0; second
-duration; default; 001; 9 / 10; 0.9; second; 900; 900.0; millisecond
-duration; default; 001; 1 / 1000; 0.001; second; 1; 1.0; millisecond
-duration; default; 001; 9 / 10000; 9.0E-4; second; 900; 900.0; microsecond
-duration; default; 001; 1 / 1000000; 1.0E-6; second; 1; 1.0; microsecond
-duration; default; 001; 9 / 10000000; 9.0E-7; second; 900; 900.0; nanosecond
-duration; default; 001; 1 / 1000000000; 1.0E-9; second; 1; 1.0; nanosecond
-duration; default; 001; 9 / 10000000000; 9.0E-10; second; 9 / 10; 0.9; nanosecond
-
-duration; media; 001; 66; 66.0; second; 1; minute; 6; 6.0; second
-duration; media; 001; 60; 60.0; second; 1; minute; 0; 0.0; second
-duration; media; 001; 54; 54.0; second; 54; 54.0; second
-duration; media; 001; 1; 1.0; second; 1; 1.0; second
-duration; media; 001; 9 / 10; 0.9; second; 9 / 10; 0.9; second
-
-energy; default; 001; 3960000; 3960000.0; kilogram-square-meter-per-square-second; 11 / 10; 1.1; kilowatt-hour
-energy; default; 001; 3600000; 3600000.0; kilogram-square-meter-per-square-second; 1; 1.0; kilowatt-hour
-energy; default; 001; 3240000; 3240000.0; kilogram-square-meter-per-square-second; 9 / 10; 0.9; kilowatt-hour
-
-energy; food; US; 23012 / 5; 4602.4; kilogram-square-meter-per-square-second; 11 / 10; 1.1; foodcalorie
-energy; food; US; 4184; 4184.0; kilogram-square-meter-per-square-second; 1; 1.0; foodcalorie
-energy; food; US; 18828 / 5; 3765.6; kilogram-square-meter-per-square-second; 9 / 10; 0.9; foodcalorie
-
-energy; food; 001; 23012 / 5; 4602.4; kilogram-square-meter-per-square-second; 11 / 10; 1.1; kilocalorie
-energy; food; 001; 4184; 4184.0; kilogram-square-meter-per-square-second; 1; 1.0; kilocalorie
-energy; food; 001; 18828 / 5; 3765.6; kilogram-square-meter-per-square-second; 9 / 10; 0.9; kilocalorie
-
-length; default; 001; 1100; 1100.0; meter; 11 / 10; 1.1; kilometer
-length; default; 001; 1000; 1000.0; meter; 1; 1.0; kilometer
-length; default; 001; 900; 900.0; meter; 900; 900.0; meter
-length; default; 001; 1; 1.0; meter; 1; 1.0; meter
-length; default; 001; 9 / 10; 0.9; meter; 90; 90.0; centimeter
-length; default; 001; 1 / 100; 0.01; meter; 1; 1.0; centimeter
-length; default; 001; 9 / 1000; 0.009; meter; 9 / 10; 0.9; centimeter
-
-length; default; GB; 1106424 / 625; 1770.2784; meter; 11 / 10; 1.1; mile
-length; default; GB; 201168 / 125; 1609.344; meter; 1; 1.0; mile
-length; default; GB; 905256 / 625; 1448.4096; meter; 4752; 4752.0; foot
-length; default; GB; 381 / 1250; 0.3048; meter; 1; 1.0; foot
-length; default; GB; 3429 / 12500; 0.27432; meter; 54 / 5; 10.8; inch
-length; default; GB; 127 / 5000; 0.0254; meter; 1; 1.0; inch
-length; default; GB; 1143 / 50000; 0.02286; meter; 9 / 10; 0.9; inch
-
-length; person; 001; 11 / 1000; 0.011; meter; 11 / 10; 1.1; centimeter
-length; person; 001; 1 / 100; 0.01; meter; 1; 1.0; centimeter
-length; person; 001; 9 / 1000; 0.009; meter; 9 / 10; 0.9; centimeter
-
-length; person; CA; 1397 / 50000; 0.02794; meter; 11 / 10; 1.1; inch
-length; person; CA; 127 / 5000; 0.0254; meter; 1; 1.0; inch
-length; person; CA; 1143 / 50000; 0.02286; meter; 9 / 10; 0.9; inch
-
-length; person-height; 001; 11 / 1000; 0.011; meter; 11 / 10; 1.1; centimeter
-length; person-height; 001; 1 / 100; 0.01; meter; 1; 1.0; centimeter
-length; person-height; 001; 9 / 1000; 0.009; meter; 9 / 10; 0.9; centimeter
-
-length; person-height; CA; 11811 / 12500; 0.94488; meter; 3; foot; 6 / 5; 1.2; inch
-length; person-height; CA; 1143 / 1250; 0.9144; meter; 3; foot; 0; 0.0; inch
-length; person-height; CA; 11049 / 12500; 0.88392; meter; 174 / 5; 34.8; inch
-length; person-height; CA; 127 / 5000; 0.0254; meter; 1; 1.0; inch
-length; person-height; CA; 1143 / 50000; 0.02286; meter; 9 / 10; 0.9; inch
-
-length; person-height; AT; 11 / 10; 1.1; meter; 1; meter; 10; 10.0; centimeter
-length; person-height; AT; 1; 1.0; meter; 1; meter; 0; 0.0; centimeter
-length; person-height; AT; 9 / 10; 0.9; meter; 0; meter; 90; 90.0; centimeter
-
-length; rainfall; BR; 11 / 1000; 0.011; meter; 11 / 10; 1.1; centimeter
-length; rainfall; BR; 1 / 100; 0.01; meter; 1; 1.0; centimeter
-length; rainfall; BR; 9 / 1000; 0.009; meter; 9 / 10; 0.9; centimeter
-
-length; rainfall; US; 1397 / 50000; 0.02794; meter; 11 / 10; 1.1; inch
-length; rainfall; US; 127 / 5000; 0.0254; meter; 1; 1.0; inch
-length; rainfall; US; 1143 / 50000; 0.02286; meter; 9 / 10; 0.9; inch
-
-length; rainfall; 001; 11 / 10000; 0.0011; meter; 11 / 10; 1.1; millimeter
-length; rainfall; 001; 1 / 1000; 0.001; meter; 1; 1.0; millimeter
-length; rainfall; 001; 9 / 10000; 9.0E-4; meter; 9 / 10; 0.9; millimeter
-
-length; road; 001; 1000; 1000.0; meter; 1; 1.0; kilometer
-length; road; 001; 900; 900.0; meter; 9 / 10; 0.9; kilometer
-length; road; 001; 800; 800.0; meter; 800; 800.0; meter
-length; road; 001; 300; 300.0; meter; 300; 300.0; meter
-length; road; 001; 2999 / 10; 299.9; meter; 2999 / 10; 299.9; meter
-length; road; 001; 1; 1.0; meter; 1; 1.0; meter
-length; road; 001; 9 / 10; 0.9; meter; 9 / 10; 0.9; meter
-
-length; road; US; 603504 / 625; 965.6064; meter; 3 / 5; 0.6; mile
-length; road; US; 100584 / 125; 804.672; meter; 1 / 2; 0.5; mile
-length; road; US; 402336 / 625; 643.7376; meter; 2112; 2112.0; foot
-length; road; US; 762 / 25; 30.48; meter; 100; 100.0; foot
-length; road; US; 380619 / 12500; 30.44952; meter; 999 / 10; 99.9; foot
-length; road; US; 381 / 1250; 0.3048; meter; 1; 1.0; foot
-length; road; US; 3429 / 12500; 0.27432; meter; 9 / 10; 0.9; foot
-
-length; road; GB; 603504 / 625; 965.6064; meter; 3 / 5; 0.6; mile
-length; road; GB; 100584 / 125; 804.672; meter; 1 / 2; 0.5; mile
-length; road; GB; 402336 / 625; 643.7376; meter; 704; 704.0; yard
-length; road; GB; 2286 / 25; 91.44; meter; 100; 100.0; yard
-length; road; GB; 1141857 / 12500; 91.34856; meter; 999 / 10; 99.9; yard
-length; road; GB; 1143 / 1250; 0.9144; meter; 1; 1.0; yard
-length; road; GB; 10287 / 12500; 0.82296; meter; 9 / 10; 0.9; yard
-
-length; road; SE; 11000; 11000.0; meter; 11 / 10; 1.1; mile-scandinavian
-length; road; SE; 10000; 10000.0; meter; 1; 1.0; mile-scandinavian
-length; road; SE; 9000; 9000.0; meter; 9; 9.0; kilometer
-length; road; SE; 1000; 1000.0; meter; 1; 1.0; kilometer
-length; road; SE; 900; 900.0; meter; 900; 900.0; meter
-length; road; SE; 300; 300.0; meter; 300; 300.0; meter
-length; road; SE; 2999 / 10; 299.9; meter; 2999 / 10; 299.9; meter
-length; road; SE; 1; 1.0; meter; 1; 1.0; meter
-length; road; SE; 9 / 10; 0.9; meter; 9 / 10; 0.9; meter
-
-length; snowfall; 001; 11 / 1000; 0.011; meter; 11 / 10; 1.1; centimeter
-length; snowfall; 001; 1 / 100; 0.01; meter; 1; 1.0; centimeter
-length; snowfall; 001; 9 / 1000; 0.009; meter; 9 / 10; 0.9; centimeter
-
-length; snowfall; US; 1397 / 50000; 0.02794; meter; 11 / 10; 1.1; inch
-length; snowfall; US; 127 / 5000; 0.0254; meter; 1; 1.0; inch
-length; snowfall; US; 1143 / 50000; 0.02286; meter; 9 / 10; 0.9; inch
-
-length; vehicle; GB; 4191 / 12500; 0.33528; meter; 1; foot; 6 / 5; 1.2; inch
-length; vehicle; GB; 381 / 1250; 0.3048; meter; 1; foot; 0; 0.0; inch
-length; vehicle; GB; 3429 / 12500; 0.27432; meter; 0; foot; 54 / 5; 10.8; inch
-
-length; vehicle; 001; 11 / 10; 1.1; meter; 11 / 10; 1.1; meter
-length; vehicle; 001; 1; 1.0; meter; 1; 1.0; meter
-length; vehicle; 001; 9 / 10; 0.9; meter; 9 / 10; 0.9; meter
-
-length; vehicle; MX; 11 / 10; 1.1; meter; 1; meter; 10; 10.0; centimeter
-length; vehicle; MX; 1; 1.0; meter; 1; meter; 0; 0.0; centimeter
-length; vehicle; MX; 9 / 10; 0.9; meter; 0; meter; 90; 90.0; centimeter
-
-length; visiblty; 001; 200; 200.0; meter; 1 / 5; 0.2; kilometer
-length; visiblty; 001; 100; 100.0; meter; 1 / 10; 0.1; kilometer
-length; visiblty; 001; 1; 1.0; meter; 1; 1.0; meter
-length; visiblty; 001; 9 / 10; 0.9; meter; 9 / 10; 0.9; meter
-length; visiblty; 001; 0; 0.0; meter; 0; 0.0; meter
-
-length; visiblty; DE; 11 / 10; 1.1; meter; 11 / 10; 1.1; meter
-length; visiblty; DE; 1; 1.0; meter; 1; 1.0; meter
-length; visiblty; DE; 9 / 10; 0.9; meter; 9 / 10; 0.9; meter
-
-length; visiblty; GB; 1106424 / 625; 1770.2784; meter; 11 / 10; 1.1; mile
-length; visiblty; GB; 201168 / 125; 1609.344; meter; 1; 1.0; mile
-length; visiblty; GB; 905256 / 625; 1448.4096; meter; 4752; 4752.0; foot
-length; visiblty; GB; 381 / 1250; 0.3048; meter; 1; 1.0; foot
-length; visiblty; GB; 3429 / 12500; 0.27432; meter; 9 / 10; 0.9; foot
-
-mass; default; 001; 1100; 1100.0; kilogram; 11 / 10; 1.1; metric-ton
-mass; default; 001; 1000; 1000.0; kilogram; 1; 1.0; metric-ton
-mass; default; 001; 900; 900.0; kilogram; 900; 900.0; kilogram
-mass; default; 001; 1; 1.0; kilogram; 1; 1.0; kilogram
-mass; default; 001; 9 / 10; 0.9; kilogram; 900; 900.0; gram
-mass; default; 001; 1 / 1000; 0.001; kilogram; 1; 1.0; gram
-mass; default; 001; 9 / 10000; 9.0E-4; kilogram; 900; 900.0; milligram
-mass; default; 001; 1 / 1000000; 1.0E-6; kilogram; 1; 1.0; milligram
-mass; default; 001; 9 / 10000000; 9.0E-7; kilogram; 900; 900.0; microgram
-mass; default; 001; 1 / 1000000000; 1.0E-9; kilogram; 1; 1.0; microgram
-mass; default; 001; 9 / 10000000000; 9.0E-10; kilogram; 9 / 10; 0.9; microgram
-
-mass; default; GB; 498951607 / 500000; 997.903214; kilogram; 11 / 10; 1.1; ton
-mass; default; GB; 45359237 / 50000; 907.18474; kilogram; 1; 1.0; ton
-mass; default; GB; 408233133 / 500000; 816.466266; kilogram; 1800; 1800.0; pound
-mass; default; GB; 45359237 / 100000000; 0.45359237; kilogram; 1; 1.0; pound
-mass; default; GB; 408233133 / 1000000000; 0.408233133; kilogram; 72 / 5; 14.4; ounce
-mass; default; GB; 45359237 / 1600000000; 0.028349523125; kilogram; 1; 1.0; ounce
-mass; default; GB; 408233133 / 16000000000; 0.0255145708125; kilogram; 9 / 10; 0.9; ounce
-
-mass; person; 001; 11 / 10; 1.1; kilogram; 11 / 10; 1.1; kilogram
-mass; person; 001; 1; 1.0; kilogram; 1; 1.0; kilogram
-mass; person; 001; 9 / 10; 0.9; kilogram; 900; 900.0; gram
-mass; person; 001; 1 / 1000; 0.001; kilogram; 1; 1.0; gram
-mass; person; 001; 9 / 10000; 9.0E-4; kilogram; 9 / 10; 0.9; gram
-
-mass; person; DZ; 11 / 10; 1.1; kilogram; 1; kilogram; 100; 100.0; gram
-mass; person; DZ; 1; 1.0; kilogram; 1; kilogram; 0; 0.0; gram
-mass; person; DZ; 9 / 10; 0.9; kilogram; 0; kilogram; 900; 900.0; gram
-
-mass; person; US; 498951607 / 1000000000; 0.498951607; kilogram; 11 / 10; 1.1; pound
-mass; person; US; 45359237 / 100000000; 0.45359237; kilogram; 1; 1.0; pound
-mass; person; US; 408233133 / 1000000000; 0.408233133; kilogram; 0; pound; 72 / 5; 14.4; ounce
-
-mass; person; GB; 3492661249 / 500000000; 6.985322498; kilogram; 1; stone; 7 / 5; 1.4; pound
-mass; person; GB; 317514659 / 50000000; 6.35029318; kilogram; 1; stone; 0; 0.0; pound
-mass; person; GB; 2857631931 / 500000000; 5.715263862; kilogram; 12; pound; 48 / 5; 9.6; ounce
-mass; person; GB; 45359237 / 100000000; 0.45359237; kilogram; 1; pound; 0; 0.0; ounce
-mass; person; GB; 408233133 / 1000000000; 0.408233133; kilogram; 0; pound; 72 / 5; 14.4; ounce
-
-mass; person; HK; 498951607 / 1000000000; 0.498951607; kilogram; 1; pound; 8 / 5; 1.6; ounce
-mass; person; HK; 45359237 / 100000000; 0.45359237; kilogram; 1; pound; 0; 0.0; ounce
-mass; person; HK; 408233133 / 1000000000; 0.408233133; kilogram; 0; pound; 72 / 5; 14.4; ounce
-
-mass-density; blood-glucose; 001; 11 / 1000; 0.011; kilogram-per-cubic-meter; 11 / 10; 1.1; milligram-per-deciliter
-mass-density; blood-glucose; 001; 1 / 100; 0.01; kilogram-per-cubic-meter; 1; 1.0; milligram-per-deciliter
-mass-density; blood-glucose; 001; 9 / 1000; 0.009; kilogram-per-cubic-meter; 9 / 10; 0.9; milligram-per-deciliter
-
-mass-density; default; 001; 11 / 10; 1.1; kilogram-per-cubic-meter; 11 / 10; 1.1; kilogram-per-cubic-meter
-mass-density; default; 001; 1; 1.0; kilogram-per-cubic-meter; 1; 1.0; kilogram-per-cubic-meter
-mass-density; default; 001; 9 / 10; 0.9; kilogram-per-cubic-meter; 9 / 10; 0.9; kilogram-per-cubic-meter
-
-power; default; 001; 1100000000; 1.1E9; kilogram-square-meter-per-cubic-second; 11 / 10; 1.1; gigawatt
-power; default; 001; 1000000000; 1.0E9; kilogram-square-meter-per-cubic-second; 1; 1.0; gigawatt
-power; default; 001; 900000000; 9.0E8; kilogram-square-meter-per-cubic-second; 900; 900.0; megawatt
-power; default; 001; 1000000; 1000000.0; kilogram-square-meter-per-cubic-second; 1; 1.0; megawatt
-power; default; 001; 900000; 900000.0; kilogram-square-meter-per-cubic-second; 900; 900.0; kilowatt
-power; default; 001; 1000; 1000.0; kilogram-square-meter-per-cubic-second; 1; 1.0; kilowatt
-power; default; 001; 900; 900.0; kilogram-square-meter-per-cubic-second; 900; 900.0; watt
-power; default; 001; 1; 1.0; kilogram-square-meter-per-cubic-second; 1; 1.0; watt
-power; default; 001; 9 / 10; 0.9; kilogram-square-meter-per-cubic-second; 900; 900.0; milliwatt
-power; default; 001; 1 / 1000; 0.001; kilogram-square-meter-per-cubic-second; 1; 1.0; milliwatt
-power; default; 001; 9 / 10000; 9.0E-4; kilogram-square-meter-per-cubic-second; 9 / 10; 0.9; milliwatt
-
-power; engine; 001; 1100; 1100.0; kilogram-square-meter-per-cubic-second; 11 / 10; 1.1; kilowatt
-power; engine; 001; 1000; 1000.0; kilogram-square-meter-per-cubic-second; 1; 1.0; kilowatt
-power; engine; 001; 900; 900.0; kilogram-square-meter-per-cubic-second; 9 / 10; 0.9; kilowatt
-
-power; engine; GB; 410134929370248621 / 500000000000000; 820.2698587404972; kilogram-square-meter-per-cubic-second; 11 / 10; 1.1; horsepower
-power; engine; GB; 37284993579113511 / 50000000000000; 745.6998715822702; kilogram-square-meter-per-cubic-second; 1; 1.0; horsepower
-power; engine; GB; 335564942212021599 / 500000000000000; 671.1298844240432; kilogram-square-meter-per-cubic-second; 9 / 10; 0.9; horsepower
-
-pressure; baromtrc; 001; 110; 110.0; kilogram-per-meter-square-second; 11 / 10; 1.1; hectopascal
-pressure; baromtrc; 001; 100; 100.0; kilogram-per-meter-square-second; 1; 1.0; hectopascal
-pressure; baromtrc; 001; 90; 90.0; kilogram-per-meter-square-second; 9 / 10; 0.9; hectopascal
-
-pressure; baromtrc; IN; 37250275043751 / 10000000000; 3725.0275043751; kilogram-per-meter-square-second; 11 / 10; 1.1; inch-ofhg
-pressure; baromtrc; IN; 3386388640341 / 1000000000; 3386.388640341; kilogram-per-meter-square-second; 1; 1.0; inch-ofhg
-pressure; baromtrc; IN; 30477497763069 / 10000000000; 3047.7497763069; kilogram-per-meter-square-second; 9 / 10; 0.9; inch-ofhg
-
-pressure; baromtrc; BR; 110; 110.0; kilogram-per-meter-square-second; 11 / 10; 1.1; millibar
-pressure; baromtrc; BR; 100; 100.0; kilogram-per-meter-square-second; 1; 1.0; millibar
-pressure; baromtrc; BR; 90; 90.0; kilogram-per-meter-square-second; 9 / 10; 0.9; millibar
-
-pressure; baromtrc; MX; 293309252313 / 2000000000; 146.6546261565; kilogram-per-meter-square-second; 11 / 10; 1.1; millimeter-ofhg
-pressure; baromtrc; MX; 26664477483 / 200000000; 133.322387415; kilogram-per-meter-square-second; 1; 1.0; millimeter-ofhg
-pressure; baromtrc; MX; 239980297347 / 2000000000; 119.9901486735; kilogram-per-meter-square-second; 9 / 10; 0.9; millimeter-ofhg
-
-pressure; default; 001; 1100000; 1100000.0; kilogram-per-meter-square-second; 11 / 10; 1.1; megapascal
-pressure; default; 001; 1000000; 1000000.0; kilogram-per-meter-square-second; 1; 1.0; megapascal
-pressure; default; 001; 900000; 900000.0; kilogram-per-meter-square-second; 900000; 900000.0; pascal
-pressure; default; 001; 1; 1.0; kilogram-per-meter-square-second; 1; 1.0; pascal
-pressure; default; 001; 9 / 10; 0.9; kilogram-per-meter-square-second; 9 / 10; 0.9; pascal
-
-pressure; default; GB; 97860875535731 / 12903200000; 7584.233022485197; kilogram-per-meter-square-second; 11 / 10; 1.1; pound-force-per-square-inch
-pressure; default; GB; 8896443230521 / 1290320000; 6894.757293168361; kilogram-per-meter-square-second; 1; 1.0; pound-force-per-square-inch
-pressure; default; GB; 80067989074689 / 12903200000; 6205.281563851525; kilogram-per-meter-square-second; 9 / 10; 0.9; pound-force-per-square-inch
-
-speed; default; 001; 11 / 36; 0.3055555555555556; meter-per-second; 11 / 10; 1.1; kilometer-per-hour
-speed; default; 001; 5 / 18; 0.2777777777777778; meter-per-second; 1; 1.0; kilometer-per-hour
-speed; default; 001; 1 / 4; 0.25; meter-per-second; 9 / 10; 0.9; kilometer-per-hour
-
-speed; default; GB; 15367 / 31250; 0.491744; meter-per-second; 11 / 10; 1.1; mile-per-hour
-speed; default; GB; 1397 / 3125; 0.44704; meter-per-second; 1; 1.0; mile-per-hour
-speed; default; GB; 12573 / 31250; 0.402336; meter-per-second; 9 / 10; 0.9; mile-per-hour
-
-speed; wind; 001; 11 / 36; 0.3055555555555556; meter-per-second; 11 / 10; 1.1; kilometer-per-hour
-speed; wind; 001; 5 / 18; 0.2777777777777778; meter-per-second; 1; 1.0; kilometer-per-hour
-speed; wind; 001; 1 / 4; 0.25; meter-per-second; 9 / 10; 0.9; kilometer-per-hour
-
-speed; wind; FI; 11 / 10; 1.1; meter-per-second; 11 / 10; 1.1; meter-per-second
-speed; wind; FI; 1; 1.0; meter-per-second; 1; 1.0; meter-per-second
-speed; wind; FI; 9 / 10; 0.9; meter-per-second; 9 / 10; 0.9; meter-per-second
-
-speed; wind; US; 15367 / 31250; 0.491744; meter-per-second; 11 / 10; 1.1; mile-per-hour
-speed; wind; US; 1397 / 3125; 0.44704; meter-per-second; 1; 1.0; mile-per-hour
-speed; wind; US; 12573 / 31250; 0.402336; meter-per-second; 9 / 10; 0.9; mile-per-hour
-
-temperature; default; 001; 1097 / 4; 274.25; kelvin; 11 / 10; 1.1; celsius
-temperature; default; 001; 5483 / 20; 274.15; kelvin; 1; 1.0; celsius
-temperature; default; 001; 5481 / 20; 274.05; kelvin; 9 / 10; 0.9; celsius
-
-temperature; default; US; 15359 / 60; 255.9833333333333; kelvin; 11 / 10; 1.1; fahrenheit
-temperature; default; US; 46067 / 180; 255.9277777777778; kelvin; 1; 1.0; fahrenheit
-temperature; default; US; 46057 / 180; 255.8722222222222; kelvin; 9 / 10; 0.9; fahrenheit
-
-temperature; weather; 001; 1097 / 4; 274.25; kelvin; 11 / 10; 1.1; celsius
-temperature; weather; 001; 5483 / 20; 274.15; kelvin; 1; 1.0; celsius
-temperature; weather; 001; 5481 / 20; 274.05; kelvin; 9 / 10; 0.9; celsius
-
-temperature; weather; BS; 15359 / 60; 255.9833333333333; kelvin; 11 / 10; 1.1; fahrenheit
-temperature; weather; BS; 46067 / 180; 255.9277777777778; kelvin; 1; 1.0; fahrenheit
-temperature; weather; BS; 46057 / 180; 255.8722222222222; kelvin; 9 / 10; 0.9; fahrenheit
-
-volume; default; 001; 11 / 10; 1.1; cubic-meter; 11 / 10; 1.1; cubic-meter
-volume; default; 001; 1; 1.0; cubic-meter; 1; 1.0; cubic-meter
-volume; default; 001; 9 / 10; 0.9; cubic-meter; 900000; 900000.0; cubic-centimeter
-volume; default; 001; 1 / 1000000; 1.0E-6; cubic-meter; 1; 1.0; cubic-centimeter
-volume; default; 001; 9 / 10000000; 9.0E-7; cubic-meter; 9 / 10; 0.9; cubic-centimeter
-
-volume; default; GB; 608369751 / 19531250000; 0.0311485312512; cubic-meter; 11 / 10; 1.1; cubic-foot
-volume; default; GB; 55306341 / 1953125000; 0.028316846592; cubic-meter; 1; 1.0; cubic-foot
-volume; default; GB; 497757069 / 19531250000; 0.0254851619328; cubic-meter; 7776 / 5; 1555.2; cubic-inch
-volume; default; GB; 2048383 / 125000000000; 1.6387064E-5; cubic-meter; 1; 1.0; cubic-inch
-volume; default; GB; 18435447 / 1250000000000; 1.47483576E-5; cubic-meter; 9 / 10; 0.9; cubic-inch
-
-volume; fluid; 001; 11 / 10000; 0.0011; cubic-meter; 11 / 10; 1.1; liter
-volume; fluid; 001; 1 / 1000; 0.001; cubic-meter; 1; 1.0; liter
-volume; fluid; 001; 9 / 10000; 9.0E-4; cubic-meter; 900; 900.0; milliliter
-volume; fluid; 001; 1 / 1000000; 1.0E-6; cubic-meter; 1; 1.0; milliliter
-volume; fluid; 001; 9 / 10000000; 9.0E-7; cubic-meter; 9 / 10; 0.9; milliliter
-
-volume; fluid; US; 5204941203 / 1250000000000; 0.0041639529624; cubic-meter; 11 / 10; 1.1; gallon
-volume; fluid; US; 473176473 / 125000000000; 0.003785411784; cubic-meter; 1; 1.0; gallon
-volume; fluid; US; 4258588257 / 1250000000000; 0.0034068706056; cubic-meter; 18 / 5; 3.6; quart
-volume; fluid; US; 473176473 / 500000000000; 9.46352946E-4; cubic-meter; 1; 1.0; quart
-volume; fluid; US; 4258588257 / 5000000000000; 8.517176514E-4; cubic-meter; 9 / 5; 1.8; pint
-volume; fluid; US; 473176473 / 1000000000000; 4.73176473E-4; cubic-meter; 1; 1.0; pint
-volume; fluid; US; 4258588257 / 10000000000000; 4.258588257E-4; cubic-meter; 9 / 5; 1.8; cup
-volume; fluid; US; 473176473 / 2000000000000; 2.365882365E-4; cubic-meter; 1; 1.0; cup
-volume; fluid; US; 4258588257 / 20000000000000; 2.1292941285E-4; cubic-meter; 36 / 5; 7.2; fluid-ounce
-volume; fluid; US; 473176473 / 16000000000000; 2.95735295625E-5; cubic-meter; 1; 1.0; fluid-ounce
-volume; fluid; US; 4258588257 / 160000000000000; 2.661617660625E-5; cubic-meter; 9 / 5; 1.8; tablespoon
-volume; fluid; US; 473176473 / 32000000000000; 1.478676478125E-5; cubic-meter; 1; 1.0; tablespoon
-volume; fluid; US; 4258588257 / 320000000000000; 1.3308088303125E-5; cubic-meter; 27 / 10; 2.7; teaspoon
-volume; fluid; US; 157725491 / 32000000000000; 4.92892159375E-6; cubic-meter; 1; 1.0; teaspoon
-volume; fluid; US; 1419529419 / 320000000000000; 4.436029434375E-6; cubic-meter; 9 / 10; 0.9; teaspoon
-
-volume; fluid; GB; 5000699 / 1000000000; 0.005000699; cubic-meter; 11 / 10; 1.1; gallon-imperial
-volume; fluid; GB; 454609 / 100000000; 0.00454609; cubic-meter; 1; 1.0; gallon-imperial
-volume; fluid; GB; 4091481 / 1000000000; 0.004091481; cubic-meter; 144; 144.0; fluid-ounce-imperial
-volume; fluid; GB; 454609 / 16000000000; 2.84130625E-5; cubic-meter; 1; 1.0; fluid-ounce-imperial
-volume; fluid; GB; 4091481 / 160000000000; 2.557175625E-5; cubic-meter; 9 / 10; 0.9; fluid-ounce-imperial
-
-volume; oil; 001; 109303765263 / 625000000000; 0.1748860244208; cubic-meter; 11 / 10; 1.1; barrel
-volume; oil; 001; 9936705933 / 62500000000; 0.158987294928; cubic-meter; 1; 1.0; barrel
-volume; oil; 001; 89430353397 / 625000000000; 0.1430885654352; cubic-meter; 9 / 10; 0.9; barrel
-
-volume; vehicle; US; 5204941203 / 1250000000000; 0.0041639529624; cubic-meter; 11 / 10; 1.1; gallon
-volume; vehicle; US; 473176473 / 125000000000; 0.003785411784; cubic-meter; 1; 1.0; gallon
-volume; vehicle; US; 4258588257 / 1250000000000; 0.0034068706056; cubic-meter; 9 / 10; 0.9; gallon
-
-volume; vehicle; 001; 11 / 10000; 0.0011; cubic-meter; 11 / 10; 1.1; liter
-volume; vehicle; 001; 1 / 1000; 0.001; cubic-meter; 1; 1.0; liter
-volume; vehicle; 001; 9 / 10000; 9.0E-4; cubic-meter; 9 / 10; 0.9; liter
-
-year-duration; default; 001; 11 / 10; 1.1; year; 11 / 10; 1.1; year
-year-duration; default; 001; 1; 1.0; year; 1; 1.0; year
-year-duration; default; 001; 9 / 10; 0.9; year; 54 / 5; 10.8; month
-year-duration; default; 001; 1 / 12; 0.08333333333333333; year; 1; 1.0; month
-year-duration; default; 001; 3 / 40; 0.075; year; 9 / 10; 0.9; month
-
-year-duration; person-age; 001; 13 / 5; 2.6; year; 13 / 5; 2.6; year-person
-year-duration; person-age; 001; 5 / 2; 2.5; year; 5 / 2; 2.5; year-person
-year-duration; person-age; 001; 12 / 5; 2.4; year; 2; year-person; 24 / 5; 4.8; month-person
-year-duration; person-age; 001; 1; 1.0; year; 1; year-person; 0; 0.0; month-person
-year-duration; person-age; 001; 9 / 10; 0.9; year; 54 / 5; 10.8; month-person
-year-duration; person-age; 001; 1 / 12; 0.08333333333333333; year; 1; 1.0; month-person
-year-duration; person-age; 001; 3 / 40; 0.075; year; 9 / 10; 0.9; month-person
+++ /dev/null
-# This file is a copy of common/testData/units/unitsTest.txt from CLDR.
-# WIP/TODO(hugovdm): determine a good update procedure and document it.
-#
-# Test data for unit conversions
-# Copyright © 1991-2020 Unicode, Inc.
-# For terms of use, see http://www.unicode.org/copyright.html
-# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
-#
-# Format:
-# Quantity ; x ; y ; conversion to y (rational) ; test: 1000 x ⟹ y
-#
-# Use: convert 1000 x units to the y unit; the result should match the final column,
-# at the given precision. For example, when the last column is 159.1549,
-# round to 4 decimal digits before comparing.
-# Note that certain conversions are approximate, such as degrees to radians
-#
-# Generation: Set GENERATE_TESTS in TestUnits.java, and look at TestParseUnit results.
-
-acceleration ; meter-per-square-second ; meter-per-square-second ; 1 * x ; 1,000.00
-acceleration ; g-force ; meter-per-square-second ; 9.80665 * x ; 9806.65
-angle ; arc-second ; revolution ; 0.0000625/81 * x ; 7.716049E-4
-angle ; arc-minute ; revolution ; 0.00125/27 * x ; 0.0462963
-angle ; degree ; revolution ; 0.025/9 * x ; 2.777778
-angle ; radian ; revolution ; 65,501,488/411,557,987 * x ; 159.1549
-angle ; revolution ; revolution ; 1 * x ; 1,000.00
-area ; square-centimeter ; square-meter ; 0.0001 * x ; 0.1
-area ; square-inch ; square-meter ; 0.00064516 * x ; 0.64516
-area ; square-foot ; square-meter ; 0.09290304 * x ; 92.90304
-area ; square-yard ; square-meter ; 0.83612736 * x ; 836.1274
-area ; square-meter ; square-meter ; 1 * x ; 1,000.00
-area ; dunam ; square-meter ; 1,000 * x ; 1000000.0
-area ; acre ; square-meter ; 4,046.8564224 * x ; 4046856.0
-area ; hectare ; square-meter ; 10,000 * x ; 1.0E7
-area ; square-kilometer ; square-meter ; 1,000,000 * x ; 1.0E9
-area ; square-mile ; square-meter ; 2,589,988.110336 * x ; 2.589988E9
-concentration ; millimole-per-liter ; item-per-cubic-meter ; 602,214,076,000,000,000,000,000 * x ; 6.022141E26
-consumption ; liter-per-100-kilometer ; cubic-meter-per-meter ; 0.00000001 * x ; 1.0E-5
-consumption ; liter-per-kilometer ; cubic-meter-per-meter ; 0.000001 * x ; 0.001
-consumption-inverse ; mile-per-gallon-imperial ; meter-per-cubic-meter ; 160,934,400,000/454,609 * x ; 3.540062E8
-consumption-inverse ; mile-per-gallon ; meter-per-cubic-meter ; 48,000,000,000/112,903 * x ; 4.251437E8
-digital ; bit ; bit ; 1 * x ; 1,000.00
-digital ; byte ; bit ; 8 * x ; 8000.0
-digital ; kilobit ; bit ; 1,000 * x ; 1000000.0
-digital ; kilobyte ; bit ; 8,000 * x ; 8000000.0
-digital ; megabit ; bit ; 1,000,000 * x ; 1.0E9
-digital ; megabyte ; bit ; 8,000,000 * x ; 8.0E9
-digital ; gigabit ; bit ; 1,000,000,000 * x ; 1.0E12
-digital ; gigabyte ; bit ; 8,000,000,000 * x ; 8.0E12
-digital ; terabit ; bit ; 1,000,000,000,000 * x ; 1.0E15
-digital ; terabyte ; bit ; 8,000,000,000,000 * x ; 8.0E15
-digital ; petabyte ; bit ; 8,000,000,000,000,000 * x ; 8.0E18
-duration ; nanosecond ; second ; 0.000000001 * x ; 1.0E-6
-duration ; microsecond ; second ; 0.000001 * x ; 0.001
-duration ; millisecond ; second ; 0.001 * x ; 1.0
-duration ; second ; second ; 1 * x ; 1,000.00
-duration ; minute ; second ; 60 * x ; 60000.0
-duration ; hour ; second ; 3,600 * x ; 3600000.0
-duration ; day ; second ; 86,400 * x ; 8.64E7
-duration ; day-person ; second ; 86,400 * x ; 8.64E7
-duration ; week ; second ; 604,800 * x ; 6.048E8
-duration ; week-person ; second ; 604,800 * x ; 6.048E8
-electric-current ; milliampere ; ampere ; 0.001 * x ; 1.0
-electric-current ; ampere ; ampere ; 1 * x ; 1,000.00
-electric-resistance ; ohm ; kilogram-square-meter-per-cubic-second-square-ampere ; 1 * x ; 1000.0
-energy ; electronvolt ; kilogram-square-meter-per-square-second ; 0.0000000000000000001602177 * x ; 1.602177E-16
-energy ; dalton ; kilogram-square-meter-per-square-second ; 0.00000000014924180856 * x ; 1.492418E-7
-energy ; joule ; kilogram-square-meter-per-square-second ; 1 * x ; 1000.0
-energy ; newton-meter ; kilogram-square-meter-per-square-second ; 1 * x ; 1000.0
-energy ; pound-force-foot ; kilogram-square-meter-per-square-second ; 1.3558179483314004 * x ; 1355.818
-energy ; calorie ; kilogram-square-meter-per-square-second ; 4.184 * x ; 4184.0
-energy ; kilojoule ; kilogram-square-meter-per-square-second ; 1,000 * x ; 1000000.0
-energy ; british-thermal-unit ; kilogram-square-meter-per-square-second ; 9,489.1523804/9 * x ; 1054350.0
-energy ; foodcalorie ; kilogram-square-meter-per-square-second ; 4,184 * x ; 4184000.0
-energy ; kilocalorie ; kilogram-square-meter-per-square-second ; 4,184 * x ; 4184000.0
-energy ; kilowatt-hour ; kilogram-square-meter-second-per-cubic-second ; 3,600,000 * x ; 3.6E9
-energy ; therm-us ; kilogram-square-meter-per-square-second ; 105,480,400 * x ; 1.054804E11
-force ; newton ; kilogram-meter-per-square-second ; 1 * x ; 1000.0
-force ; pound-force ; kilogram-meter-per-square-second ; 4.4482216152605 * x ; 4448.222
-frequency ; hertz ; revolution-per-second ; 1 * x ; 1000.0
-frequency ; kilohertz ; revolution-per-second ; 1,000 * x ; 1000000.0
-frequency ; megahertz ; revolution-per-second ; 1,000,000 * x ; 1.0E9
-frequency ; gigahertz ; revolution-per-second ; 1,000,000,000 * x ; 1.0E12
-graphics ; dot ; pixel ; 1 * x ; 1000.0
-graphics ; pixel ; pixel ; 1 * x ; 1,000.00
-graphics ; megapixel ; pixel ; 1,000,000 * x ; 1.0E9
-length ; picometer ; meter ; 0.000000000001 * x ; 1.0E-9
-length ; nanometer ; meter ; 0.000000001 * x ; 1.0E-6
-length ; micrometer ; meter ; 0.000001 * x ; 0.001
-length ; point ; meter ; 0.003175/9 * x ; 0.3527778
-length ; millimeter ; meter ; 0.001 * x ; 1.0
-length ; centimeter ; meter ; 0.01 * x ; 10.0
-length ; inch ; meter ; 0.0254 * x ; 25.4
-length ; decimeter ; meter ; 0.1 * x ; 100.0
-length ; foot ; meter ; 0.3048 * x ; 304.8
-length ; yard ; meter ; 0.9144 * x ; 914.4
-length ; meter ; meter ; 1 * x ; 1,000.00
-length ; fathom ; meter ; 1.8288 * x ; 1828.8
-length ; furlong ; meter ; 201.168 * x ; 201168.0
-length ; kilometer ; meter ; 1,000 * x ; 1000000.0
-length ; mile ; meter ; 1,609.344 * x ; 1609344.0
-length ; nautical-mile ; meter ; 1,852 * x ; 1852000.0
-length ; mile-scandinavian ; meter ; 10,000 * x ; 1.0E7
-length ; 100-kilometer ; meter ; 100,000 * x ; 1.0E8
-length ; earth-radius ; meter ; 6,378,100 * x ; 6.3781E9
-length ; solar-radius ; meter ; 695,700,000 * x ; 6.957E11
-length ; astronomical-unit ; meter ; 149,597,900,000 * x ; 1.495979E14
-length ; light-year ; meter ; 9,460,730,000,000,000 * x ; 9.46073E18
-length ; parsec ; meter ; 30,856,780,000,000,000 * x ; 3.085678E19
-luminous-flux ; lux ; candela-square-meter-per-square-meter ; 1 * x ; 1000.0
-luminous-intensity ; candela ; candela ; 1 * x ; 1,000.00
-mass ; microgram ; kilogram ; 0.000000001 * x ; 1.0E-6
-mass ; milligram ; kilogram ; 0.000001 * x ; 0.001
-mass ; carat ; kilogram ; 0.0002 * x ; 0.2
-mass ; gram ; kilogram ; 0.001 * x ; 1.0
-mass ; ounce ; kilogram ; 0.028349523125 * x ; 28.34952
-mass ; ounce-troy ; kilogram ; 0.03110348 * x ; 31.10348
-mass ; pound ; kilogram ; 0.45359237 * x ; 453.5924
-mass ; kilogram ; kilogram ; 1 * x ; 1,000.00
-mass ; stone ; kilogram ; 6.35029318 * x ; 6350.293
-mass ; ton ; kilogram ; 907.18474 * x ; 907184.7
-mass ; metric-ton ; kilogram ; 1,000 * x ; 1000000.0
-mass ; earth-mass ; kilogram ; 5,972,200,000,000,000,000,000,000 * x ; 5.9722E27
-mass ; solar-mass ; kilogram ; 1,988,470,000,000,000,000,000,000,000,000 * x ; 1.98847E33
-mass-density ; milligram-per-deciliter ; kilogram-per-cubic-meter ; 0.01 * x ; 10.0
-portion ; permillion ; portion ; 0.000001 * x ; 0.001
-portion ; permyriad ; portion ; 0.0001 * x ; 0.1
-portion ; permille ; portion ; 0.001 * x ; 1.0
-portion ; percent ; portion ; 0.01 * x ; 10.0
-portion ; karat ; portion ; 0.125/3 * x ; 41.66667
-portion ; portion ; portion ; 1 * x ; 1,000.00
-power ; milliwatt ; kilogram-square-meter-per-cubic-second ; 0.001 * x ; 1.0
-power ; watt ; kilogram-square-meter-per-cubic-second ; 1 * x ; 1000.0
-power ; horsepower ; kilogram-square-meter-per-cubic-second ; 745.69987158227022 * x ; 745699.9
-power ; kilowatt ; kilogram-square-meter-per-cubic-second ; 1,000 * x ; 1000000.0
-power ; megawatt ; kilogram-square-meter-per-cubic-second ; 1,000,000 * x ; 1.0E9
-power ; gigawatt ; kilogram-square-meter-per-cubic-second ; 1,000,000,000 * x ; 1.0E12
-power ; solar-luminosity ; kilogram-square-meter-per-cubic-second ; 382,800,000,000,000,000,000,000,000 * x ; 3.828E29
-pressure ; pascal ; kilogram-per-meter-square-second ; 1 * x ; 1000.0
-pressure ; hectopascal ; kilogram-per-meter-square-second ; 100 * x ; 100000.0
-pressure ; millibar ; kilogram-per-meter-square-second ; 100 * x ; 100000.0
-pressure ; millimeter-ofhg ; kilogram-meter-per-square-meter-square-second ; 133.322387415 * x ; 133322.4
-pressure ; kilopascal ; kilogram-per-meter-square-second ; 1,000 * x ; 1000000.0
-pressure ; inch-ofhg ; kilogram-meter-per-square-meter-square-second ; 3,386.388640341 * x ; 3386389.0
-pressure ; pound-force-per-square-inch ; kilogram-meter-per-square-meter-square-second ; 111,205,540.3815125/16,129 * x ; 6894757.0
-pressure ; bar ; kilogram-per-meter-square-second ; 100,000 * x ; 1.0E8
-pressure ; atmosphere ; kilogram-per-meter-square-second ; 101,325 * x ; 1.01325E8
-pressure ; megapascal ; kilogram-per-meter-square-second ; 1,000,000 * x ; 1.0E9
-pressure-per-length ; ofhg ; kilogram-per-square-meter-square-second ; 133,322.387415 * x ; 1.333224E8
-resolution ; dot-per-inch ; pixel-per-meter ; 5,000/127 * x ; 39370.08
-resolution ; pixel-per-inch ; pixel-per-meter ; 5,000/127 * x ; 39370.08
-resolution ; dot-per-centimeter ; pixel-per-meter ; 100 * x ; 100000.0
-resolution ; pixel-per-centimeter ; pixel-per-meter ; 100 * x ; 100000.0
-speed ; kilometer-per-hour ; meter-per-second ; 2.5/9 * x ; 277.7778
-speed ; mile-per-hour ; meter-per-second ; 0.44704 * x ; 447.04
-speed ; knot ; meter-per-second ; 4.63/9 * x ; 514.4444
-speed ; meter-per-second ; meter-per-second ; 1 * x ; 1,000.00
-substance-amount ; item ; item ; 1 * x ; 1,000.00
-substance-amount ; mole ; item ; 602,214,076,000,000,000,000,000 * x ; 6.022141E26
-temperature ; fahrenheit ; kelvin ; 5/9 * x - 2,298.35/9 ; 810.9278
-temperature ; kelvin ; kelvin ; 1 * x ; 1,000.00
-temperature ; celsius ; kelvin ; 1 * x - 273.15 ; 1273.15
-typewidth ; em ; em ; 1 * x ; 1,000.00
-voltage ; volt ; kilogram-square-meter-per-cubic-second-ampere ; 1 * x ; 1000.0
-volume ; cubic-centimeter ; cubic-meter ; 0.000001 * x ; 0.001
-volume ; milliliter ; cubic-meter ; 0.000001 * x ; 0.001
-volume ; teaspoon ; cubic-meter ; 0.00000492892159375 * x ; 0.004928922
-volume ; centiliter ; cubic-meter ; 0.00001 * x ; 0.01
-volume ; tablespoon ; cubic-meter ; 0.00001478676478125 * x ; 0.01478676
-volume ; cubic-inch ; cubic-meter ; 0.000016387064 * x ; 0.01638706
-volume ; fluid-ounce-imperial ; cubic-meter ; 0.0000284130625 * x ; 0.02841306
-volume ; fluid-ounce ; cubic-meter ; 0.0000295735295625 * x ; 0.02957353
-volume ; deciliter ; cubic-meter ; 0.0001 * x ; 0.1
-volume ; cup ; cubic-meter ; 0.0002365882365 * x ; 0.2365882
-volume ; cup-metric ; cubic-meter ; 0.00025 * x ; 0.25
-volume ; pint ; cubic-meter ; 0.000473176473 * x ; 0.4731765
-volume ; pint-metric ; cubic-meter ; 0.0005 * x ; 0.5
-volume ; quart ; cubic-meter ; 0.000946352946 * x ; 0.9463529
-volume ; liter ; cubic-meter ; 0.001 * x ; 1.0
-volume ; gallon ; cubic-meter ; 0.003785411784 * x ; 3.785412
-volume ; gallon-imperial ; cubic-meter ; 0.00454609 * x ; 4.54609
-volume ; cubic-foot ; cubic-meter ; 0.028316846592 * x ; 28.31685
-volume ; bushel ; cubic-meter ; 0.03523907016688 * x ; 35.23907
-volume ; hectoliter ; cubic-meter ; 0.1 * x ; 100.0
-volume ; barrel ; cubic-meter ; 0.158987294928 * x ; 158.9873
-volume ; cubic-yard ; cubic-meter ; 0.764554857984 * x ; 764.5549
-volume ; cubic-meter ; cubic-meter ; 1 * x ; 1,000.00
-volume ; megaliter ; cubic-meter ; 1,000 * x ; 1000000.0
-volume ; acre-foot ; cubic-meter ; 1,233.48183754752 * x ; 1233482.0
-volume ; cubic-kilometer ; cubic-meter ; 1,000,000,000 * x ; 1.0E12
-volume ; cubic-mile ; cubic-meter ; 4,168,181,825.440579584 * x ; 4.168182E12
-year-duration ; month ; year ; 0.25/3 * x ; 83.33333
-year-duration ; month-person ; year ; 0.25/3 * x ; 83.33333
-year-duration ; year ; year ; 1 * x ; 1,000.00
-year-duration ; year-person ; year ; 1 * x ; 1000.0
-year-duration ; decade ; year ; 10 * x ; 10000.0
-year-duration ; century ; year ; 100 * x ; 100000.0