ICU-20568 Support smart units / unit contexts / preferences

Explore Usage-related error codes, address icu-units/icu#36.
PR: https://github.com/icu-units/icu/pull/56
Commit: d5d7fdccfef887bb1af180bba3e2a0286dc32135

Implement Precision handling in UsagePrefsHandler::processQuantity
PR: https://github.com/icu-units/icu/pull/61
Commit: 16547f32986600a46e4adf20a6870c1708dd1c75

Support Mixed Units in NumberFormatter when using usage()
PR: https://github.com/icu-units/icu/pull/52
Commit: cc5a12202133855e15ffba889acffc10aad2d46b

For MixedUnits, use the correct ListFormatter styles.
PR: https://github.com/icu-units/icu/pull/66
Commit: 77bb747002d36626386f18e45c68c44b276cf575

Read the CLDR testData test files from the new location.
PR: https://github.com/icu-units/icu/pull/68
Commit: 6eb992e2b3c0bbe4870b554a5aa855b3636566d4

Fix double-precision maths in unit conversions
PR: https://github.com/icu-units/icu/pull/71
Commit: 78e88fbddef0f6817654d58c9c5dfeb6606324b9

Support .unit(MIXED_UNIT) without .usage(...).
PR: https://github.com/icu-units/icu/pull/72
Commit: 56ac7959375b8c9363ff022185165e52490c6c00

