mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 17:01:16 +00:00
parent
7c412f9c8f
commit
1c61c57ece
17 changed files with 893 additions and 344 deletions
|
@ -41,12 +41,20 @@ namespace {
|
|||
// TODO: Propose a new error code for this?
|
||||
constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
// Trie value offset for SI Prefixes. This is big enough to ensure we only
|
||||
// Trie value offset for SI or binary prefixes. This is big enough to ensure we only
|
||||
// insert positive integers into the trie.
|
||||
constexpr int32_t kSIPrefixOffset = 64;
|
||||
constexpr int32_t kPrefixOffset = 64;
|
||||
static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_BIN > 0,
|
||||
"kPrefixOffset is too small for minimum UMeasurePrefix value");
|
||||
static_assert(kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MIN_SI > 0,
|
||||
"kPrefixOffset is too small for minimum UMeasurePrefix value");
|
||||
|
||||
// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
|
||||
constexpr int32_t kCompoundPartOffset = 128;
|
||||
static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_BIN,
|
||||
"Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
|
||||
static_assert(kCompoundPartOffset > kPrefixOffset + UMEASURE_PREFIX_INTERNAL_MAX_SI,
|
||||
"Ambiguous token values: prefix tokens are overlapping with CompoundPart tokens");
|
||||
|
||||
enum CompoundPart {
|
||||
// Represents "-per-"
|
||||
|
@ -90,30 +98,40 @@ enum PowerPart {
|
|||
// "fluid-ounce-imperial".
|
||||
constexpr int32_t kSimpleUnitOffset = 512;
|
||||
|
||||
const struct SIPrefixStrings {
|
||||
const struct UnitPrefixStrings {
|
||||
const char* const string;
|
||||
UMeasureSIPrefix value;
|
||||
} gSIPrefixStrings[] = {
|
||||
{ "yotta", UMEASURE_SI_PREFIX_YOTTA },
|
||||
{ "zetta", UMEASURE_SI_PREFIX_ZETTA },
|
||||
{ "exa", UMEASURE_SI_PREFIX_EXA },
|
||||
{ "peta", UMEASURE_SI_PREFIX_PETA },
|
||||
{ "tera", UMEASURE_SI_PREFIX_TERA },
|
||||
{ "giga", UMEASURE_SI_PREFIX_GIGA },
|
||||
{ "mega", UMEASURE_SI_PREFIX_MEGA },
|
||||
{ "kilo", UMEASURE_SI_PREFIX_KILO },
|
||||
{ "hecto", UMEASURE_SI_PREFIX_HECTO },
|
||||
{ "deka", UMEASURE_SI_PREFIX_DEKA },
|
||||
{ "deci", UMEASURE_SI_PREFIX_DECI },
|
||||
{ "centi", UMEASURE_SI_PREFIX_CENTI },
|
||||
{ "milli", UMEASURE_SI_PREFIX_MILLI },
|
||||
{ "micro", UMEASURE_SI_PREFIX_MICRO },
|
||||
{ "nano", UMEASURE_SI_PREFIX_NANO },
|
||||
{ "pico", UMEASURE_SI_PREFIX_PICO },
|
||||
{ "femto", UMEASURE_SI_PREFIX_FEMTO },
|
||||
{ "atto", UMEASURE_SI_PREFIX_ATTO },
|
||||
{ "zepto", UMEASURE_SI_PREFIX_ZEPTO },
|
||||
{ "yocto", UMEASURE_SI_PREFIX_YOCTO },
|
||||
UMeasurePrefix value;
|
||||
} gUnitPrefixStrings[] = {
|
||||
// SI prefixes
|
||||
{ "yotta", UMEASURE_PREFIX_YOTTA },
|
||||
{ "zetta", UMEASURE_PREFIX_ZETTA },
|
||||
{ "exa", UMEASURE_PREFIX_EXA },
|
||||
{ "peta", UMEASURE_PREFIX_PETA },
|
||||
{ "tera", UMEASURE_PREFIX_TERA },
|
||||
{ "giga", UMEASURE_PREFIX_GIGA },
|
||||
{ "mega", UMEASURE_PREFIX_MEGA },
|
||||
{ "kilo", UMEASURE_PREFIX_KILO },
|
||||
{ "hecto", UMEASURE_PREFIX_HECTO },
|
||||
{ "deka", UMEASURE_PREFIX_DEKA },
|
||||
{ "deci", UMEASURE_PREFIX_DECI },
|
||||
{ "centi", UMEASURE_PREFIX_CENTI },
|
||||
{ "milli", UMEASURE_PREFIX_MILLI },
|
||||
{ "micro", UMEASURE_PREFIX_MICRO },
|
||||
{ "nano", UMEASURE_PREFIX_NANO },
|
||||
{ "pico", UMEASURE_PREFIX_PICO },
|
||||
{ "femto", UMEASURE_PREFIX_FEMTO },
|
||||
{ "atto", UMEASURE_PREFIX_ATTO },
|
||||
{ "zepto", UMEASURE_PREFIX_ZEPTO },
|
||||
{ "yocto", UMEASURE_PREFIX_YOCTO },
|
||||
// Binary prefixes
|
||||
{ "yobi", UMEASURE_PREFIX_YOBI },
|
||||
{ "zebi", UMEASURE_PREFIX_ZEBI },
|
||||
{ "exbi", UMEASURE_PREFIX_EXBI },
|
||||
{ "pebi", UMEASURE_PREFIX_PEBI },
|
||||
{ "tebi", UMEASURE_PREFIX_TEBI },
|
||||
{ "gibi", UMEASURE_PREFIX_GIBI },
|
||||
{ "mebi", UMEASURE_PREFIX_MEBI },
|
||||
{ "kibi", UMEASURE_PREFIX_KIBI },
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -221,9 +239,9 @@ void U_CALLCONV initUnitExtras(UErrorCode& status) {
|
|||
BytesTrieBuilder b(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
// Add SI prefixes
|
||||
for (const auto& siPrefixInfo : gSIPrefixStrings) {
|
||||
b.add(siPrefixInfo.string, siPrefixInfo.value + kSIPrefixOffset, status);
|
||||
// Add SI and binary prefixes
|
||||
for (const auto& unitPrefixInfo : gUnitPrefixStrings) {
|
||||
b.add(unitPrefixInfo.string, unitPrefixInfo.value + kPrefixOffset, status);
|
||||
}
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
|
@ -295,7 +313,7 @@ public:
|
|||
|
||||
enum Type {
|
||||
TYPE_UNDEFINED,
|
||||
TYPE_SI_PREFIX,
|
||||
TYPE_PREFIX,
|
||||
// Token type for "-per-", "-", and "-and-".
|
||||
TYPE_COMPOUND_PART,
|
||||
// Token type for "per-".
|
||||
|
@ -309,7 +327,7 @@ public:
|
|||
Type getType() const {
|
||||
U_ASSERT(fMatch > 0);
|
||||
if (fMatch < kCompoundPartOffset) {
|
||||
return TYPE_SI_PREFIX;
|
||||
return TYPE_PREFIX;
|
||||
}
|
||||
if (fMatch < kInitialCompoundPartOffset) {
|
||||
return TYPE_COMPOUND_PART;
|
||||
|
@ -323,9 +341,9 @@ public:
|
|||
return TYPE_SIMPLE_UNIT;
|
||||
}
|
||||
|
||||
UMeasureSIPrefix getSIPrefix() const {
|
||||
U_ASSERT(getType() == TYPE_SI_PREFIX);
|
||||
return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
|
||||
UMeasurePrefix getUnitPrefix() const {
|
||||
U_ASSERT(getType() == TYPE_PREFIX);
|
||||
return static_cast<UMeasurePrefix>(fMatch - kPrefixOffset);
|
||||
}
|
||||
|
||||
// Valid only for tokens with type TYPE_COMPOUND_PART.
|
||||
|
@ -511,9 +529,9 @@ private:
|
|||
}
|
||||
|
||||
// state:
|
||||
// 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
|
||||
// 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit)
|
||||
// 1 = power token seen (will not accept another power token)
|
||||
// 2 = SI prefix token seen (will not accept a power or SI prefix token)
|
||||
// 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token)
|
||||
int32_t state = 0;
|
||||
|
||||
bool atStart = fIndex == 0;
|
||||
|
@ -589,12 +607,12 @@ private:
|
|||
state = 1;
|
||||
break;
|
||||
|
||||
case Token::TYPE_SI_PREFIX:
|
||||
case Token::TYPE_PREFIX:
|
||||
if (state > 1) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return result;
|
||||
}
|
||||
result.siPrefix = token.getSIPrefix();
|
||||
result.unitPrefix = token.getUnitPrefix();
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
|
@ -622,6 +640,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
// Sorting function wrapping SingleUnitImpl::compareTo for use with uprv_sortArray.
|
||||
int32_t U_CALLCONV
|
||||
compareSingleUnits(const void* /*context*/, const void* left, const void* right) {
|
||||
auto realLeft = static_cast<const SingleUnitImpl* const*>(left);
|
||||
|
@ -631,7 +650,29 @@ compareSingleUnits(const void* /*context*/, const void* left, const void* right)
|
|||
|
||||
} // namespace
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
umeas_getPrefixPower(UMeasurePrefix unitPrefix) {
|
||||
if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
|
||||
unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
|
||||
return unitPrefix - UMEASURE_PREFIX_INTERNAL_ONE_BIN;
|
||||
}
|
||||
U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&
|
||||
unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI);
|
||||
return unitPrefix - UMEASURE_PREFIX_ONE;
|
||||
}
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
umeas_getPrefixBase(UMeasurePrefix unitPrefix) {
|
||||
if (unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_BIN &&
|
||||
unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_BIN) {
|
||||
return 1024;
|
||||
}
|
||||
U_ASSERT(unitPrefix >= UMEASURE_PREFIX_INTERNAL_MIN_SI &&
|
||||
unitPrefix <= UMEASURE_PREFIX_INTERNAL_MAX_SI);
|
||||
return 10;
|
||||
}
|
||||
|
||||
// In ICU4J, this is MeasureUnit.getSingleUnitImpl().
|
||||
SingleUnitImpl SingleUnitImpl::forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status) {
|
||||
MeasureUnitImpl temp;
|
||||
const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(measureUnit, temp, status);
|
||||
|
@ -682,13 +723,21 @@ void SingleUnitImpl::appendNeutralIdentifier(CharString &result, UErrorCode &sta
|
|||
return;
|
||||
}
|
||||
|
||||
if (this->siPrefix != UMEASURE_SI_PREFIX_ONE) {
|
||||
for (const auto &siPrefixInfo : gSIPrefixStrings) {
|
||||
if (siPrefixInfo.value == this->siPrefix) {
|
||||
result.append(siPrefixInfo.string, status);
|
||||
if (this->unitPrefix != UMEASURE_PREFIX_ONE) {
|
||||
bool found = false;
|
||||
for (const auto &unitPrefixInfo : gUnitPrefixStrings) {
|
||||
// TODO: consider using binary search? If we do this, add a unit
|
||||
// test to ensure gUnitPrefixStrings is sorted?
|
||||
if (unitPrefixInfo.value == this->unitPrefix) {
|
||||
result.append(unitPrefixInfo.string, status);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result.append(StringPiece(this->getSimpleUnitID()), status);
|
||||
|
@ -868,13 +917,13 @@ UMeasureUnitComplexity MeasureUnit::getComplexity(UErrorCode& status) const {
|
|||
return MeasureUnitImpl::forMeasureUnit(*this, temp, status).complexity;
|
||||
}
|
||||
|
||||
UMeasureSIPrefix MeasureUnit::getSIPrefix(UErrorCode& status) const {
|
||||
return SingleUnitImpl::forMeasureUnit(*this, status).siPrefix;
|
||||
UMeasurePrefix MeasureUnit::getPrefix(UErrorCode& status) const {
|
||||
return SingleUnitImpl::forMeasureUnit(*this, status).unitPrefix;
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& status) const {
|
||||
MeasureUnit MeasureUnit::withPrefix(UMeasurePrefix prefix, UErrorCode& status) const {
|
||||
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
|
||||
singleUnit.siPrefix = prefix;
|
||||
singleUnit.unitPrefix = prefix;
|
||||
return singleUnit.build(status);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ struct U_I18N_API MeasureUnitImplWithIndex : public UMemory {
|
|||
};
|
||||
|
||||
/**
|
||||
* A struct representing a single unit (optional SI prefix and dimensionality).
|
||||
* A struct representing a single unit (optional SI or binary prefix, and dimensionality).
|
||||
*/
|
||||
struct U_I18N_API SingleUnitImpl : public UMemory {
|
||||
/**
|
||||
|
@ -96,10 +96,12 @@ struct U_I18N_API SingleUnitImpl : public UMemory {
|
|||
if (index > other.index) {
|
||||
return 1;
|
||||
}
|
||||
if (siPrefix < other.siPrefix) {
|
||||
// TODO(icu-units#70): revisit when fixing normalization. For now we're
|
||||
// sorting binary prefixes before SI prefixes, as per enum values order.
|
||||
if (unitPrefix < other.unitPrefix) {
|
||||
return -1;
|
||||
}
|
||||
if (siPrefix > other.siPrefix) {
|
||||
if (unitPrefix > other.unitPrefix) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -108,8 +110,8 @@ struct U_I18N_API SingleUnitImpl : public UMemory {
|
|||
/**
|
||||
* Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
|
||||
*
|
||||
* Units with the same base unit and SI prefix should match, except that they must also have
|
||||
* the same dimensionality sign, such that we don't merge numerator and denominator.
|
||||
* Units with the same base unit and SI or binary prefix should match, except that they must also
|
||||
* have the same dimensionality sign, such that we don't merge numerator and denominator.
|
||||
*/
|
||||
bool isCompatibleWith(const SingleUnitImpl& other) const {
|
||||
return (compareTo(other) == 0);
|
||||
|
@ -134,11 +136,11 @@ struct U_I18N_API SingleUnitImpl : public UMemory {
|
|||
int32_t index = -1;
|
||||
|
||||
/**
|
||||
* SI prefix.
|
||||
* SI or binary prefix.
|
||||
*
|
||||
* This is ignored for the dimensionless unit.
|
||||
*/
|
||||
UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
|
||||
UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE;
|
||||
|
||||
/**
|
||||
* Dimensionality.
|
||||
|
|
|
@ -36,15 +36,15 @@ class MeasureUnitImpl;
|
|||
/**
|
||||
* Enumeration for unit complexity. There are three levels:
|
||||
*
|
||||
* - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
|
||||
* square-kilometer, kilojoule, per-second.
|
||||
* - SINGLE: A single unit, optionally with a power and/or SI or binary prefix.
|
||||
* Examples: hectare, square-kilometer, kilojoule, per-second, mebibyte.
|
||||
* - COMPOUND: A unit composed of the product of multiple single units. Examples:
|
||||
* meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
|
||||
* - MIXED: A unit composed of the sum of multiple single units. Examples: foot+inch,
|
||||
* hour+minute+second, degree+arcminute+arcsecond.
|
||||
*
|
||||
* The complexity determines which operations are available. For example, you cannot set the power
|
||||
* or SI prefix of a compound unit.
|
||||
* or prefix of a compound unit.
|
||||
*
|
||||
* @draft ICU 67
|
||||
*/
|
||||
|
@ -72,159 +72,276 @@ enum UMeasureUnitComplexity {
|
|||
};
|
||||
|
||||
/**
|
||||
* Enumeration for SI prefixes, such as "kilo".
|
||||
* Enumeration for SI and binary prefixes, e.g. "kilo-", "nano-", "mebi-".
|
||||
*
|
||||
* @draft ICU 67
|
||||
* Enum values should be treated as opaque: use umeas_getPrefixPower() and
|
||||
* umeas_getPrefixBase() to find their corresponding values.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @see umeas_getPrefixBase
|
||||
* @see umeas_getPrefixPower
|
||||
*/
|
||||
typedef enum UMeasureSIPrefix {
|
||||
typedef enum UMeasurePrefix {
|
||||
/**
|
||||
* The absence of an SI or binary prefix.
|
||||
*
|
||||
* The integer representation of this enum value is an arbitrary
|
||||
* implementation detail and should not be relied upon: use
|
||||
* umeas_getPrefixPower() to obtain meaningful values.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_ONE = 30 + 0,
|
||||
|
||||
/**
|
||||
* SI prefix: yotta, 10^24.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_YOTTA = 24,
|
||||
UMEASURE_PREFIX_YOTTA = UMEASURE_PREFIX_ONE + 24,
|
||||
|
||||
/**
|
||||
* ICU use only.
|
||||
* Used to determine the set of base-10 SI prefixes.
|
||||
* @internal
|
||||
*/
|
||||
UMEASURE_PREFIX_INTERNAL_MAX_SI = UMEASURE_PREFIX_YOTTA,
|
||||
|
||||
/**
|
||||
* SI prefix: zetta, 10^21.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_ZETTA = 21,
|
||||
UMEASURE_PREFIX_ZETTA = UMEASURE_PREFIX_ONE + 21,
|
||||
|
||||
/**
|
||||
* SI prefix: exa, 10^18.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_EXA = 18,
|
||||
UMEASURE_PREFIX_EXA = UMEASURE_PREFIX_ONE + 18,
|
||||
|
||||
/**
|
||||
* SI prefix: peta, 10^15.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_PETA = 15,
|
||||
UMEASURE_PREFIX_PETA = UMEASURE_PREFIX_ONE + 15,
|
||||
|
||||
/**
|
||||
* SI prefix: tera, 10^12.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_TERA = 12,
|
||||
UMEASURE_PREFIX_TERA = UMEASURE_PREFIX_ONE + 12,
|
||||
|
||||
/**
|
||||
* SI prefix: giga, 10^9.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_GIGA = 9,
|
||||
UMEASURE_PREFIX_GIGA = UMEASURE_PREFIX_ONE + 9,
|
||||
|
||||
/**
|
||||
* SI prefix: mega, 10^6.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_MEGA = 6,
|
||||
UMEASURE_PREFIX_MEGA = UMEASURE_PREFIX_ONE + 6,
|
||||
|
||||
/**
|
||||
* SI prefix: kilo, 10^3.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_KILO = 3,
|
||||
UMEASURE_PREFIX_KILO = UMEASURE_PREFIX_ONE + 3,
|
||||
|
||||
/**
|
||||
* SI prefix: hecto, 10^2.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_HECTO = 2,
|
||||
UMEASURE_PREFIX_HECTO = UMEASURE_PREFIX_ONE + 2,
|
||||
|
||||
/**
|
||||
* SI prefix: deka, 10^1.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_DEKA = 1,
|
||||
|
||||
/**
|
||||
* The absence of an SI prefix.
|
||||
*
|
||||
* @draft ICU 67
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_ONE = 0,
|
||||
UMEASURE_PREFIX_DEKA = UMEASURE_PREFIX_ONE + 1,
|
||||
|
||||
/**
|
||||
* SI prefix: deci, 10^-1.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_DECI = -1,
|
||||
UMEASURE_PREFIX_DECI = UMEASURE_PREFIX_ONE + -1,
|
||||
|
||||
/**
|
||||
* SI prefix: centi, 10^-2.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_CENTI = -2,
|
||||
UMEASURE_PREFIX_CENTI = UMEASURE_PREFIX_ONE + -2,
|
||||
|
||||
/**
|
||||
* SI prefix: milli, 10^-3.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_MILLI = -3,
|
||||
UMEASURE_PREFIX_MILLI = UMEASURE_PREFIX_ONE + -3,
|
||||
|
||||
/**
|
||||
* SI prefix: micro, 10^-6.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_MICRO = -6,
|
||||
UMEASURE_PREFIX_MICRO = UMEASURE_PREFIX_ONE + -6,
|
||||
|
||||
/**
|
||||
* SI prefix: nano, 10^-9.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_NANO = -9,
|
||||
UMEASURE_PREFIX_NANO = UMEASURE_PREFIX_ONE + -9,
|
||||
|
||||
/**
|
||||
* SI prefix: pico, 10^-12.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_PICO = -12,
|
||||
UMEASURE_PREFIX_PICO = UMEASURE_PREFIX_ONE + -12,
|
||||
|
||||
/**
|
||||
* SI prefix: femto, 10^-15.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_FEMTO = -15,
|
||||
UMEASURE_PREFIX_FEMTO = UMEASURE_PREFIX_ONE + -15,
|
||||
|
||||
/**
|
||||
* SI prefix: atto, 10^-18.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_ATTO = -18,
|
||||
UMEASURE_PREFIX_ATTO = UMEASURE_PREFIX_ONE + -18,
|
||||
|
||||
/**
|
||||
* SI prefix: zepto, 10^-21.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_ZEPTO = -21,
|
||||
UMEASURE_PREFIX_ZEPTO = UMEASURE_PREFIX_ONE + -21,
|
||||
|
||||
/**
|
||||
* SI prefix: yocto, 10^-24.
|
||||
*
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_SI_PREFIX_YOCTO = -24
|
||||
} UMeasureSIPrefix;
|
||||
UMEASURE_PREFIX_YOCTO = UMEASURE_PREFIX_ONE + -24,
|
||||
|
||||
/**
|
||||
* ICU use only.
|
||||
* Used to determine the set of base-10 SI prefixes.
|
||||
* @internal
|
||||
*/
|
||||
UMEASURE_PREFIX_INTERNAL_MIN_SI = UMEASURE_PREFIX_YOCTO,
|
||||
|
||||
/**
|
||||
* ICU use only.
|
||||
* Sets the arbitrary offset of the base-1024 binary prefixes' enum values.
|
||||
* @internal
|
||||
*/
|
||||
UMEASURE_PREFIX_INTERNAL_ONE_BIN = -60,
|
||||
|
||||
/**
|
||||
* Binary prefix: kibi, 1024^1.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_KIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 1,
|
||||
|
||||
/**
|
||||
* ICU use only.
|
||||
* Used to determine the set of base-1024 binary prefixes.
|
||||
* @internal
|
||||
*/
|
||||
UMEASURE_PREFIX_INTERNAL_MIN_BIN = UMEASURE_PREFIX_KIBI,
|
||||
|
||||
/**
|
||||
* Binary prefix: mebi, 1024^2.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_MEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 2,
|
||||
|
||||
/**
|
||||
* Binary prefix: gibi, 1024^3.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_GIBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 3,
|
||||
|
||||
/**
|
||||
* Binary prefix: tebi, 1024^4.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_TEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 4,
|
||||
|
||||
/**
|
||||
* Binary prefix: pebi, 1024^5.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_PEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 5,
|
||||
|
||||
/**
|
||||
* Binary prefix: exbi, 1024^6.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_EXBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 6,
|
||||
|
||||
/**
|
||||
* Binary prefix: zebi, 1024^7.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_ZEBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 7,
|
||||
|
||||
/**
|
||||
* Binary prefix: yobi, 1024^8.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMEASURE_PREFIX_YOBI = UMEASURE_PREFIX_INTERNAL_ONE_BIN + 8,
|
||||
|
||||
/**
|
||||
* ICU use only.
|
||||
* Used to determine the set of base-1024 binary prefixes.
|
||||
* @internal
|
||||
*/
|
||||
UMEASURE_PREFIX_INTERNAL_MAX_BIN = UMEASURE_PREFIX_YOBI,
|
||||
} UMeasurePrefix;
|
||||
|
||||
/**
|
||||
* Returns the base of the factor associated with the given unit prefix: the
|
||||
* base is 10 for SI prefixes (kilo, micro) and 1024 for binary prefixes (kibi,
|
||||
* mebi).
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
U_CAPI int32_t U_EXPORT2 umeas_getPrefixBase(UMeasurePrefix unitPrefix);
|
||||
|
||||
/**
|
||||
* Returns the exponent of the factor associated with the given unit prefix, for
|
||||
* example 3 for kilo, -6 for micro, 1 for kibi, 2 for mebi, 3 for gibi.
|
||||
*
|
||||
* @draft ICU 69
|
||||
*/
|
||||
U_CAPI int32_t U_EXPORT2 umeas_getPrefixPower(UMeasurePrefix unitPrefix);
|
||||
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
/**
|
||||
|
@ -352,33 +469,36 @@ class U_I18N_API MeasureUnit: public UObject {
|
|||
UMeasureUnitComplexity getComplexity(UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Creates a MeasureUnit which is this SINGLE unit augmented with the specified SI prefix.
|
||||
* For example, UMEASURE_SI_PREFIX_KILO for "kilo".
|
||||
* Creates a MeasureUnit which is this SINGLE unit augmented with the specified prefix.
|
||||
* For example, UMEASURE_PREFIX_KILO for "kilo", or UMEASURE_PREFIX_KIBI for "kibi".
|
||||
*
|
||||
* There is sufficient locale data to format all standard SI prefixes.
|
||||
* There is sufficient locale data to format all standard prefixes.
|
||||
*
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see UMeasureUnitComplexity.
|
||||
*
|
||||
* @param prefix The SI prefix, from UMeasureSIPrefix.
|
||||
* @param prefix The prefix, from UMeasurePrefix.
|
||||
* @param status Set if this is not a SINGLE unit or if another error occurs.
|
||||
* @return A new SINGLE unit.
|
||||
* @draft ICU 67
|
||||
* @draft ICU 69
|
||||
*/
|
||||
MeasureUnit withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& status) const;
|
||||
MeasureUnit withPrefix(UMeasurePrefix prefix, UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Gets the current SI prefix of this SINGLE unit. For example, if the unit has the SI prefix
|
||||
* "kilo", then UMEASURE_SI_PREFIX_KILO is returned.
|
||||
* Returns the current SI or binary prefix of this SINGLE unit. For example,
|
||||
* if the unit has the prefix "kilo", then UMEASURE_PREFIX_KILO is
|
||||
* returned.
|
||||
*
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see UMeasureUnitComplexity.
|
||||
*
|
||||
* @param status Set if this is not a SINGLE unit or if another error occurs.
|
||||
* @return The SI prefix of this SINGLE unit, from UMeasureSIPrefix.
|
||||
* @draft ICU 67
|
||||
* @return The prefix of this SINGLE unit, from UMeasurePrefix.
|
||||
* @see umeas_getPrefixBase
|
||||
* @see umeas_getPrefixPower
|
||||
* @draft ICU 69
|
||||
*/
|
||||
UMeasureSIPrefix getSIPrefix(UErrorCode& status) const;
|
||||
UMeasurePrefix getPrefix(UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Creates a MeasureUnit which is this SINGLE unit augmented with the specified dimensionality
|
||||
|
|
|
@ -66,17 +66,19 @@ void U_I18N_API Factor::power(int32_t power) {
|
|||
}
|
||||
}
|
||||
|
||||
void U_I18N_API Factor::applySiPrefix(UMeasureSIPrefix siPrefix) {
|
||||
if (siPrefix == UMeasureSIPrefix::UMEASURE_SI_PREFIX_ONE) return; // No need to do anything
|
||||
|
||||
double siApplied = std::pow(10.0, std::abs(siPrefix));
|
||||
|
||||
if (siPrefix < 0) {
|
||||
factorDen *= siApplied;
|
||||
void U_I18N_API Factor::applyPrefix(UMeasurePrefix unitPrefix) {
|
||||
if (unitPrefix == UMeasurePrefix::UMEASURE_PREFIX_ONE) {
|
||||
// No need to do anything
|
||||
return;
|
||||
}
|
||||
|
||||
factorNum *= siApplied;
|
||||
int32_t prefixPower = umeas_getPrefixPower(unitPrefix);
|
||||
double prefixFactor = std::pow((double)umeas_getPrefixBase(unitPrefix), (double)std::abs(prefixPower));
|
||||
if (prefixPower >= 0) {
|
||||
factorNum *= prefixFactor;
|
||||
} else {
|
||||
factorDen *= prefixFactor;
|
||||
}
|
||||
}
|
||||
|
||||
void U_I18N_API Factor::substituteConstants() {
|
||||
|
@ -213,6 +215,7 @@ Factor loadSingleFactor(StringPiece source, const ConversionRates &ratesInfo, UE
|
|||
}
|
||||
|
||||
// Load Factor of a compound source unit.
|
||||
// In ICU4J, this is a pair of ConversionRates.getFactorToBase() functions.
|
||||
Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status) {
|
||||
|
||||
|
@ -223,8 +226,10 @@ Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &
|
|||
Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status);
|
||||
if (U_FAILURE(status)) return result;
|
||||
|
||||
// Apply SiPrefix before the power, because the power may be will flip the factor.
|
||||
singleFactor.applySiPrefix(singleUnit.siPrefix);
|
||||
// Prefix before power, because:
|
||||
// - square-kilometer to square-meter: (1000)^2
|
||||
// - square-kilometer to square-foot (approximate): (3.28*1000)^2
|
||||
singleFactor.applyPrefix(singleUnit.unitPrefix);
|
||||
|
||||
// Apply the power of the `dimensionality`
|
||||
singleFactor.power(singleUnit.dimensionality);
|
||||
|
@ -241,6 +246,8 @@ Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &
|
|||
*
|
||||
* NOTE:
|
||||
* Empty unit means simple unit.
|
||||
*
|
||||
* In ICU4J, this is ConversionRates.checkSimpleUnit().
|
||||
*/
|
||||
UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) return false;
|
||||
|
@ -255,7 +262,7 @@ UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
|
|||
|
||||
auto singleUnit = *(unit.singleUnits[0]);
|
||||
|
||||
if (singleUnit.dimensionality != 1 || singleUnit.siPrefix != UMEASURE_SI_PREFIX_ONE) {
|
||||
if (singleUnit.dimensionality != 1 || singleUnit.unitPrefix != UMEASURE_PREFIX_ONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -293,6 +300,7 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &s
|
|||
conversionRate.factorNum = finalFactor.factorNum;
|
||||
conversionRate.factorDen = finalFactor.factorDen;
|
||||
|
||||
// This code corresponds to ICU4J's ConversionRates.getOffset().
|
||||
// In case of simple units (such as: celsius or fahrenheit), offsets are considered.
|
||||
if (checkSimpleUnit(source, status) && checkSimpleUnit(target, status)) {
|
||||
conversionRate.sourceOffset =
|
||||
|
@ -300,6 +308,8 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &s
|
|||
conversionRate.targetOffset =
|
||||
targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum;
|
||||
}
|
||||
// TODO(icu-units#127): should we consider failure if there's an offset for
|
||||
// a not-simple-unit? What about kilokelvin / kilocelsius?
|
||||
|
||||
conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ struct U_I18N_API Factor {
|
|||
// Apply the power to the factor.
|
||||
void power(int32_t power);
|
||||
|
||||
// Apply SI prefix to the `Factor`
|
||||
void applySiPrefix(UMeasureSIPrefix siPrefix);
|
||||
// Apply SI or binary prefix to the Factor.
|
||||
void applyPrefix(UMeasurePrefix unitPrefix);
|
||||
|
||||
// Does an in-place substition of the "symbolic constants" based on
|
||||
// constantExponents (resetting the exponents).
|
||||
|
|
|
@ -83,6 +83,8 @@ private:
|
|||
void TestNumericTimeSomeSpecialFormats();
|
||||
void TestIdentifiers();
|
||||
void TestInvalidIdentifiers();
|
||||
void TestIdentifierDetails();
|
||||
void TestPrefixes();
|
||||
void TestParseToBuiltIn();
|
||||
void TestKilogramIdentifier();
|
||||
void TestCompoundUnitOperations();
|
||||
|
@ -152,7 +154,7 @@ private:
|
|||
int32_t end);
|
||||
void verifySingleUnit(
|
||||
const MeasureUnit& unit,
|
||||
UMeasureSIPrefix siPrefix,
|
||||
UMeasurePrefix unitPrefix,
|
||||
int8_t power,
|
||||
const char* identifier);
|
||||
void verifyCompoundUnit(
|
||||
|
@ -212,6 +214,8 @@ void MeasureFormatTest::runIndexedTest(
|
|||
TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
|
||||
TESTCASE_AUTO(TestIdentifiers);
|
||||
TESTCASE_AUTO(TestInvalidIdentifiers);
|
||||
TESTCASE_AUTO(TestIdentifierDetails);
|
||||
TESTCASE_AUTO(TestPrefixes);
|
||||
TESTCASE_AUTO(TestParseToBuiltIn);
|
||||
TESTCASE_AUTO(TestKilogramIdentifier);
|
||||
TESTCASE_AUTO(TestCompoundUnitOperations);
|
||||
|
@ -3664,6 +3668,45 @@ void MeasureFormatTest::TestIdentifiers() {
|
|||
{"kilogram-per-meter-per-second", "kilogram-per-meter-second"},
|
||||
|
||||
// TODO(ICU-21284): Add more test cases once the proper ranking is available.
|
||||
|
||||
// Testing prefixes are parsed and produced correctly (ensures no
|
||||
// collisions in the enum values)
|
||||
{"yoctofoot", "yoctofoot"},
|
||||
{"zeptofoot", "zeptofoot"},
|
||||
{"attofoot", "attofoot"},
|
||||
{"femtofoot", "femtofoot"},
|
||||
{"picofoot", "picofoot"},
|
||||
{"nanofoot", "nanofoot"},
|
||||
{"microfoot", "microfoot"},
|
||||
{"millifoot", "millifoot"},
|
||||
{"centifoot", "centifoot"},
|
||||
{"decifoot", "decifoot"},
|
||||
{"foot", "foot"},
|
||||
{"dekafoot", "dekafoot"},
|
||||
{"hectofoot", "hectofoot"},
|
||||
{"kilofoot", "kilofoot"},
|
||||
{"megafoot", "megafoot"},
|
||||
{"gigafoot", "gigafoot"},
|
||||
{"terafoot", "terafoot"},
|
||||
{"petafoot", "petafoot"},
|
||||
{"exafoot", "exafoot"},
|
||||
{"zettafoot", "zettafoot"},
|
||||
{"yottafoot", "yottafoot"},
|
||||
{"kibibyte", "kibibyte"},
|
||||
{"mebibyte", "mebibyte"},
|
||||
{"gibibyte", "gibibyte"},
|
||||
{"tebibyte", "tebibyte"},
|
||||
{"pebibyte", "pebibyte"},
|
||||
{"exbibyte", "exbibyte"},
|
||||
{"zebibyte", "zebibyte"},
|
||||
{"yobibyte", "yobibyte"},
|
||||
|
||||
// Testing sort order of prefixes.
|
||||
//
|
||||
// TODO(icu-units#70): revisit when fixing normalization. For now we're
|
||||
// just checking some consistency between C&J.
|
||||
{"megafoot-mebifoot-kibifoot-kilofoot", "kibifoot-mebifoot-kilofoot-megafoot"},
|
||||
|
||||
};
|
||||
for (const auto &cas : cases) {
|
||||
status.setScope(cas.id);
|
||||
|
@ -3722,6 +3765,77 @@ void MeasureFormatTest::TestInvalidIdentifiers() {
|
|||
}
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestIdentifierDetails() {
|
||||
IcuTestErrorCode status(*this, "TestIdentifierDetails()");
|
||||
|
||||
MeasureUnit joule = MeasureUnit::forIdentifier("joule", status);
|
||||
status.assertSuccess();
|
||||
assertEquals("Initial joule", "joule", joule.getIdentifier());
|
||||
|
||||
static_assert(UMEASURE_PREFIX_INTERNAL_MAX_SI < 99, "Tests assume there is no prefix 99.");
|
||||
static_assert(UMEASURE_PREFIX_INTERNAL_MAX_BIN < 99, "Tests assume there is no prefix 99.");
|
||||
MeasureUnit unit = joule.withPrefix((UMeasurePrefix)99, status);
|
||||
if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
|
||||
errln("Invalid prefix should result in an error.");
|
||||
}
|
||||
assertEquals("Invalid prefix results in no identifier", "", unit.getIdentifier());
|
||||
|
||||
unit = joule.withPrefix(UMEASURE_PREFIX_HECTO, status);
|
||||
status.assertSuccess();
|
||||
assertEquals("foo identifier", "hectojoule", unit.getIdentifier());
|
||||
|
||||
unit = unit.withPrefix(UMEASURE_PREFIX_EXBI, status);
|
||||
status.assertSuccess();
|
||||
assertEquals("foo identifier", "exbijoule", unit.getIdentifier());
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestPrefixes() {
|
||||
IcuTestErrorCode status(*this, "TestPrefixes()");
|
||||
const struct TestCase {
|
||||
UMeasurePrefix prefix;
|
||||
int32_t expectedBase;
|
||||
int32_t expectedPower;
|
||||
} cases[] = {
|
||||
{UMEASURE_PREFIX_YOCTO, 10, -24},
|
||||
{UMEASURE_PREFIX_ZEPTO, 10, -21},
|
||||
{UMEASURE_PREFIX_ATTO, 10, -18},
|
||||
{UMEASURE_PREFIX_FEMTO, 10, -15},
|
||||
{UMEASURE_PREFIX_PICO, 10, -12},
|
||||
{UMEASURE_PREFIX_NANO, 10, -9},
|
||||
{UMEASURE_PREFIX_MICRO, 10, -6},
|
||||
{UMEASURE_PREFIX_MILLI, 10, -3},
|
||||
{UMEASURE_PREFIX_CENTI, 10, -2},
|
||||
{UMEASURE_PREFIX_DECI, 10, -1},
|
||||
{UMEASURE_PREFIX_ONE, 10, 0},
|
||||
{UMEASURE_PREFIX_DEKA, 10, 1},
|
||||
{UMEASURE_PREFIX_HECTO, 10, 2},
|
||||
{UMEASURE_PREFIX_KILO, 10, 3},
|
||||
{UMEASURE_PREFIX_MEGA, 10, 6},
|
||||
{UMEASURE_PREFIX_GIGA, 10, 9},
|
||||
{UMEASURE_PREFIX_TERA, 10, 12},
|
||||
{UMEASURE_PREFIX_PETA, 10, 15},
|
||||
{UMEASURE_PREFIX_EXA, 10, 18},
|
||||
{UMEASURE_PREFIX_ZETTA, 10, 21},
|
||||
{UMEASURE_PREFIX_YOTTA, 10, 24},
|
||||
{UMEASURE_PREFIX_KIBI, 1024, 1},
|
||||
{UMEASURE_PREFIX_MEBI, 1024, 2},
|
||||
{UMEASURE_PREFIX_GIBI, 1024, 3},
|
||||
{UMEASURE_PREFIX_TEBI, 1024, 4},
|
||||
{UMEASURE_PREFIX_PEBI, 1024, 5},
|
||||
{UMEASURE_PREFIX_EXBI, 1024, 6},
|
||||
{UMEASURE_PREFIX_ZEBI, 1024, 7},
|
||||
{UMEASURE_PREFIX_YOBI, 1024, 8},
|
||||
};
|
||||
|
||||
for (auto cas : cases) {
|
||||
MeasureUnit m = MeasureUnit::getAmpere().withPrefix(cas.prefix, status);
|
||||
assertEquals("umeas_getPrefixPower()", cas.expectedPower,
|
||||
umeas_getPrefixPower(m.getPrefix(status)));
|
||||
assertEquals("umeas_getPrefixBase()", cas.expectedBase,
|
||||
umeas_getPrefixBase(m.getPrefix(status)));
|
||||
}
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestParseToBuiltIn() {
|
||||
IcuTestErrorCode status(*this, "TestParseToBuiltIn()");
|
||||
const struct TestCase {
|
||||
|
@ -3778,12 +3892,12 @@ void MeasureFormatTest::TestKilogramIdentifier() {
|
|||
assertEquals("nanogram", "", nanogram.getType());
|
||||
assertEquals("nanogram", "nanogram", nanogram.getIdentifier());
|
||||
|
||||
assertEquals("prefix of kilogram", UMEASURE_SI_PREFIX_KILO, kilogram.getSIPrefix(status));
|
||||
assertEquals("prefix of gram", UMEASURE_SI_PREFIX_ONE, gram.getSIPrefix(status));
|
||||
assertEquals("prefix of microgram", UMEASURE_SI_PREFIX_MICRO, microgram.getSIPrefix(status));
|
||||
assertEquals("prefix of nanogram", UMEASURE_SI_PREFIX_NANO, nanogram.getSIPrefix(status));
|
||||
assertEquals("prefix of kilogram", UMEASURE_PREFIX_KILO, kilogram.getPrefix(status));
|
||||
assertEquals("prefix of gram", UMEASURE_PREFIX_ONE, gram.getPrefix(status));
|
||||
assertEquals("prefix of microgram", UMEASURE_PREFIX_MICRO, microgram.getPrefix(status));
|
||||
assertEquals("prefix of nanogram", UMEASURE_PREFIX_NANO, nanogram.getPrefix(status));
|
||||
|
||||
MeasureUnit tmp = kilogram.withSIPrefix(UMEASURE_SI_PREFIX_MILLI, status);
|
||||
MeasureUnit tmp = kilogram.withPrefix(UMEASURE_PREFIX_MILLI, status);
|
||||
assertEquals(UnicodeString("Kilogram + milli should be milligram, got: ") + tmp.getIdentifier(),
|
||||
MeasureUnit::getMilligram().getIdentifier(), tmp.getIdentifier());
|
||||
}
|
||||
|
@ -3795,16 +3909,16 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
|||
|
||||
MeasureUnit kilometer = MeasureUnit::getKilometer();
|
||||
MeasureUnit cubicMeter = MeasureUnit::getCubicMeter();
|
||||
MeasureUnit meter = kilometer.withSIPrefix(UMEASURE_SI_PREFIX_ONE, status);
|
||||
MeasureUnit centimeter1 = kilometer.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
|
||||
MeasureUnit centimeter2 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
|
||||
MeasureUnit cubicDecimeter = cubicMeter.withSIPrefix(UMEASURE_SI_PREFIX_DECI, status);
|
||||
MeasureUnit meter = kilometer.withPrefix(UMEASURE_PREFIX_ONE, status);
|
||||
MeasureUnit centimeter1 = kilometer.withPrefix(UMEASURE_PREFIX_CENTI, status);
|
||||
MeasureUnit centimeter2 = meter.withPrefix(UMEASURE_PREFIX_CENTI, status);
|
||||
MeasureUnit cubicDecimeter = cubicMeter.withPrefix(UMEASURE_PREFIX_DECI, status);
|
||||
|
||||
verifySingleUnit(kilometer, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
|
||||
verifySingleUnit(meter, UMEASURE_SI_PREFIX_ONE, 1, "meter");
|
||||
verifySingleUnit(centimeter1, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
|
||||
verifySingleUnit(centimeter2, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
|
||||
verifySingleUnit(cubicDecimeter, UMEASURE_SI_PREFIX_DECI, 3, "cubic-decimeter");
|
||||
verifySingleUnit(kilometer, UMEASURE_PREFIX_KILO, 1, "kilometer");
|
||||
verifySingleUnit(meter, UMEASURE_PREFIX_ONE, 1, "meter");
|
||||
verifySingleUnit(centimeter1, UMEASURE_PREFIX_CENTI, 1, "centimeter");
|
||||
verifySingleUnit(centimeter2, UMEASURE_PREFIX_CENTI, 1, "centimeter");
|
||||
verifySingleUnit(cubicDecimeter, UMEASURE_PREFIX_DECI, 3, "cubic-decimeter");
|
||||
|
||||
assertTrue("centimeter equality", centimeter1 == centimeter2);
|
||||
assertTrue("kilometer inequality", centimeter1 != kilometer);
|
||||
|
@ -3814,10 +3928,10 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
|||
MeasureUnit quarticKilometer = kilometer.withDimensionality(4, status);
|
||||
MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4, status);
|
||||
|
||||
verifySingleUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
|
||||
verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "per-cubic-centimeter");
|
||||
verifySingleUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(squareMeter, UMEASURE_PREFIX_ONE, 2, "square-meter");
|
||||
verifySingleUnit(overCubicCentimeter, UMEASURE_PREFIX_CENTI, -3, "per-cubic-centimeter");
|
||||
verifySingleUnit(quarticKilometer, UMEASURE_PREFIX_KILO, 4, "pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
|
||||
assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
|
||||
|
||||
|
@ -3828,26 +3942,26 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
|||
.reciprocal(status);
|
||||
MeasureUnit overQuarticKilometer4 = meter.withDimensionality(4, status)
|
||||
.reciprocal(status)
|
||||
.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
.withPrefix(UMEASURE_PREFIX_KILO, status);
|
||||
|
||||
verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer2, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, UMEASURE_PREFIX_KILO, -4, "per-pow4-kilometer");
|
||||
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer4);
|
||||
|
||||
MeasureUnit kiloSquareSecond = MeasureUnit::getSecond()
|
||||
.withDimensionality(2, status).withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
.withDimensionality(2, status).withPrefix(UMEASURE_PREFIX_KILO, status);
|
||||
MeasureUnit meterSecond = meter.product(kiloSquareSecond, status);
|
||||
MeasureUnit cubicMeterSecond1 = meter.withDimensionality(3, status).product(kiloSquareSecond, status);
|
||||
MeasureUnit centimeterSecond1 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status).product(kiloSquareSecond, status);
|
||||
MeasureUnit centimeterSecond1 = meter.withPrefix(UMEASURE_PREFIX_CENTI, status).product(kiloSquareSecond, status);
|
||||
MeasureUnit secondCubicMeter = kiloSquareSecond.product(meter.withDimensionality(3, status), status);
|
||||
MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status), status);
|
||||
MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withPrefix(UMEASURE_PREFIX_CENTI, status), status);
|
||||
MeasureUnit secondCentimeterPerKilometer = secondCentimeter.product(kilometer.reciprocal(status), status);
|
||||
|
||||
verifySingleUnit(kiloSquareSecond, UMEASURE_SI_PREFIX_KILO, 2, "square-kilosecond");
|
||||
verifySingleUnit(kiloSquareSecond, UMEASURE_PREFIX_KILO, 2, "square-kilosecond");
|
||||
const char* meterSecondSub[] = {"meter", "square-kilosecond"};
|
||||
verifyCompoundUnit(meterSecond, "meter-square-kilosecond",
|
||||
meterSecondSub, UPRV_LENGTHOF(meterSecondSub));
|
||||
|
@ -3870,20 +3984,20 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
|||
assertTrue("reordering equality", cubicMeterSecond1 == secondCubicMeter);
|
||||
assertTrue("additional simple units inequality", secondCubicMeter != secondCentimeter);
|
||||
|
||||
// Don't allow get/set power or SI prefix on compound units
|
||||
// Don't allow get/set power or SI or binary prefix on compound units
|
||||
status.errIfFailureAndReset();
|
||||
meterSecond.getDimensionality(status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
meterSecond.withDimensionality(3, status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
meterSecond.getSIPrefix(status);
|
||||
meterSecond.getPrefix(status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
meterSecond.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
|
||||
meterSecond.withPrefix(UMEASURE_PREFIX_CENTI, status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
|
||||
// Test that StringPiece does not overflow
|
||||
MeasureUnit centimeter3 = MeasureUnit::forIdentifier({secondCentimeter.getIdentifier(), 10}, status);
|
||||
verifySingleUnit(centimeter3, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
|
||||
verifySingleUnit(centimeter3, UMEASURE_PREFIX_CENTI, 1, "centimeter");
|
||||
assertTrue("string piece equality", centimeter1 == centimeter3);
|
||||
|
||||
MeasureUnit footInch = MeasureUnit::forIdentifier("foot-and-inch", status);
|
||||
|
@ -3907,19 +4021,19 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
|||
// with others via product:
|
||||
MeasureUnit kilometer2 = dimensionless.product(kilometer, status);
|
||||
status.errIfFailureAndReset("dimensionless.product(kilometer, status)");
|
||||
verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
|
||||
verifySingleUnit(kilometer2, UMEASURE_PREFIX_KILO, 1, "kilometer");
|
||||
assertTrue("kilometer equality", kilometer == kilometer2);
|
||||
|
||||
// Test out-of-range powers
|
||||
MeasureUnit power15 = MeasureUnit::forIdentifier("pow15-kilometer", status);
|
||||
verifySingleUnit(power15, UMEASURE_SI_PREFIX_KILO, 15, "pow15-kilometer");
|
||||
verifySingleUnit(power15, UMEASURE_PREFIX_KILO, 15, "pow15-kilometer");
|
||||
status.errIfFailureAndReset();
|
||||
MeasureUnit power16a = MeasureUnit::forIdentifier("pow16-kilometer", status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
MeasureUnit power16b = power15.product(kilometer, status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
MeasureUnit powerN15 = MeasureUnit::forIdentifier("per-pow15-kilometer", status);
|
||||
verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "per-pow15-kilometer");
|
||||
verifySingleUnit(powerN15, UMEASURE_PREFIX_KILO, -15, "per-pow15-kilometer");
|
||||
status.errIfFailureAndReset();
|
||||
MeasureUnit powerN16a = MeasureUnit::forIdentifier("per-pow16-kilometer", status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
|
@ -3945,22 +4059,22 @@ void MeasureFormatTest::TestDimensionlessBehaviour() {
|
|||
MeasureUnit mile = MeasureUnit::getMile();
|
||||
mile = mile.product(dimensionless, status);
|
||||
status.errIfFailureAndReset("mile.product(dimensionless, ...)");
|
||||
verifySingleUnit(mile, UMEASURE_SI_PREFIX_ONE, 1, "mile");
|
||||
verifySingleUnit(mile, UMEASURE_PREFIX_ONE, 1, "mile");
|
||||
|
||||
// dimensionless.getSIPrefix()
|
||||
UMeasureSIPrefix siPrefix = dimensionless.getSIPrefix(status);
|
||||
status.errIfFailureAndReset("dimensionless.getSIPrefix(...)");
|
||||
assertEquals("dimensionless SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
|
||||
// dimensionless.getPrefix()
|
||||
UMeasurePrefix unitPrefix = dimensionless.getPrefix(status);
|
||||
status.errIfFailureAndReset("dimensionless.getPrefix(...)");
|
||||
assertEquals("dimensionless SIPrefix", UMEASURE_PREFIX_ONE, unitPrefix);
|
||||
|
||||
// dimensionless.withSIPrefix()
|
||||
modified = dimensionless.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
status.errIfFailureAndReset("dimensionless.withSIPrefix(...)");
|
||||
// dimensionless.withPrefix()
|
||||
modified = dimensionless.withPrefix(UMEASURE_PREFIX_KILO, status);
|
||||
status.errIfFailureAndReset("dimensionless.withPrefix(...)");
|
||||
pair = dimensionless.splitToSingleUnits(status);
|
||||
count = pair.second;
|
||||
assertEquals("no singles in modified", 0, count);
|
||||
siPrefix = modified.getSIPrefix(status);
|
||||
status.errIfFailureAndReset("modified.getSIPrefix(...)");
|
||||
assertEquals("modified SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
|
||||
unitPrefix = modified.getPrefix(status);
|
||||
status.errIfFailureAndReset("modified.getPrefix(...)");
|
||||
assertEquals("modified SIPrefix", UMEASURE_PREFIX_ONE, unitPrefix);
|
||||
|
||||
// dimensionless.getComplexity()
|
||||
UMeasureUnitComplexity complexity = dimensionless.getComplexity(status);
|
||||
|
@ -4192,15 +4306,15 @@ void MeasureFormatTest::verifyFormat(
|
|||
|
||||
void MeasureFormatTest::verifySingleUnit(
|
||||
const MeasureUnit& unit,
|
||||
UMeasureSIPrefix siPrefix,
|
||||
UMeasurePrefix unitPrefix,
|
||||
int8_t power,
|
||||
const char* identifier) {
|
||||
IcuTestErrorCode status(*this, "verifySingleUnit");
|
||||
UnicodeString uid(identifier, -1, US_INV);
|
||||
assertEquals(uid + ": SI prefix",
|
||||
siPrefix,
|
||||
unit.getSIPrefix(status));
|
||||
status.errIfFailureAndReset("%s: SI prefix", identifier);
|
||||
assertEquals(uid + ": SI or binary prefix",
|
||||
unitPrefix,
|
||||
unit.getPrefix(status));
|
||||
status.errIfFailureAndReset("%s: SI or binary prefix", identifier);
|
||||
assertEquals(uid + ": Power",
|
||||
static_cast<int32_t>(power),
|
||||
static_cast<int32_t>(unit.getDimensionality(status)));
|
||||
|
|
|
@ -1102,10 +1102,9 @@ void NumberFormatterApiTest::unitSkeletons() {
|
|||
u"measure-unit/concentr-permille", //
|
||||
u"permille"},
|
||||
|
||||
// // TODO: binary prefixes not supported yet!
|
||||
// {"Round-trip example from icu-units#35", //
|
||||
// u"unit/kibijoule-per-furlong", //
|
||||
// u"unit/kibijoule-per-furlong"},
|
||||
{"Round-trip example from icu-units#35", //
|
||||
u"unit/kibijoule-per-furlong", //
|
||||
u"unit/kibijoule-per-furlong"},
|
||||
};
|
||||
for (auto &cas : cases) {
|
||||
IcuTestErrorCode status(*this, cas.msg);
|
||||
|
|
|
@ -192,6 +192,14 @@ void UnitsTest::testConverter() {
|
|||
{"gigabyte", "byte", 1.0, 1000000000},
|
||||
{"megawatt", "watt", 1.0, 1000000},
|
||||
{"megawatt", "kilowatt", 1.0, 1000},
|
||||
// Binary Prefixes
|
||||
{"kilobyte", "byte", 1, 1000},
|
||||
{"kibibyte", "byte", 1, 1024},
|
||||
{"mebibyte", "byte", 1, 1048576},
|
||||
{"gibibyte", "kibibyte", 1, 1048576},
|
||||
{"pebibyte", "tebibyte", 4, 4096},
|
||||
{"zebibyte", "pebibyte", 1.0/16, 65536.0},
|
||||
{"yobibyte", "exbibyte", 1, 1048576},
|
||||
// Mass
|
||||
{"gram", "kilogram", 1.0, 0.001},
|
||||
{"pound", "kilogram", 1.0, 0.453592},
|
||||
|
|
|
@ -37,12 +37,16 @@ public class ConversionRates {
|
|||
* @param singleUnit
|
||||
* @return
|
||||
*/
|
||||
// In ICU4C, this is called loadCompoundFactor().
|
||||
private UnitConverter.Factor getFactorToBase(SingleUnitImpl singleUnit) {
|
||||
int power = singleUnit.getDimensionality();
|
||||
MeasureUnit.SIPrefix siPrefix = singleUnit.getSiPrefix();
|
||||
MeasureUnit.MeasurePrefix unitPrefix = singleUnit.getPrefix();
|
||||
UnitConverter.Factor result = UnitConverter.Factor.processFactor(mapToConversionRate.get(singleUnit.getSimpleUnitID()).getConversionRate());
|
||||
|
||||
return result.applySiPrefix(siPrefix).power(power); // NOTE: you must apply the SI prefixes before the power.
|
||||
// Prefix before power, because:
|
||||
// - square-kilometer to square-meter: (1000)^2
|
||||
// - square-kilometer to square-foot (approximate): (3.28*1000)^2
|
||||
return result.applyPrefix(unitPrefix).power(power);
|
||||
}
|
||||
|
||||
public UnitConverter.Factor getFactorToBase(MeasureUnitImpl measureUnit) {
|
||||
|
@ -55,6 +59,7 @@ public class ConversionRates {
|
|||
return result;
|
||||
}
|
||||
|
||||
// In ICU4C, this functionality is found in loadConversionRate().
|
||||
protected BigDecimal getOffset(MeasureUnitImpl source, MeasureUnitImpl target, UnitConverter.Factor
|
||||
sourceToBase, UnitConverter.Factor targetToBase, UnitConverter.Convertibility convertibility) {
|
||||
if (convertibility != UnitConverter.Convertibility.CONVERTIBLE) return BigDecimal.valueOf(0);
|
||||
|
@ -124,7 +129,7 @@ public class ConversionRates {
|
|||
if (measureUnitImpl.getComplexity() != MeasureUnit.Complexity.SINGLE) return false;
|
||||
SingleUnitImpl singleUnit = measureUnitImpl.getSingleUnits().get(0);
|
||||
|
||||
if (singleUnit.getSiPrefix() != MeasureUnit.SIPrefix.ONE) return false;
|
||||
if (singleUnit.getPrefix() != MeasureUnit.MeasurePrefix.ONE) return false;
|
||||
if (singleUnit.getDimensionality() != 1) return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -388,6 +388,11 @@ public class MeasureUnitImpl {
|
|||
// * unit", sawAnd is set to true. If not, it is left as is.
|
||||
private boolean fSawAnd = false;
|
||||
|
||||
// Cache the MeasurePrefix values array to make getPrefixFromTrieIndex()
|
||||
// more efficient
|
||||
private static MeasureUnit.MeasurePrefix[] measurePrefixValues =
|
||||
MeasureUnit.MeasurePrefix.values();
|
||||
|
||||
private UnitsParser(String identifier) {
|
||||
this.fSource = identifier;
|
||||
|
||||
|
@ -403,6 +408,11 @@ public class MeasureUnitImpl {
|
|||
CharsTrieBuilder trieBuilder;
|
||||
trieBuilder = new CharsTrieBuilder();
|
||||
|
||||
// Add SI and binary prefixes
|
||||
for (MeasureUnit.MeasurePrefix unitPrefix : measurePrefixValues) {
|
||||
trieBuilder.add(unitPrefix.getIdentifier(), getTrieIndexForPrefix(unitPrefix));
|
||||
}
|
||||
|
||||
// Add syntax parts (compound, power prefixes)
|
||||
trieBuilder.add("-per-", CompoundPart.PER.getTrieIndex());
|
||||
trieBuilder.add("-", CompoundPart.TIMES.getTrieIndex());
|
||||
|
@ -425,12 +435,6 @@ public class MeasureUnitImpl {
|
|||
trieBuilder.add("pow14-", PowerPart.P14.getTrieIndex());
|
||||
trieBuilder.add("pow15-", PowerPart.P15.getTrieIndex());
|
||||
|
||||
// Add SI prefixes
|
||||
for (MeasureUnit.SIPrefix siPrefix :
|
||||
MeasureUnit.SIPrefix.values()) {
|
||||
trieBuilder.add(siPrefix.getIdentifier(), getTrieIndex(siPrefix));
|
||||
}
|
||||
|
||||
// Add simple units
|
||||
String[] simpleUnits = UnitsData.getSimpleUnits();
|
||||
for (int i = 0; i < simpleUnits.length; i++) {
|
||||
|
@ -459,18 +463,12 @@ public class MeasureUnitImpl {
|
|||
|
||||
}
|
||||
|
||||
private static MeasureUnit.SIPrefix getSiPrefixFromTrieIndex(int trieIndex) {
|
||||
for (MeasureUnit.SIPrefix element :
|
||||
MeasureUnit.SIPrefix.values()) {
|
||||
if (getTrieIndex(element) == trieIndex)
|
||||
return element;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Incorrect trieIndex");
|
||||
private static MeasureUnit.MeasurePrefix getPrefixFromTrieIndex(int trieIndex) {
|
||||
return measurePrefixValues[trieIndex - UnitsData.Constants.kPrefixOffset];
|
||||
}
|
||||
|
||||
private static int getTrieIndex(MeasureUnit.SIPrefix prefix) {
|
||||
return prefix.getPower() + UnitsData.Constants.kSIPrefixOffset;
|
||||
private static int getTrieIndexForPrefix(MeasureUnit.MeasurePrefix prefix) {
|
||||
return prefix.ordinal() + UnitsData.Constants.kPrefixOffset;
|
||||
}
|
||||
|
||||
private MeasureUnitImpl parse() {
|
||||
|
@ -524,9 +522,9 @@ public class MeasureUnitImpl {
|
|||
SingleUnitImpl result = new SingleUnitImpl();
|
||||
|
||||
// state:
|
||||
// 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
|
||||
// 0 = no tokens seen yet (will accept power, SI or binary prefix, or simple unit)
|
||||
// 1 = power token seen (will not accept another power token)
|
||||
// 2 = SI prefix token seen (will not accept a power or SI prefix token)
|
||||
// 2 = SI or binary prefix token seen (will not accept a power, or SI or binary prefix token)
|
||||
int state = 0;
|
||||
|
||||
boolean atStart = fIndex == 0;
|
||||
|
@ -591,12 +589,12 @@ public class MeasureUnitImpl {
|
|||
state = 1;
|
||||
break;
|
||||
|
||||
case TYPE_SI_PREFIX:
|
||||
case TYPE_PREFIX:
|
||||
if (state > 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
result.setSiPrefix(token.getSIPrefix());
|
||||
result.setPrefix(token.getPrefix());
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
|
@ -674,9 +672,9 @@ public class MeasureUnitImpl {
|
|||
return this.type;
|
||||
}
|
||||
|
||||
public MeasureUnit.SIPrefix getSIPrefix() {
|
||||
assert this.type == Type.TYPE_SI_PREFIX;
|
||||
return getSiPrefixFromTrieIndex(this.fMatch);
|
||||
public MeasureUnit.MeasurePrefix getPrefix() {
|
||||
assert this.type == Type.TYPE_PREFIX;
|
||||
return getPrefixFromTrieIndex(this.fMatch);
|
||||
}
|
||||
|
||||
// Valid only for tokens with type TYPE_COMPOUND_PART.
|
||||
|
@ -700,6 +698,7 @@ public class MeasureUnitImpl {
|
|||
}
|
||||
|
||||
public int getSimpleUnitIndex() {
|
||||
assert this.type == Type.TYPE_SIMPLE_UNIT;
|
||||
return this.fMatch - UnitsData.Constants.kSimpleUnitOffset;
|
||||
}
|
||||
|
||||
|
@ -711,7 +710,7 @@ public class MeasureUnitImpl {
|
|||
}
|
||||
|
||||
if (fMatch < UnitsData.Constants.kCompoundPartOffset) {
|
||||
return Type.TYPE_SI_PREFIX;
|
||||
return Type.TYPE_PREFIX;
|
||||
}
|
||||
if (fMatch < UnitsData.Constants.kInitialCompoundPartOffset) {
|
||||
return Type.TYPE_COMPOUND_PART;
|
||||
|
@ -728,7 +727,7 @@ public class MeasureUnitImpl {
|
|||
|
||||
enum Type {
|
||||
TYPE_UNDEFINED,
|
||||
TYPE_SI_PREFIX,
|
||||
TYPE_PREFIX,
|
||||
// Token type for "-per-", "-", and "-and-".
|
||||
TYPE_COMPOUND_PART,
|
||||
// Token type for "per-".
|
||||
|
|
|
@ -5,6 +5,9 @@ package com.ibm.icu.impl.units;
|
|||
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
|
||||
/**
|
||||
* A class representing a single unit (optional SI or binary prefix, and dimensionality).
|
||||
*/
|
||||
public class SingleUnitImpl {
|
||||
/**
|
||||
* Simple unit index, unique for every simple unit, -1 for the dimensionless
|
||||
|
@ -29,16 +32,16 @@ public class SingleUnitImpl {
|
|||
*/
|
||||
private int dimensionality = 1;
|
||||
/**
|
||||
* SI Prefix
|
||||
* SI or binary prefix.
|
||||
*/
|
||||
private MeasureUnit.SIPrefix siPrefix = MeasureUnit.SIPrefix.ONE;
|
||||
private MeasureUnit.MeasurePrefix unitPrefix = MeasureUnit.MeasurePrefix.ONE;
|
||||
|
||||
public SingleUnitImpl copy() {
|
||||
SingleUnitImpl result = new SingleUnitImpl();
|
||||
result.index = this.index;
|
||||
result.dimensionality = this.dimensionality;
|
||||
result.simpleUnitID = this.simpleUnitID;
|
||||
result.siPrefix = this.siPrefix;
|
||||
result.unitPrefix = this.unitPrefix;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -71,7 +74,7 @@ public class SingleUnitImpl {
|
|||
throw new IllegalArgumentException("Unit Identifier Syntax Error");
|
||||
}
|
||||
|
||||
result.append(this.getSiPrefix().getIdentifier());
|
||||
result.append(this.getPrefix().getIdentifier());
|
||||
result.append(this.getSimpleUnitID());
|
||||
|
||||
return result.toString();
|
||||
|
@ -103,10 +106,18 @@ public class SingleUnitImpl {
|
|||
if (index > other.index) {
|
||||
return 1;
|
||||
}
|
||||
if (this.getSiPrefix().getPower() < other.getSiPrefix().getPower()) {
|
||||
// TODO(icu-units#70): revisit when fixing normalization. For now we're
|
||||
// sorting binary prefixes before SI prefixes, for consistency with ICU4C.
|
||||
if (this.getPrefix().getBase() < other.getPrefix().getBase()) {
|
||||
return 1;
|
||||
}
|
||||
if (this.getPrefix().getBase() > other.getPrefix().getBase()) {
|
||||
return -1;
|
||||
}
|
||||
if (this.getSiPrefix().getPower() > other.getSiPrefix().getPower()) {
|
||||
if (this.getPrefix().getPower() < other.getPrefix().getPower()) {
|
||||
return -1;
|
||||
}
|
||||
if (this.getPrefix().getPower() > other.getPrefix().getPower()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -115,8 +126,8 @@ public class SingleUnitImpl {
|
|||
/**
|
||||
* Checks whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
|
||||
* <p>
|
||||
* Units with the same base unit and SI prefix should match, except that they must also have
|
||||
* the same dimensionality sign, such that we don't merge numerator and denominator.
|
||||
* Units with the same base unit and SI or binary prefix should match, except that they must also
|
||||
* have the same dimensionality sign, such that we don't merge numerator and denominator.
|
||||
*/
|
||||
boolean isCompatibleWith(SingleUnitImpl other) {
|
||||
return (compareTo(other) == 0);
|
||||
|
@ -139,12 +150,12 @@ public class SingleUnitImpl {
|
|||
this.dimensionality = dimensionality;
|
||||
}
|
||||
|
||||
public MeasureUnit.SIPrefix getSiPrefix() {
|
||||
return siPrefix;
|
||||
public MeasureUnit.MeasurePrefix getPrefix() {
|
||||
return unitPrefix;
|
||||
}
|
||||
|
||||
public void setSiPrefix(MeasureUnit.SIPrefix siPrefix) {
|
||||
this.siPrefix = siPrefix;
|
||||
public void setPrefix(MeasureUnit.MeasurePrefix unitPrefix) {
|
||||
this.unitPrefix = unitPrefix;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
|
|
|
@ -211,20 +211,24 @@ public class UnitConverter {
|
|||
}
|
||||
}
|
||||
|
||||
public Factor applySiPrefix(MeasureUnit.SIPrefix siPrefix) {
|
||||
/** Apply SI or binary prefix to the Factor. */
|
||||
public Factor applyPrefix(MeasureUnit.MeasurePrefix unitPrefix) {
|
||||
Factor result = this.copy();
|
||||
if (siPrefix == MeasureUnit.SIPrefix.ONE) {
|
||||
if (unitPrefix == MeasureUnit.MeasurePrefix.ONE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
BigDecimal siApplied = BigDecimal.valueOf(Math.pow(10.0, Math.abs(siPrefix.getPower())));
|
||||
int base = unitPrefix.getBase();
|
||||
int power = unitPrefix.getPower();
|
||||
BigDecimal absFactor =
|
||||
BigDecimal.valueOf(base).pow(Math.abs(power), DECIMAL128);
|
||||
|
||||
if (siPrefix.getPower() < 0) {
|
||||
result.factorDen = this.factorDen.multiply(siApplied);
|
||||
if (power < 0) {
|
||||
result.factorDen = this.factorDen.multiply(absFactor);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.factorNum = this.factorNum.multiply(siApplied);
|
||||
result.factorNum = this.factorNum.multiply(absFactor);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,9 @@ public class UnitsData {
|
|||
* Contains all the needed constants.
|
||||
*/
|
||||
public static class Constants {
|
||||
// TODO: consider moving the Trie-offset-related constants into
|
||||
// MeasureUnitImpl.java, the only place they're being used?
|
||||
|
||||
// Trie value offset for simple units, e.g. "gram", "nautical-mile",
|
||||
// "fluid-ounce-imperial".
|
||||
public static final int kSimpleUnitOffset = 512;
|
||||
|
@ -123,9 +126,9 @@ public class UnitsData {
|
|||
// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
|
||||
public final static int kCompoundPartOffset = 128;
|
||||
|
||||
// Trie value offset for SI Prefixes. This is big enough to ensure we only
|
||||
// insert positive integers into the trie.
|
||||
public static final int kSIPrefixOffset = 64;
|
||||
// Trie value offset for SI or binary prefixes. This is big enough to
|
||||
// ensure we only insert positive integers into the trie.
|
||||
public static final int kPrefixOffset = 64;
|
||||
|
||||
|
||||
/* Tables Names*/
|
||||
|
|
|
@ -35,7 +35,6 @@ import com.ibm.icu.text.UnicodeSet;
|
|||
* A unit such as length, mass, volume, currency, etc. A unit is
|
||||
* coupled with a numeric amount to produce a Measure. MeasureUnit objects are immutable.
|
||||
* All subclasses must guarantee that. (However, subclassing is discouraged.)
|
||||
|
||||
*
|
||||
* @see com.ibm.icu.util.Measure
|
||||
* @author Alan Liu
|
||||
|
@ -76,16 +75,16 @@ public class MeasureUnit implements Serializable {
|
|||
|
||||
/**
|
||||
* Enumeration for unit complexity. There are three levels:
|
||||
* <p>
|
||||
* - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
|
||||
* square-kilometer, kilojoule, one-per-second.
|
||||
* - COMPOUND: A unit composed of the product of multiple single units. Examples:
|
||||
* meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
|
||||
* - MIXED: A unit composed of the sum of multiple single units. Examples: foot-and-inch,
|
||||
* hour-and-minute-and-second, degree-and-arcminute-and-arcsecond.
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>SINGLE: A single unit, optionally with a power and/or SI or binary prefix.
|
||||
* Examples: hectare, square-kilometer, kilojoule, per-second, mebibyte.</li>
|
||||
* <li>COMPOUND: A unit composed of the product of multiple single units. Examples:
|
||||
* meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.</li>
|
||||
* <li>MIXED: A unit composed of the sum of multiple single units. Examples: foot-and-inch,
|
||||
* hour-and-minute-and-second, degree-and-arcminute-and-arcsecond.</li>
|
||||
* </ul>
|
||||
* The complexity determines which operations are available. For example, you cannot set the power
|
||||
* or SI prefix of a compound unit.
|
||||
* or prefix of a compound unit.
|
||||
*
|
||||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
|
@ -117,12 +116,12 @@ public class MeasureUnit implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enumeration for SI prefixes, such as "kilo".
|
||||
* Enumeration for SI and binary prefixes, e.g. "kilo-", "nano-", "mebi-".
|
||||
*
|
||||
* @draft ICU 68
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public enum SIPrefix {
|
||||
public enum MeasurePrefix {
|
||||
|
||||
/**
|
||||
* SI prefix: yotta, 10^24.
|
||||
|
@ -130,7 +129,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
YOTTA(24, "yotta"),
|
||||
YOTTA(24, "yotta", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: zetta, 10^21.
|
||||
|
@ -138,7 +137,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ZETTA(21, "zetta"),
|
||||
ZETTA(21, "zetta", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: exa, 10^18.
|
||||
|
@ -146,7 +145,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
EXA(18, "exa"),
|
||||
EXA(18, "exa", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: peta, 10^15.
|
||||
|
@ -154,7 +153,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
PETA(15, "peta"),
|
||||
PETA(15, "peta", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: tera, 10^12.
|
||||
|
@ -162,7 +161,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
TERA(12, "tera"),
|
||||
TERA(12, "tera", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: giga, 10^9.
|
||||
|
@ -170,7 +169,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
GIGA(9, "giga"),
|
||||
GIGA(9, "giga", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: mega, 10^6.
|
||||
|
@ -178,7 +177,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
MEGA(6, "mega"),
|
||||
MEGA(6, "mega", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: kilo, 10^3.
|
||||
|
@ -186,7 +185,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
KILO(3, "kilo"),
|
||||
KILO(3, "kilo", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: hecto, 10^2.
|
||||
|
@ -194,7 +193,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
HECTO(2, "hecto"),
|
||||
HECTO(2, "hecto", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: deka, 10^1.
|
||||
|
@ -202,7 +201,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
DEKA(1, "deka"),
|
||||
DEKA(1, "deka", 10),
|
||||
|
||||
/**
|
||||
* The absence of an SI prefix.
|
||||
|
@ -210,7 +209,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ONE(0, ""),
|
||||
ONE(0, "", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: deci, 10^-1.
|
||||
|
@ -218,7 +217,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
DECI(-1, "deci"),
|
||||
DECI(-1, "deci", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: centi, 10^-2.
|
||||
|
@ -226,7 +225,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
CENTI(-2, "centi"),
|
||||
CENTI(-2, "centi", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: milli, 10^-3.
|
||||
|
@ -234,7 +233,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
MILLI(-3, "milli"),
|
||||
MILLI(-3, "milli", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: micro, 10^-6.
|
||||
|
@ -242,7 +241,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
MICRO(-6, "micro"),
|
||||
MICRO(-6, "micro", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: nano, 10^-9.
|
||||
|
@ -250,7 +249,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
NANO(-9, "nano"),
|
||||
NANO(-9, "nano", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: pico, 10^-12.
|
||||
|
@ -258,7 +257,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
PICO(-12, "pico"),
|
||||
PICO(-12, "pico", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: femto, 10^-15.
|
||||
|
@ -266,7 +265,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
FEMTO(-15, "femto"),
|
||||
FEMTO(-15, "femto", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: atto, 10^-18.
|
||||
|
@ -274,7 +273,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ATTO(-18, "atto"),
|
||||
ATTO(-18, "atto", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: zepto, 10^-21.
|
||||
|
@ -282,7 +281,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ZEPTO(-21, "zepto"),
|
||||
ZEPTO(-21, "zepto", 10),
|
||||
|
||||
/**
|
||||
* SI prefix: yocto, 10^-24.
|
||||
|
@ -290,12 +289,78 @@ public class MeasureUnit implements Serializable {
|
|||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
YOCTO(-24, "yocto");
|
||||
YOCTO(-24, "yocto", 10),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: kibi, 1024^1.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
KIBI(1, "kibi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: mebi, 1024^2.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
MEBI(2, "mebi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: gibi, 1024^3.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
GIBI(3, "gibi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: tebi, 1024^4.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
TEBI(4, "tebi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: pebi, 1024^5.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
PEBI(5, "pebi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: exbi, 1024^6.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
EXBI(6, "exbi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: zebi, 1024^7.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ZEBI(7, "zebi", 1024),
|
||||
|
||||
/**
|
||||
* IEC binary prefix: yobi, 1024^8.
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
YOBI(8, "yobi", 1024);
|
||||
|
||||
private final int base;
|
||||
private final int power;
|
||||
private final String identifier;
|
||||
|
||||
SIPrefix(int power, String identifier) {
|
||||
MeasurePrefix(int power, String identifier, int base) {
|
||||
this.base = base;
|
||||
this.power = power;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
@ -312,9 +377,23 @@ public class MeasureUnit implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the power of 10 of the prefix. For example, if the prefix is "centi", the power will be -2.
|
||||
* Returns the base of the prefix. For example:
|
||||
* - if the prefix is "centi", the base will be 10.
|
||||
* - if the prefix is "gibi", the base will be 1024.
|
||||
*
|
||||
* @draft ICU 68
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public int getBase() {
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the power of the prefix. For example:
|
||||
* - if the prefix is "centi", the power will be -2.
|
||||
* - if the prefix is "gibi", the power will be 3 (for base 1024).
|
||||
*
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public int getPower() {
|
||||
|
@ -426,41 +505,41 @@ public class MeasureUnit implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a MeasureUnit which is this SINGLE unit augmented with the specified SI prefix.
|
||||
* For example, SI_PREFIX_KILO for "kilo".
|
||||
* May return this if this unit already has that prefix.
|
||||
* Creates a MeasureUnit which is this SINGLE unit augmented with the specified prefix.
|
||||
* For example, MeasurePrefix.KILO for "kilo", or MeasurePrefix.KIBI for "kibi".
|
||||
* May return `this` if this unit already has that prefix.
|
||||
* <p>
|
||||
* There is sufficient locale data to format all standard SI prefixes.
|
||||
* There is sufficient locale data to format all standard prefixes.
|
||||
* <p>
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see `Complexity`.
|
||||
*
|
||||
* @param prefix The SI prefix, from SIPrefix.
|
||||
* @param prefix The prefix, from MeasurePrefix.
|
||||
* @return A new SINGLE unit.
|
||||
* @throws UnsupportedOperationException if this unit is a COMPOUND or MIXED unit.
|
||||
* @draft ICU 68
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public MeasureUnit withSIPrefix(SIPrefix prefix) {
|
||||
public MeasureUnit withPrefix(MeasurePrefix prefix) {
|
||||
SingleUnitImpl singleUnit = getSingleUnitImpl();
|
||||
singleUnit.setSiPrefix(prefix);
|
||||
singleUnit.setPrefix(prefix);
|
||||
return singleUnit.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current SI prefix of this SINGLE unit. For example, if the unit has the SI prefix
|
||||
* "kilo", then SI_PREFIX_KILO is returned.
|
||||
* Returns the current SI or binary prefix of this SINGLE unit. For example,
|
||||
* if the unit has the prefix "kilo", then MeasurePrefix.KILO is returned.
|
||||
* <p>
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see `Complexity`.
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an
|
||||
* error will occur. For more information, see `Complexity`.
|
||||
*
|
||||
* @return The SI prefix of this SINGLE unit, from SIPrefix.
|
||||
* @return The prefix of this SINGLE unit, from MeasurePrefix.
|
||||
* @throws UnsupportedOperationException if the unit is COMPOUND or MIXED.
|
||||
* @draft ICU 68
|
||||
* @draft ICU 69
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public SIPrefix getSIPrefix() {
|
||||
return getSingleUnitImpl().getSiPrefix();
|
||||
public MeasurePrefix getPrefix() {
|
||||
return getSingleUnitImpl().getPrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -560,7 +639,7 @@ public class MeasureUnit implements Serializable {
|
|||
* <p>
|
||||
* Examples:
|
||||
* - Given "meter-kilogram-per-second", three units will be returned: "meter",
|
||||
* "kilogram", and "one-per-second".
|
||||
* "kilogram", and "per-second".
|
||||
* - Given "hour+minute+second", three units will be returned: "hour", "minute",
|
||||
* and "second".
|
||||
* <p>
|
||||
|
@ -1982,6 +2061,7 @@ public class MeasureUnit implements Serializable {
|
|||
* @return this object as a SingleUnitImpl.
|
||||
* @throws UnsupportedOperationException if this object could not be converted to a single unit.
|
||||
*/
|
||||
// In ICU4C, this is SingleUnitImpl::forMeasureUnit().
|
||||
private SingleUnitImpl getSingleUnitImpl() {
|
||||
if (measureUnitImpl == null) {
|
||||
return MeasureUnitImpl.forIdentifier(getIdentifier()).getSingleUnitImpl();
|
||||
|
|
|
@ -3481,27 +3481,61 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
}
|
||||
|
||||
TestCase cases[] = {
|
||||
// Correctly normalized identifiers should not change
|
||||
new TestCase("square-meter-per-square-meter", "square-meter-per-square-meter"),
|
||||
new TestCase("kilogram-meter-per-square-meter-square-second",
|
||||
"kilogram-meter-per-square-meter-square-second"),
|
||||
new TestCase("square-mile-and-square-foot", "square-mile-and-square-foot"),
|
||||
new TestCase("square-foot-and-square-mile", "square-foot-and-square-mile"),
|
||||
new TestCase("per-cubic-centimeter", "per-cubic-centimeter"),
|
||||
new TestCase("per-kilometer", "per-kilometer"),
|
||||
// Correctly normalized identifiers should not change
|
||||
new TestCase("square-meter-per-square-meter", "square-meter-per-square-meter"),
|
||||
new TestCase("kilogram-meter-per-square-meter-square-second",
|
||||
"kilogram-meter-per-square-meter-square-second"),
|
||||
new TestCase("square-mile-and-square-foot", "square-mile-and-square-foot"),
|
||||
new TestCase("square-foot-and-square-mile", "square-foot-and-square-mile"),
|
||||
new TestCase("per-cubic-centimeter", "per-cubic-centimeter"),
|
||||
new TestCase("per-kilometer", "per-kilometer"),
|
||||
|
||||
// Normalization of power and per
|
||||
new TestCase(
|
||||
"pow2-foot-and-pow2-mile", "square-foot-and-square-mile"),
|
||||
new TestCase(
|
||||
"gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"),
|
||||
new TestCase(
|
||||
"kilogram-per-meter-per-second", "kilogram-per-meter-second"),
|
||||
// Normalization of power and per
|
||||
new TestCase("pow2-foot-and-pow2-mile", "square-foot-and-square-mile"),
|
||||
new TestCase("gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"),
|
||||
new TestCase("kilogram-per-meter-per-second", "kilogram-per-meter-second"),
|
||||
|
||||
// TODO(ICU-21284): Add more test cases once the proper ranking is available.
|
||||
// TODO(ICU-21284): Add more test cases once the proper ranking is available.
|
||||
|
||||
// Testing prefixes are parsed and produced correctly (ensures no
|
||||
// collisions in the enum values)
|
||||
new TestCase("yoctofoot", "yoctofoot"),
|
||||
new TestCase("zeptofoot", "zeptofoot"),
|
||||
new TestCase("attofoot", "attofoot"),
|
||||
new TestCase("femtofoot", "femtofoot"),
|
||||
new TestCase("picofoot", "picofoot"),
|
||||
new TestCase("nanofoot", "nanofoot"),
|
||||
new TestCase("microfoot", "microfoot"),
|
||||
new TestCase("millifoot", "millifoot"),
|
||||
new TestCase("centifoot", "centifoot"),
|
||||
new TestCase("decifoot", "decifoot"),
|
||||
new TestCase("foot", "foot"),
|
||||
new TestCase("dekafoot", "dekafoot"),
|
||||
new TestCase("hectofoot", "hectofoot"),
|
||||
new TestCase("kilofoot", "kilofoot"),
|
||||
new TestCase("megafoot", "megafoot"),
|
||||
new TestCase("gigafoot", "gigafoot"),
|
||||
new TestCase("terafoot", "terafoot"),
|
||||
new TestCase("petafoot", "petafoot"),
|
||||
new TestCase("exafoot", "exafoot"),
|
||||
new TestCase("zettafoot", "zettafoot"),
|
||||
new TestCase("yottafoot", "yottafoot"),
|
||||
new TestCase("kibibyte", "kibibyte"),
|
||||
new TestCase("mebibyte", "mebibyte"),
|
||||
new TestCase("gibibyte", "gibibyte"),
|
||||
new TestCase("tebibyte", "tebibyte"),
|
||||
new TestCase("pebibyte", "pebibyte"),
|
||||
new TestCase("exbibyte", "exbibyte"),
|
||||
new TestCase("zebibyte", "zebibyte"),
|
||||
new TestCase("yobibyte", "yobibyte"),
|
||||
|
||||
// Testing sort order of prefixes.
|
||||
//
|
||||
// TODO(icu-units#70): revisit when fixing normalization. For now we're
|
||||
// just checking some consistency between C&J.
|
||||
new TestCase("megafoot-mebifoot-kibifoot-kilofoot", "kibifoot-mebifoot-kilofoot-megafoot"),
|
||||
};
|
||||
|
||||
|
||||
for (TestCase testCase : cases) {
|
||||
MeasureUnit unit = MeasureUnit.forIdentifier(testCase.id);
|
||||
|
||||
|
@ -3562,22 +3596,120 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestIdentifierDetails() {
|
||||
MeasureUnit joule = MeasureUnit.forIdentifier("joule");
|
||||
assertEquals("Initial joule", "joule", joule.getIdentifier());
|
||||
|
||||
// "Invalid prefix" test not needed: in Java we cannot pass a
|
||||
// non-existant enum instance. (In C++ an int can be typecast.)
|
||||
|
||||
MeasureUnit unit = joule.withPrefix(MeasureUnit.MeasurePrefix.HECTO);
|
||||
assertEquals("Joule with hecto prefix", "hectojoule", unit.getIdentifier());
|
||||
|
||||
unit = unit.withPrefix(MeasureUnit.MeasurePrefix.EXBI);
|
||||
assertEquals("Joule with exbi prefix", "exbijoule", unit.getIdentifier());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestPrefixes() {
|
||||
class TestCase {
|
||||
final MeasureUnit.MeasurePrefix prefix;
|
||||
final int expectedBase;
|
||||
final int expectedPower;
|
||||
|
||||
TestCase(MeasureUnit.MeasurePrefix prefix, int expectedBase, int expectedPower) {
|
||||
this.prefix = prefix;
|
||||
this.expectedBase = expectedBase;
|
||||
this.expectedPower = expectedPower;
|
||||
}
|
||||
}
|
||||
|
||||
TestCase cases[] = {
|
||||
new TestCase(MeasureUnit.MeasurePrefix.YOCTO, 10, -24),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.ZEPTO, 10, -21),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.ATTO, 10, -18),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.FEMTO, 10, -15),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.PICO, 10, -12),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.NANO, 10, -9),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.MICRO, 10, -6),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.MILLI, 10, -3),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.CENTI, 10, -2),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.DECI, 10, -1),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.ONE, 10, 0),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.DEKA, 10, 1),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.HECTO, 10, 2),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.KILO, 10, 3),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.MEGA, 10, 6),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.GIGA, 10, 9),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.TERA, 10, 12),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.PETA, 10, 15),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.EXA, 10, 18),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.ZETTA, 10, 21),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.YOTTA, 10, 24),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.KIBI, 1024, 1),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.MEBI, 1024, 2),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.GIBI, 1024, 3),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.TEBI, 1024, 4),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.PEBI, 1024, 5),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.EXBI, 1024, 6),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.ZEBI, 1024, 7),
|
||||
new TestCase(MeasureUnit.MeasurePrefix.YOBI, 1024, 8),
|
||||
};
|
||||
|
||||
for (TestCase testCase : cases) {
|
||||
MeasureUnit m = MeasureUnit.AMPERE.withPrefix(testCase.prefix);
|
||||
assertEquals("getPrefixPower()", testCase.expectedPower, m.getPrefix().getPower());
|
||||
assertEquals("getPrefixBase()", testCase.expectedBase, m.getPrefix().getBase());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestParseToBuiltIn() {
|
||||
class TestCase {
|
||||
final String identifier;
|
||||
MeasureUnit expectedBuiltin;
|
||||
|
||||
TestCase(String identifier, MeasureUnit expectedBuiltin) {
|
||||
this.identifier = identifier;
|
||||
this.expectedBuiltin = expectedBuiltin;
|
||||
}
|
||||
}
|
||||
|
||||
TestCase cases[] = {
|
||||
new TestCase("meter-per-second-per-second", MeasureUnit.METER_PER_SECOND_SQUARED),
|
||||
new TestCase("meter-per-second-second", MeasureUnit.METER_PER_SECOND_SQUARED),
|
||||
new TestCase("centimeter-centimeter", MeasureUnit.SQUARE_CENTIMETER),
|
||||
new TestCase("square-foot", MeasureUnit.SQUARE_FOOT),
|
||||
new TestCase("pow2-inch", MeasureUnit.SQUARE_INCH),
|
||||
new TestCase("milligram-per-deciliter", MeasureUnit.MILLIGRAM_PER_DECILITER),
|
||||
new TestCase("pound-force-per-pow2-inch", MeasureUnit.POUND_PER_SQUARE_INCH),
|
||||
new TestCase("yard-pow2-yard", MeasureUnit.CUBIC_YARD),
|
||||
new TestCase("square-yard-yard", MeasureUnit.CUBIC_YARD),
|
||||
};
|
||||
|
||||
for (TestCase testCase : cases) {
|
||||
MeasureUnit m = MeasureUnit.forIdentifier(testCase.identifier);
|
||||
assertTrue(testCase.identifier + " parsed to builtin", m.equals(testCase.expectedBuiltin));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestCompoundUnitOperations() {
|
||||
MeasureUnit.forIdentifier("kilometer-per-second-joule");
|
||||
|
||||
MeasureUnit kilometer = MeasureUnit.KILOMETER;
|
||||
MeasureUnit cubicMeter = MeasureUnit.CUBIC_METER;
|
||||
MeasureUnit meter = kilometer.withSIPrefix(MeasureUnit.SIPrefix.ONE);
|
||||
MeasureUnit centimeter1 = kilometer.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
|
||||
MeasureUnit centimeter2 = meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
|
||||
MeasureUnit cubicDecimeter = cubicMeter.withSIPrefix(MeasureUnit.SIPrefix.DECI);
|
||||
MeasureUnit meter = kilometer.withPrefix(MeasureUnit.MeasurePrefix.ONE);
|
||||
MeasureUnit centimeter1 = kilometer.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
|
||||
MeasureUnit centimeter2 = meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
|
||||
MeasureUnit cubicDecimeter = cubicMeter.withPrefix(MeasureUnit.MeasurePrefix.DECI);
|
||||
|
||||
verifySingleUnit(kilometer, MeasureUnit.SIPrefix.KILO, 1, "kilometer");
|
||||
verifySingleUnit(meter, MeasureUnit.SIPrefix.ONE, 1, "meter");
|
||||
verifySingleUnit(centimeter1, MeasureUnit.SIPrefix.CENTI, 1, "centimeter");
|
||||
verifySingleUnit(centimeter2, MeasureUnit.SIPrefix.CENTI, 1, "centimeter");
|
||||
verifySingleUnit(cubicDecimeter, MeasureUnit.SIPrefix.DECI, 3, "cubic-decimeter");
|
||||
verifySingleUnit(kilometer, MeasureUnit.MeasurePrefix.KILO, 1, "kilometer");
|
||||
verifySingleUnit(meter, MeasureUnit.MeasurePrefix.ONE, 1, "meter");
|
||||
verifySingleUnit(centimeter1, MeasureUnit.MeasurePrefix.CENTI, 1, "centimeter");
|
||||
verifySingleUnit(centimeter2, MeasureUnit.MeasurePrefix.CENTI, 1, "centimeter");
|
||||
verifySingleUnit(cubicDecimeter, MeasureUnit.MeasurePrefix.DECI, 3, "cubic-decimeter");
|
||||
|
||||
assertTrue("centimeter equality", centimeter1.equals( centimeter2));
|
||||
assertTrue("kilometer inequality", !centimeter1.equals( kilometer));
|
||||
|
@ -3587,10 +3719,10 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
MeasureUnit quarticKilometer = kilometer.withDimensionality(4);
|
||||
MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4);
|
||||
|
||||
verifySingleUnit(squareMeter, MeasureUnit.SIPrefix.ONE, 2, "square-meter");
|
||||
verifySingleUnit(overCubicCentimeter, MeasureUnit.SIPrefix.CENTI, -3, "per-cubic-centimeter");
|
||||
verifySingleUnit(quarticKilometer, MeasureUnit.SIPrefix.KILO, 4, "pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(squareMeter, MeasureUnit.MeasurePrefix.ONE, 2, "square-meter");
|
||||
verifySingleUnit(overCubicCentimeter, MeasureUnit.MeasurePrefix.CENTI, -3, "per-cubic-centimeter");
|
||||
verifySingleUnit(quarticKilometer, MeasureUnit.MeasurePrefix.KILO, 4, "pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
|
||||
|
||||
assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
|
||||
|
||||
|
@ -3601,26 +3733,26 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
.reciprocal();
|
||||
MeasureUnit overQuarticKilometer4 = meter.withDimensionality(4)
|
||||
.reciprocal()
|
||||
.withSIPrefix(MeasureUnit.SIPrefix.KILO);
|
||||
.withPrefix(MeasureUnit.MeasurePrefix.KILO);
|
||||
|
||||
verifySingleUnit(overQuarticKilometer2, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, MeasureUnit.SIPrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer2, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, MeasureUnit.MeasurePrefix.KILO, -4, "per-pow4-kilometer");
|
||||
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer2));
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer3));
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1.equals(overQuarticKilometer4));
|
||||
|
||||
MeasureUnit kiloSquareSecond = MeasureUnit.SECOND
|
||||
.withDimensionality(2).withSIPrefix(MeasureUnit.SIPrefix.KILO);
|
||||
.withDimensionality(2).withPrefix(MeasureUnit.MeasurePrefix.KILO);
|
||||
MeasureUnit meterSecond = meter.product(kiloSquareSecond);
|
||||
MeasureUnit cubicMeterSecond1 = meter.withDimensionality(3).product(kiloSquareSecond);
|
||||
MeasureUnit centimeterSecond1 = meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI).product(kiloSquareSecond);
|
||||
MeasureUnit centimeterSecond1 = meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI).product(kiloSquareSecond);
|
||||
MeasureUnit secondCubicMeter = kiloSquareSecond.product(meter.withDimensionality(3));
|
||||
MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withSIPrefix(MeasureUnit.SIPrefix.CENTI));
|
||||
MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withPrefix(MeasureUnit.MeasurePrefix.CENTI));
|
||||
MeasureUnit secondCentimeterPerKilometer = secondCentimeter.product(kilometer.reciprocal());
|
||||
|
||||
verifySingleUnit(kiloSquareSecond, MeasureUnit.SIPrefix.KILO, 2, "square-kilosecond");
|
||||
verifySingleUnit(kiloSquareSecond, MeasureUnit.MeasurePrefix.KILO, 2, "square-kilosecond");
|
||||
String meterSecondSub[] = {
|
||||
"meter", "square-kilosecond"
|
||||
};
|
||||
|
@ -3655,7 +3787,7 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
assertTrue("reordering equality", cubicMeterSecond1.equals(secondCubicMeter));
|
||||
assertTrue("additional simple units inequality", !secondCubicMeter.equals(secondCentimeter));
|
||||
|
||||
// Don't allow get/set power or SI prefix on compound units
|
||||
// Don't allow get/set power or SI or binary prefix on compound units
|
||||
try {
|
||||
meterSecond.getDimensionality();
|
||||
fail("UnsupportedOperationException must be thrown");
|
||||
|
@ -3671,14 +3803,14 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
}
|
||||
|
||||
try {
|
||||
meterSecond.getSIPrefix();
|
||||
meterSecond.getPrefix();
|
||||
fail("UnsupportedOperationException must be thrown");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Expecting an exception to be thrown
|
||||
}
|
||||
|
||||
try {
|
||||
meterSecond.withSIPrefix(MeasureUnit.SIPrefix.CENTI);
|
||||
meterSecond.withPrefix(MeasureUnit.MeasurePrefix.CENTI);
|
||||
fail("UnsupportedOperationException must be thrown");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Expecting an exception to be thrown
|
||||
|
@ -3709,12 +3841,12 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
// with others via product:
|
||||
MeasureUnit kilometer2 = kilometer.product(dimensionless);
|
||||
|
||||
verifySingleUnit(kilometer2, MeasureUnit.SIPrefix.KILO, 1, "kilometer");
|
||||
verifySingleUnit(kilometer2, MeasureUnit.MeasurePrefix.KILO, 1, "kilometer");
|
||||
assertTrue("kilometer equality", kilometer.equals(kilometer2));
|
||||
|
||||
// Test out-of-range powers
|
||||
MeasureUnit power15 = MeasureUnit.forIdentifier("pow15-kilometer");
|
||||
verifySingleUnit(power15, MeasureUnit.SIPrefix.KILO, 15, "pow15-kilometer");
|
||||
verifySingleUnit(power15, MeasureUnit.MeasurePrefix.KILO, 15, "pow15-kilometer");
|
||||
|
||||
try {
|
||||
MeasureUnit.forIdentifier("pow16-kilometer");
|
||||
|
@ -3731,7 +3863,7 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
}
|
||||
|
||||
MeasureUnit powerN15 = MeasureUnit.forIdentifier("per-pow15-kilometer");
|
||||
verifySingleUnit(powerN15, MeasureUnit.SIPrefix.KILO, -15, "per-pow15-kilometer");
|
||||
verifySingleUnit(powerN15, MeasureUnit.MeasurePrefix.KILO, -15, "per-pow15-kilometer");
|
||||
|
||||
try {
|
||||
MeasureUnit.forIdentifier("per-pow16-kilometer");
|
||||
|
@ -3762,11 +3894,11 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
// product(dimensionless)
|
||||
MeasureUnit mile = MeasureUnit.MILE;
|
||||
mile = mile.product(dimensionless);
|
||||
verifySingleUnit(mile, MeasureUnit.SIPrefix.ONE, 1, "mile");
|
||||
verifySingleUnit(mile, MeasureUnit.MeasurePrefix.ONE, 1, "mile");
|
||||
}
|
||||
|
||||
private void verifySingleUnit(MeasureUnit singleMeasureUnit, MeasureUnit.SIPrefix prefix, int power, String identifier) {
|
||||
assertEquals(identifier + ": SI prefix", prefix, singleMeasureUnit.getSIPrefix());
|
||||
private void verifySingleUnit(MeasureUnit singleMeasureUnit, MeasureUnit.MeasurePrefix prefix, int power, String identifier) {
|
||||
assertEquals(identifier + ": SI or binary prefix", prefix, singleMeasureUnit.getPrefix());
|
||||
|
||||
assertEquals(identifier + ": Power", power, singleMeasureUnit.getDimensionality());
|
||||
|
||||
|
@ -3805,12 +3937,12 @@ public class MeasureUnitTest extends TestFmwk {
|
|||
assertEquals("nanogram", null, nanogram.getType());
|
||||
assertEquals("nanogram", "nanogram", nanogram.getIdentifier());
|
||||
|
||||
assertEquals("prefix of kilogram", MeasureUnit.SIPrefix.KILO, kilogram.getSIPrefix());
|
||||
assertEquals("prefix of gram", MeasureUnit.SIPrefix.ONE, gram.getSIPrefix());
|
||||
assertEquals("prefix of microgram", MeasureUnit.SIPrefix.MICRO, microgram.getSIPrefix());
|
||||
assertEquals("prefix of nanogram", MeasureUnit.SIPrefix.NANO, nanogram.getSIPrefix());
|
||||
assertEquals("prefix of kilogram", MeasureUnit.MeasurePrefix.KILO, kilogram.getPrefix());
|
||||
assertEquals("prefix of gram", MeasureUnit.MeasurePrefix.ONE, gram.getPrefix());
|
||||
assertEquals("prefix of microgram", MeasureUnit.MeasurePrefix.MICRO, microgram.getPrefix());
|
||||
assertEquals("prefix of nanogram", MeasureUnit.MeasurePrefix.NANO, nanogram.getPrefix());
|
||||
|
||||
MeasureUnit tmp = kilogram.withSIPrefix(MeasureUnit.SIPrefix.MILLI);
|
||||
MeasureUnit tmp = kilogram.withPrefix(MeasureUnit.MeasurePrefix.MILLI);
|
||||
assertEquals("Kilogram + milli should be milligram, got: " + tmp.getIdentifier(),
|
||||
MeasureUnit.MILLIGRAM.getIdentifier(), tmp.getIdentifier());
|
||||
}
|
||||
|
|
|
@ -296,6 +296,14 @@ public class UnitsTest {
|
|||
new TestData("gigabyte", "byte", 1.0, 1000000000),
|
||||
new TestData("megawatt", "watt", 1.0, 1000000),
|
||||
new TestData("megawatt", "kilowatt", 1.0, 1000),
|
||||
// Binary Prefixes
|
||||
new TestData("kilobyte", "byte", 1, 1000),
|
||||
new TestData("kibibyte", "byte", 1, 1024),
|
||||
new TestData("mebibyte", "byte", 1, 1048576),
|
||||
new TestData("gibibyte", "kibibyte", 1, 1048576),
|
||||
new TestData("pebibyte", "tebibyte", 4, 4096),
|
||||
new TestData("zebibyte", "pebibyte", 1.0/16, 65536.0),
|
||||
new TestData("yobibyte", "exbibyte", 1, 1048576),
|
||||
// Mass
|
||||
new TestData("gram", "kilogram", 1.0, 0.001),
|
||||
new TestData("pound", "kilogram", 1.0, 0.453592),
|
||||
|
@ -333,7 +341,13 @@ public class UnitsTest {
|
|||
ConversionRates conversionRates = new ConversionRates();
|
||||
for (TestData test : tests) {
|
||||
UnitConverter converter = new UnitConverter(test.source, test.target, conversionRates);
|
||||
assertEquals(test.expected.doubleValue(), converter.convert(test.input).doubleValue(), (0.001));
|
||||
double maxDelta = 1e-6 * Math.abs(test.expected.doubleValue());
|
||||
if (test.expected.doubleValue() == 0) {
|
||||
maxDelta = 1e-12;
|
||||
}
|
||||
assertEquals("testConverter: " + test.source + " to " + test.target,
|
||||
test.expected.doubleValue(), converter.convert(test.input).doubleValue(),
|
||||
maxDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1067,10 +1067,9 @@ public class NumberFormatterApiTest extends TestFmwk {
|
|||
"measure-unit/concentr-permille", //
|
||||
"permille"},
|
||||
|
||||
// // TODO: binary prefixes not supported yet!
|
||||
// {"Round-trip example from icu-units#35", //
|
||||
// "unit/kibijoule-per-furlong", //
|
||||
// "unit/kibijoule-per-furlong"},
|
||||
{"Round-trip example from icu-units#35", //
|
||||
"unit/kibijoule-per-furlong", //
|
||||
"unit/kibijoule-per-furlong"},
|
||||
};
|
||||
for (Object[] cas : cases) {
|
||||
String msg = (String)cas[0];
|
||||
|
|
Loading…
Add table
Reference in a new issue