More commits:
- Reorder numbertest_api.cpp tests for consistent order.
- NumberFormatterApiTest: fold unitPipeline() into unitCompoundMeasure()
- Add some 'template class' instantiations for MSVC.
- Make trimField handle all whitespace, improve test messages
- Drop templated 'appendAll': it requires copy constructor
- Add protected MaybeStackArray::copyFrom()
- Add TODO(icu-units#67) and commented-out test case: use kUndefinedField for now
- Provide correct output order for units like "inch-and-foot"
- MSVC: export MaybeStackVector<MeasureUnit>
- Code review feedback: dependencies.txt and doc comments
- Consistent naming for code files: units_*
This commit is contained in:
Hugo van der Merwe 2020-08-11 17:54:10 +02:00
parent 1b853904cd
commit e3123c83a4
43 changed files with 1837 additions and 1103 deletions

View File

@ -387,6 +387,20 @@ public:
* 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;

View File

@ -55,7 +55,6 @@ class U_I18N_API FormattedStringBuilder : public UMemory {
// 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;

View File

@ -202,6 +202,7 @@
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_symbolswrapper.cpp" />
<ClCompile Include="number_capi.cpp" />
<ClCompile Include="string_segment.cpp" />
<ClCompile Include="numparse_parsednumber.cpp" />
@ -251,10 +252,10 @@
<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" />
@ -470,13 +471,14 @@
<ClInclude Include="number_scientific.h" />
<ClInclude Include="formatted_string_builder.h" />
<ClInclude Include="number_types.h" />
<ClCompile Include="number_usageprefs.h" />
<ClInclude Include="number_usageprefs.h" />
<ClInclude Include="number_utypes.h" />
<ClInclude Include="number_utils.h" />
<ClInclude Include="number_mapper.h" />
<ClInclude Include="number_multiplier.h" />
<ClInclude Include="number_currencysymbols.h" />
<ClInclude Include="number_skeletons.h" />
<ClInclude Include="number_symbolswrapper.h" />
<ClInclude Include="string_segment.h" />
<ClInclude Include="numparse_impl.h" />
<ClInclude Include="numparse_symbols.h" />
@ -490,10 +492,10 @@
<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" />

View File

@ -588,6 +588,9 @@
<ClCompile Include="formatted_string_builder.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_usageprefs.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_utils.cpp">
<Filter>formatting</Filter>
</ClCompile>
@ -603,6 +606,9 @@
<ClCompile Include="number_skeletons.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_symbolswrapper.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_capi.cpp">
<Filter>formatting</Filter>
</ClCompile>
@ -651,16 +657,16 @@
<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>
@ -914,6 +920,9 @@
<ClInclude Include="number_utypes.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_usageprefs.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_utils.h">
<Filter>formatting</Filter>
</ClInclude>
@ -929,6 +938,9 @@
<ClInclude Include="number_skeletons.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_symbolswrapper.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="string_segment.h">
<Filter>formatting</Filter>
</ClInclude>
@ -1007,16 +1019,16 @@
<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">

View File

@ -483,10 +483,10 @@
<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" />
@ -723,10 +723,10 @@
<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" />

View File

@ -520,123 +520,6 @@ LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale& locale
return LocalizedNumberFormatter(std::move(fMacros), locale);
}
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper& other) {
doCopyFrom(other);
}
SymbolsWrapper::SymbolsWrapper(SymbolsWrapper&& src) U_NOEXCEPT {
doMoveFrom(std::move(src));
}
SymbolsWrapper& SymbolsWrapper::operator=(const SymbolsWrapper& other) {
if (this == &other) {
return *this;
}
doCleanup();
doCopyFrom(other);
return *this;
}
SymbolsWrapper& SymbolsWrapper::operator=(SymbolsWrapper&& src) U_NOEXCEPT {
if (this == &src) {
return *this;
}
doCleanup();
doMoveFrom(std::move(src));
return *this;
}
SymbolsWrapper::~SymbolsWrapper() {
doCleanup();
}
void SymbolsWrapper::setTo(const DecimalFormatSymbols& dfs) {
doCleanup();
fType = SYMPTR_DFS;
fPtr.dfs = new DecimalFormatSymbols(dfs);
}
void SymbolsWrapper::setTo(const NumberingSystem* ns) {
doCleanup();
fType = SYMPTR_NS;
fPtr.ns = ns;
}
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper& other) {
fType = other.fType;
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
// Memory allocation failures are exposed in copyErrorTo()
if (other.fPtr.dfs != nullptr) {
fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
} else {
fPtr.dfs = nullptr;
}
break;
case SYMPTR_NS:
// Memory allocation failures are exposed in copyErrorTo()
if (other.fPtr.ns != nullptr) {
fPtr.ns = new NumberingSystem(*other.fPtr.ns);
} else {
fPtr.ns = nullptr;
}
break;
}
}
void SymbolsWrapper::doMoveFrom(SymbolsWrapper&& src) {
fType = src.fType;
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
fPtr.dfs = src.fPtr.dfs;
src.fPtr.dfs = nullptr;
break;
case SYMPTR_NS:
fPtr.ns = src.fPtr.ns;
src.fPtr.ns = nullptr;
break;
}
}
void SymbolsWrapper::doCleanup() {
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
delete fPtr.dfs;
break;
case SYMPTR_NS:
delete fPtr.ns;
break;
}
}
bool SymbolsWrapper::isDecimalFormatSymbols() const {
return fType == SYMPTR_DFS;
}
bool SymbolsWrapper::isNumberingSystem() const {
return fType == SYMPTR_NS;
}
const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
U_ASSERT(fType == SYMPTR_DFS);
return fPtr.dfs;
}
const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
U_ASSERT(fType == SYMPTR_NS);
return fPtr.ns;
}
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode& status) const {
if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
auto results = new UFormattedNumberData();

View File

@ -25,9 +25,6 @@ using namespace icu::number;
using namespace icu::number::impl;
MicroPropsGenerator::~MicroPropsGenerator() = default;
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
: NumberFormatterImpl(macros, true, status) {
}
@ -162,6 +159,8 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|| !(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;
@ -241,7 +240,8 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// 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;
}
@ -249,6 +249,10 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
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
@ -265,16 +269,14 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
precision = Precision::integer().withMinDigits(2);
} else if (isCurrency) {
precision = Precision::currency(UCURR_USAGE_STANDARD);
} else if (macros.usage.isSet()) {
// Bogus Precision - it will get set in the UsagePrefsHandler instead
precision = Precision();
} else {
precision = Precision::maxFraction(6);
}
UNumberFormatRoundingMode roundingMode;
if (macros.roundingMode != kDefaultMode) {
roundingMode = macros.roundingMode;
} else {
// Temporary until ICU 64
roundingMode = precision.fRoundingMode;
}
roundingMode = macros.roundingMode;
fMicros.rounder = {precision, roundingMode, currency, status};
if (U_FAILURE(status)) {
return nullptr;
@ -375,6 +377,14 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
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,

View File

@ -94,6 +94,7 @@ class NumberFormatterImpl : public UMemory {
// 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;
@ -101,6 +102,10 @@ class NumberFormatterImpl : public UMemory {
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;

View File

@ -40,6 +40,9 @@ IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) {
}
void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fHasError) {
status = U_ILLEGAL_ARGUMENT_ERROR;
} else if (fUnion.minMaxInt.fMaxInt == -1) {

View File

@ -38,6 +38,7 @@ constexpr int32_t DNAM_INDEX = StandardPlural::Form::COUNT;
* `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) {
@ -52,6 +53,11 @@ 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,
@ -101,12 +107,18 @@ class PluralTableSink : public ResourceSink {
// 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);
@ -204,24 +216,26 @@ UnicodeString getPerUnitFormat(const Locale& locale, const UNumberUnitWidth &wid
} // 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) {
@ -244,7 +258,6 @@ void LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitR
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,
@ -390,6 +403,131 @@ const Modifier* LongNameHandler::getModifier(Signum /*signum*/, StandardPlural::
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 &micros,
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 &micros,
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 &micros.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 &micros.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 &micros.helpers.emptyWeakModifier;
}
// Return a SimpleModifier for the "premixed" pattern
micros.helpers.mixedUnitModifier =
SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, finalPlural});
return &micros.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,
@ -399,16 +537,23 @@ LongNameMultiplexer::forMeasureUnits(const Locale &loc, const MaybeStackVector<M
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;
}
@ -424,12 +569,15 @@ void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps
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;

View File

@ -8,6 +8,7 @@
#define __NUMBER_LONGNAMES_H__
#include "cmemory.h"
#include "unicode/listformatter.h"
#include "unicode/uversion.h"
#include "number_utils.h"
#include "number_modifiers.h"
@ -34,17 +35,47 @@ class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public
forCurrencyLongNames(const Locale &loc, const CurrencyUnit &currency, 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 &micros, 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;
@ -58,34 +89,136 @@ class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public
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 &micros,
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 &micros,
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 &micros,
UErrorCode &status) const U_OVERRIDE;
@ -95,8 +228,14 @@ class LongNameMultiplexer : public MicroPropsGenerator, public UMemory {
* 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) {

View File

@ -92,6 +92,8 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
int32_t minSig = properties.minimumSignificantDigits;
int32_t maxSig = properties.maximumSignificantDigits;
double roundingIncrement = properties.roundingIncrement;
// Not assigning directly to macros.roundingMode here: we change
// roundingMode if and when we also change macros.precision.
RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN);
bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
bool explicitMinMaxSig = minSig != -1 || maxSig != -1;
@ -145,7 +147,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
precision = Precision::constructCurrency(currencyUsage);
}
if (!precision.isBogus()) {
precision.fRoundingMode = roundingMode;
macros.roundingMode = roundingMode;
macros.precision = precision;
}
@ -239,7 +241,7 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert
// TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
macros.precision = Precision::constructSignificant(minSig_, maxSig_);
}
macros.precision.fRoundingMode = roundingMode;
macros.roundingMode = roundingMode;
}
}

View File

@ -22,6 +22,51 @@
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
@ -41,22 +86,48 @@ struct MicroProps : public MicroPropsGenerator {
// 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;

View File

@ -5,13 +5,16 @@
#if !UCONFIG_NO_FORMATTING
#include "charstr.h"
#include "uassert.h"
#include "unicode/numberformatter.h"
#include "number_types.h"
#include "number_decimalquantity.h"
#include "double-conversion.h"
#include "number_roundingutils.h"
#include "number_skeletons.h"
#include "putilimp.h"
#include "string_segment.h"
using namespace icu;
using namespace icu::number;
@ -19,6 +22,40 @@ using namespace icu::number::impl;
using double_conversion::DoubleToStringConverter;
using icu::StringSegment;
// Most blueprint_helpers live in number_skeletons.cpp. This one is in
// number_rounding.cpp for dependency reasons.
void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps &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);
}
}
namespace {
@ -84,7 +121,7 @@ digits_t roundingutils::doubleFractionLength(double input, int8_t* singleDigit)
Precision Precision::unlimited() {
return Precision(RND_NONE, {}, kDefaultMode);
return Precision(RND_NONE, {});
}
FractionPrecision Precision::integer() {
@ -229,7 +266,7 @@ FractionPrecision Precision::constructFraction(int32_t minFrac, int32_t maxFrac)
settings.fMaxSig = -1;
PrecisionUnion union_;
union_.fracSig = settings;
return {RND_FRACTION, union_, kDefaultMode};
return {RND_FRACTION, union_};
}
Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
@ -240,7 +277,7 @@ Precision Precision::constructSignificant(int32_t minSig, int32_t maxSig) {
settings.fMaxSig = static_cast<digits_t>(maxSig);
PrecisionUnion union_;
union_.fracSig = settings;
return {RND_SIGNIFICANT, union_, kDefaultMode};
return {RND_SIGNIFICANT, union_};
}
Precision
@ -250,7 +287,7 @@ Precision::constructFractionSignificant(const FractionPrecision &base, int32_t m
settings.fMaxSig = static_cast<digits_t>(maxSig);
PrecisionUnion union_;
union_.fracSig = settings;
return {RND_FRACTION_SIGNIFICANT, union_, kDefaultMode};
return {RND_FRACTION_SIGNIFICANT, union_};
}
IncrementPrecision Precision::constructIncrement(double increment, int32_t minFrac) {
@ -270,18 +307,18 @@ IncrementPrecision Precision::constructIncrement(double increment, int32_t minFr
// NOTE: In C++, we must return the correct value type with the correct union.
// It would be invalid to return a RND_FRACTION here because the methods on the
// IncrementPrecision type assume that the union is backed by increment data.
return {RND_INCREMENT_ONE, union_, kDefaultMode};
return {RND_INCREMENT_ONE, union_};
} else if (singleDigit == 5) {
return {RND_INCREMENT_FIVE, union_, kDefaultMode};
return {RND_INCREMENT_FIVE, union_};
} else {
return {RND_INCREMENT, union_, kDefaultMode};
return {RND_INCREMENT, union_};
}
}
CurrencyPrecision Precision::constructCurrency(UCurrencyUsage usage) {
PrecisionUnion union_;
union_.currencyUsage = usage;
return {RND_CURRENCY, union_, kDefaultMode};
return {RND_CURRENCY, union_};
}
@ -341,6 +378,9 @@ RoundingImpl::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl:
/** This is the method that contains the actual rounding logic. */
void RoundingImpl::apply(impl::DecimalQuantity &value, UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fPassThrough) {
return;
}

View File

@ -44,6 +44,9 @@ enum Section {
inline bool
getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
UErrorCode &status) {
if (U_FAILURE(status)) {
return false;
}
switch (roundingMode) {
case RoundingMode::UNUM_ROUND_UP:
// round away from zero
@ -187,6 +190,12 @@ class RoundingImpl {
Precision fPrecision;
UNumberFormatRoundingMode fRoundingMode;
bool fPassThrough = true; // default value
// Permits access to fPrecision.
friend class UsagePrefsHandler;
// Permits access to fPrecision.
friend class UnitConversionHandler;
};

View File

@ -152,21 +152,6 @@ UPRV_BLOCK_MACRO_BEGIN { \
} UPRV_BLOCK_MACRO_END
#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \
UPRV_BLOCK_MACRO_BEGIN { \
UErrorCode conversionStatus = U_ZERO_ERROR; \
(dest).appendInvariantChars({FALSE, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \
if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \
/* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \
(status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
return; \
} else if (U_FAILURE(conversionStatus)) { \
(status) = conversionStatus; \
return; \
} \
} UPRV_BLOCK_MACRO_END
} // anonymous namespace
@ -480,6 +465,7 @@ UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) {
MacroProps skeleton::parseSkeleton(
const UnicodeString& skeletonString, int32_t& errOffset, UErrorCode& status) {
U_ASSERT(U_SUCCESS(status));
U_ASSERT(kSerializedStemTrie != nullptr);
// Add a trailing whitespace to the end of the skeleton string to make code cleaner.
UnicodeString tempSkeletonString(skeletonString);
@ -586,6 +572,8 @@ MacroProps skeleton::parseSkeleton(
ParseState
skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
MacroProps& macros, UErrorCode& status) {
U_ASSERT(U_SUCCESS(status));
// First check for "blueprint" stems, which start with a "signal char"
switch (segment.charAt(0)) {
case u'.':
@ -764,6 +752,7 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
U_ASSERT(U_SUCCESS(status));
///// Required options: /////
@ -992,6 +981,7 @@ blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeS
void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
U_ASSERT(U_SUCCESS(status));
const UnicodeString stemString = segment.toTempUnicodeString();
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
@ -1007,7 +997,6 @@ void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, Mac
}
// Need to do char <-> UChar conversion...
U_ASSERT(U_SUCCESS(status));
CharString type;
SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
CharString subType;
@ -1069,7 +1058,15 @@ void blueprint_helpers::parseIdentifierUnitOption(const StringSegment& segment,
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) {
@ -1336,36 +1333,8 @@ bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroPr
return true;
}
void blueprint_helpers::parseIncrementOption(const StringSegment& segment, MacroProps& macros,
UErrorCode& status) {
// Need to do char <-> UChar conversion...
U_ASSERT(U_SUCCESS(status));
CharString buffer;
SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
// Utilize DecimalQuantity/decNumber to parse this for us.
DecimalQuantity dq;
UErrorCode localStatus = U_ZERO_ERROR;
dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
if (U_FAILURE(localStatus)) {
// throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
status = U_NUMBER_SKELETON_SYNTAX_ERROR;
return;
}
double increment = dq.toDouble();
// We also need to figure out how many digits. Do a brute force string operation.
int decimalOffset = 0;
while (decimalOffset < segment.length() && segment.charAt(decimalOffset) != '.') {
decimalOffset++;
}
if (decimalOffset == segment.length()) {
macros.precision = Precision::increment(increment);
} else {
int32_t fractionLength = segment.length() - decimalOffset - 1;
macros.precision = Precision::increment(increment).withMinFraction(fractionLength);
}
}
// blueprint_helpers::parseIncrementOption lives in number_rounding.cpp for
// dependencies reasons.
void blueprint_helpers::generateIncrementOption(double increment, int32_t trailingZeros, UnicodeString& sb,
UErrorCode&) {
@ -1551,10 +1520,14 @@ bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorC
} 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;
}
}
@ -1573,7 +1546,7 @@ bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErr
}
}
bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) {
if (macros.usage.fLength > 0) {
sb.append(u"usage/", -1);
sb.append(UnicodeString(macros.usage.fUsage, -1, US_INV));

View File

@ -246,6 +246,10 @@ void generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb
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);
@ -355,6 +359,24 @@ struct SeenMacroProps {
bool scale = false;
};
namespace {
#define SKELETON_UCHAR_TO_CHAR(dest, src, start, end, status) (void)(dest); \
UPRV_BLOCK_MACRO_BEGIN { \
UErrorCode conversionStatus = U_ZERO_ERROR; \
(dest).appendInvariantChars({false, (src).getBuffer() + (start), (end) - (start)}, conversionStatus); \
if (conversionStatus == U_INVARIANT_CONVERSION_ERROR) { \
/* Don't propagate the invariant conversion error; it is a skeleton syntax error */ \
(status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
return; \
} else if (U_FAILURE(conversionStatus)) { \
(status) = conversionStatus; \
return; \
} \
} UPRV_BLOCK_MACRO_END
} // namespace
} // namespace impl
} // namespace number
U_NAMESPACE_END

View File

@ -0,0 +1,125 @@
// © 2020 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "number_microprops.h"
#include "unicode/numberformatter.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
doCopyFrom(other);
}
SymbolsWrapper::SymbolsWrapper(SymbolsWrapper &&src) U_NOEXCEPT {
doMoveFrom(std::move(src));
}
SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
if (this == &other) {
return *this;
}
doCleanup();
doCopyFrom(other);
return *this;
}
SymbolsWrapper &SymbolsWrapper::operator=(SymbolsWrapper &&src) U_NOEXCEPT {
if (this == &src) {
return *this;
}
doCleanup();
doMoveFrom(std::move(src));
return *this;
}
SymbolsWrapper::~SymbolsWrapper() {
doCleanup();
}
void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
doCleanup();
fType = SYMPTR_DFS;
fPtr.dfs = new DecimalFormatSymbols(dfs);
}
void SymbolsWrapper::setTo(const NumberingSystem *ns) {
doCleanup();
fType = SYMPTR_NS;
fPtr.ns = ns;
}
void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
fType = other.fType;
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
// Memory allocation failures are exposed in copyErrorTo()
if (other.fPtr.dfs != nullptr) {
fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
} else {
fPtr.dfs = nullptr;
}
break;
case SYMPTR_NS:
// Memory allocation failures are exposed in copyErrorTo()
if (other.fPtr.ns != nullptr) {
fPtr.ns = new NumberingSystem(*other.fPtr.ns);
} else {
fPtr.ns = nullptr;
}
break;
}
}
void SymbolsWrapper::doMoveFrom(SymbolsWrapper &&src) {
fType = src.fType;
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
fPtr.dfs = src.fPtr.dfs;
src.fPtr.dfs = nullptr;
break;
case SYMPTR_NS:
fPtr.ns = src.fPtr.ns;
src.fPtr.ns = nullptr;
break;
}
}
void SymbolsWrapper::doCleanup() {
switch (fType) {
case SYMPTR_NONE:
// No action necessary
break;
case SYMPTR_DFS:
delete fPtr.dfs;
break;
case SYMPTR_NS:
delete fPtr.ns;
break;
}
}
bool SymbolsWrapper::isDecimalFormatSymbols() const {
return fType == SYMPTR_DFS;
}
bool SymbolsWrapper::isNumberingSystem() const {
return fType == SYMPTR_NS;
}
const DecimalFormatSymbols *SymbolsWrapper::getDecimalFormatSymbols() const {
U_ASSERT(fType == SYMPTR_DFS);
return fPtr.dfs;
}
const NumberingSystem *SymbolsWrapper::getNumberingSystem() const {
U_ASSERT(fType == SYMPTR_NS);
return fPtr.ns;
}

View File

@ -262,7 +262,7 @@ class U_I18N_API ModifierStore {
*/
class U_I18N_API MicroPropsGenerator {
public:
virtual ~MicroPropsGenerator();
virtual ~MicroPropsGenerator() = default;
/**
* Considers the given {@link DecimalQuantity}, optionally mutates it, and

View File

@ -10,6 +10,7 @@
#include "number_decimalquantity.h"
#include "number_microprops.h"
#include "number_roundingutils.h"
#include "number_skeletons.h"
#include "unicode/char16ptr.h"
#include "unicode/currunit.h"
#include "unicode/fmtable.h"
@ -18,9 +19,13 @@
#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) {
@ -41,10 +46,7 @@ Usage &Usage::operator=(const Usage &other) {
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;
@ -84,8 +86,43 @@ void Usage::set(StringPiece value) {
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)
@ -102,25 +139,95 @@ void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
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, &micros, 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 &micros,
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, &micros, 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 */

View File

@ -9,18 +9,23 @@
#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
using ::icu::units::ComplexUnitsConverter;
using ::icu::units::UnitsRouter;
namespace number {
namespace impl {
using ::icu::units::UnitsRouter;
/**
* A MicroPropsGenerator which uses UnitsRouter to produce output converted to a
* MeasureUnit appropriate for a particular localized usage: see
@ -28,12 +33,15 @@ using ::icu::units::UnitsRouter;
*/
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 &micros,
UErrorCode &status) const U_OVERRIDE;
@ -52,6 +60,62 @@ class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory
private:
UnitsRouter fUnitsRouter;
const MicroPropsGenerator *fParent;
static Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, UErrorCode status);
};
} // namespace impl
} // 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 &micros,
UErrorCode &status) const U_OVERRIDE;
private:
MeasureUnit fOutputUnit;
LocalPointer<ComplexUnitsConverter> fUnitConverter;
const MicroPropsGenerator *fParent;
};
} // namespace impl

View File

@ -32,7 +32,6 @@ collationsettings.cpp
collationtailoring.cpp
collationweights.cpp
compactdecimalformat.cpp
complexunitsconverter.cpp
coptccal.cpp
cpdtrans.cpp
csdetect.cpp
@ -122,6 +121,7 @@ number_patternstring.cpp
number_rounding.cpp
number_scientific.cpp
number_skeletons.cpp
number_symbolswrapper.cpp
number_usageprefs.cpp
number_utils.cpp
numfmt.cpp
@ -210,9 +210,10 @@ ulocdata.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

View File

@ -707,12 +707,8 @@ class U_I18N_API Precision : public UMemory {
typedef PrecisionUnion::FractionSignificantSettings FractionSignificantSettings;
typedef PrecisionUnion::IncrementSettings IncrementSettings;
/** The Precision encapsulates the RoundingMode when used within the implementation. */
UNumberFormatRoundingMode fRoundingMode;
Precision(const PrecisionType& type, const PrecisionUnion& union_,
UNumberFormatRoundingMode roundingMode)
: fType(type), fUnion(union_), fRoundingMode(roundingMode) {}
Precision(const PrecisionType& type, const PrecisionUnion& union_)
: fType(type), fUnion(union_) {}
Precision(UErrorCode errorCode) : fType(RND_ERROR) {
fUnion.errorCode = errorCode;
@ -746,8 +742,6 @@ class U_I18N_API Precision : public UMemory {
static CurrencyPrecision constructCurrency(UCurrencyUsage usage);
static Precision constructPassThrough();
// To allow MacroProps/MicroProps to initialize bogus instances:
friend struct impl::MacroProps;
friend struct impl::MicroProps;
@ -769,9 +763,7 @@ class U_I18N_API Precision : public UMemory {
// To allow access to the skeleton generation code:
friend class impl::GeneratorHelpers;
// TODO(units): revisit when UnitsRouter is changed: do we still need this
// once Precision is returned by UnitsRouter? For now, we allow access to
// Precision constructor from UsagePrefsHandler:
// To allow access to isBogus and the default (bogus) constructor:
friend class impl::UsagePrefsHandler;
};

View File

@ -8,14 +8,14 @@
#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 {
@ -30,6 +30,11 @@ ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
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.
@ -101,13 +106,21 @@ UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) c
}
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));
@ -115,7 +128,14 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
// 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);
@ -125,6 +145,24 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
}
}
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;
}

View File

@ -4,18 +4,36 @@
#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 {
/**
@ -27,7 +45,7 @@ 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`.
@ -59,12 +77,15 @@ class U_I18N_API ComplexUnitsConverter : UMemory {
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 */

View File

@ -13,7 +13,7 @@
#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>
@ -516,11 +516,7 @@ double UnitConverter::convert(double inputValue) const {
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

View File

@ -4,16 +4,16 @@
#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 {
@ -81,7 +81,7 @@ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Sign
/**
* 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;
@ -159,6 +159,6 @@ class U_I18N_API UnitConverter : public UMemory {
} // namespace units
U_NAMESPACE_END
#endif //__UNITCONVERTER_H__
#endif //__UNITS_CONVERTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -11,7 +11,7 @@
#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>
@ -293,9 +293,7 @@ int32_t getPreferenceMetadataIndex(const MaybeStackVector<UnitPreferenceMetadata
} 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;
}
@ -310,6 +308,7 @@ int32_t getPreferenceMetadataIndex(const MaybeStackVector<UnitPreferenceMetadata
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;
}

View File

@ -4,8 +4,8 @@
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef __GETUNITSDATA_H__
#define __GETUNITSDATA_H__
#ifndef __UNITS_DATA_H__
#define __UNITS_DATA_H__
#include <limits>
@ -26,10 +26,10 @@ namespace units {
* 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);
@ -59,6 +59,22 @@ class U_I18N_API ConversionRateInfo : public UMemory {
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.
*
@ -136,6 +152,25 @@ class U_I18N_API UnitPreferenceMetadata : public UMemory {
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.
*/
@ -189,6 +224,6 @@ class U_I18N_API UnitPreferences {
} // namespace units
U_NAMESPACE_END
#endif //__GETUNITSDATA_H__
#endif //__UNITS_DATA_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -12,8 +12,8 @@
#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 {
@ -70,19 +70,19 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
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 {

View File

@ -4,18 +4,18 @@
#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
@ -25,11 +25,25 @@ class Measure;
namespace units {
struct RouteResult : UMemory {
// A list of measures: a single measure for single units, multiple measures
// for mixed units.
//
// TODO(icu-units/icu#21): figure out the right mixed unit API.
MaybeStackVector<Measure> measures;
// A skeleton string starting with a precision-increment.
//
// TODO(hugovdm): generalise? or narrow down to only a precision-increment?
// or document that other skeleton elements are ignored?
UnicodeString precision;
RouteResult(MaybeStackVector<Measure> measures, UnicodeString precision)
: 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)) {}
};
/**
@ -46,6 +60,10 @@ struct ConverterPreference : UMemory {
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)
@ -56,9 +74,25 @@ struct ConverterPreference : UMemory {
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.
@ -114,6 +148,6 @@ class U_I18N_API UnitsRouter {
} // namespace units
U_NAMESPACE_END
#endif //__UNITSROUTER_H__
#endif //__UNITS_ROUTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -869,7 +869,8 @@ library: i18n
dayperiodrules
listformatter
formatting formattable_cnv regex regex_cnv translit
double_conversion number_representation number_output numberformatter number_skeletons numberparser
double_conversion number_representation number_output numberformatter
number_skeletons number_usageprefs numberparser
units_extra unitsformatter
universal_time_scale
uclean_i18n
@ -976,11 +977,9 @@ group: number_output
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
@ -989,17 +988,43 @@ group: numberformatter
number_decimfmtprops.o
number_fluent.o number_formatimpl.o
number_grouping.o number_integerwidth.o number_longnames.o
number_mapper.o number_modifiers.o number_multiplier.o
number_mapper.o number_modifiers.o
number_notation.o number_padding.o
number_patternmodifier.o number_patternstring.o number_rounding.o
number_scientific.o number_usageprefs.o
currpinf.o dcfmtsym.o numsys.o
number_patternmodifier.o number_patternstring.o
number_scientific.o
currpinf.o
numrange_fluent.o numrange_impl.o
deps
decnumber double_conversion formattable units unitsformatter
number_representation number_output
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
@ -1007,6 +1032,12 @@ group: number_skeletons
numberformatter
units_extra
group: number_symbolswrapper
number_symbolswrapper.o
deps
platform
numsys
group: numberparser
numparse_affixes.o numparse_compositions.o numparse_currency.o
numparse_decimal.o numparse_impl.o numparse_parsednumber.o
@ -1076,7 +1107,7 @@ group: units
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

View File

@ -69,7 +69,7 @@ string_segment_test.o \
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)

View File

@ -285,9 +285,9 @@
<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" />

View File

@ -550,13 +550,13 @@
<ClCompile Include="localematchertest.cpp">
<Filter>locales &amp; 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>

View File

@ -82,6 +82,7 @@ private:
void TestNumericTimeSomeSpecialFormats();
void TestIdentifiers();
void TestInvalidIdentifiers();
void TestParseToBuiltIn();
void TestKilogramIdentifier();
void TestCompoundUnitOperations();
void TestDimensionlessBehaviour();
@ -209,6 +210,7 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
TESTCASE_AUTO(TestIdentifiers);
TESTCASE_AUTO(TestInvalidIdentifiers);
TESTCASE_AUTO(TestParseToBuiltIn);
TESTCASE_AUTO(TestKilogramIdentifier);
TESTCASE_AUTO(TestCompoundUnitOperations);
TESTCASE_AUTO(TestDimensionlessBehaviour);
@ -3514,11 +3516,23 @@ void MeasureFormatTest::TestIndividualPluralFallback() {
// 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() {
@ -3705,6 +3719,33 @@ void MeasureFormatTest::TestInvalidIdentifiers() {
}
}
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() {

View File

@ -55,6 +55,8 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
void unitMeasure();
void unitCompoundMeasure();
void unitUsage();
void unitUsageErrorCodes();
void unitUsageSkeletons();
void unitCurrency();
void unitPercent();
void percentParity();
@ -85,6 +87,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
void localPointerCAPI();
void toObject();
void toDecimalNumber();
void microPropsInternals();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);

View File

@ -11,12 +11,14 @@
#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;
@ -76,6 +78,8 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
TESTCASE_AUTO(unitMeasure);
TESTCASE_AUTO(unitCompoundMeasure);
TESTCASE_AUTO(unitUsage);
TESTCASE_AUTO(unitUsageErrorCodes);
TESTCASE_AUTO(unitUsageSkeletons);
TESTCASE_AUTO(unitCurrency);
TESTCASE_AUTO(unitPercent);
if (!quick) {
@ -113,6 +117,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
TESTCASE_AUTO(localPointerCAPI);
TESTCASE_AUTO(toObject);
TESTCASE_AUTO(toDecimalNumber);
TESTCASE_AUTO(microPropsInternals);
TESTCASE_AUTO_END;
}
@ -520,6 +525,8 @@ void NumberFormatterApiTest::notationCompact() {
}
void NumberFormatterApiTest::unitMeasure() {
IcuTestErrorCode status(*this, "unitMeasure()");
assertFormatDescending(
u"Meters Short and unit() method",
u"measure-unit/length-meter",
@ -686,82 +693,86 @@ void NumberFormatterApiTest::unitMeasure() {
Locale("es-MX"),
5,
u"5 a\u00F1os");
}
void NumberFormatterApiTest::unitUsage() {
UnlocalizedNumberFormatter unloc_formatter =
NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
IcuTestErrorCode status(*this, "unitUsage()");
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");
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");
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",
nullptr,
u"unit/yard-and-foot-and-inch",
NumberFormatter::with()
.unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
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");
3.65,
"3 yd, 1 ft, 11.4 in");
// 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");
// 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");
// 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");
// 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"),
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",
@ -778,6 +789,17 @@ void NumberFormatterApiTest::unitCompoundMeasure() {
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",
@ -826,6 +848,427 @@ void NumberFormatterApiTest::unitCompoundMeasure() {
// 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() {
@ -3604,6 +4047,33 @@ void NumberFormatterApiTest::toDecimalNumber() {
"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,

View File

@ -5,7 +5,7 @@
#if !UCONFIG_NO_FORMATTING
#include "unitsdata.h"
#include "units_data.h"
#include "intltest.h"
using namespace ::icu::units;

View File

@ -7,7 +7,7 @@
#include "intltest.h"
#include "unicode/unistr.h"
#include "unitsrouter.h"
#include "units_router.h"
class UnitsRouterTest : public IntlTest {

View File

@ -19,9 +19,10 @@
#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"
@ -44,6 +45,7 @@ class UnitsTest : public IntlTest {
void testUnitConstantFreshness();
void testConversionCapability();
void testConversions();
void testComplexUnitsConverter();
void testComplexUnitConverterSorting();
void testPreferences();
void testSiPrefixes();
@ -62,6 +64,7 @@ void UnitsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, cha
TESTCASE_AUTO(testUnitConstantFreshness);
TESTCASE_AUTO(testConversionCapability);
TESTCASE_AUTO(testConversions);
TESTCASE_AUTO(testComplexUnitsConverter);
TESTCASE_AUTO(testComplexUnitConverterSorting);
TESTCASE_AUTO(testPreferences);
TESTCASE_AUTO(testSiPrefixes);
@ -88,7 +91,7 @@ void UnitsTest::testUnitConstantFreshness() {
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;
@ -105,7 +108,7 @@ void UnitsTest::testUnitConstantFreshness() {
}
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
@ -294,19 +297,21 @@ void UnitsTest::testArea() {
}
/**
* 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);
}
@ -359,11 +364,13 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
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;
@ -378,14 +385,14 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
// 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;
}
@ -394,7 +401,7 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
// 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);
@ -405,7 +412,7 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
/**
* 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";
@ -420,7 +427,7 @@ void UnitsTest::testConversions() {
}
CharString path(sourceTestDataPath, errorCode);
path.appendPathPart("units", errorCode);
path.appendPathPart("cldr/units", errorCode);
path.appendPathPart(filename, errorCode);
ConversionRates rates(errorCode);
@ -431,6 +438,92 @@ void UnitsTest::testConversions() {
}
}
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");
@ -442,11 +535,11 @@ void 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);
}
/**
@ -750,7 +843,9 @@ void parsePreferencesTests(const char *filename, char delimiter, char *fields[][
}
/**
* 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";
@ -765,7 +860,7 @@ void UnitsTest::testPreferences() {
}
CharString path(sourceTestDataPath, errorCode);
path.appendPathPart("units", errorCode);
path.appendPathPart("cldr/units", errorCode);
path.appendPathPart(filename, errorCode);
parsePreferencesTests(path.data(), ';', fields, maxFields, unitPreferencesTestDataLineFn, this,

View File

@ -1,453 +0,0 @@
# 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

View File

@ -1,197 +0,0 @@
# 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