mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-08 06:53:45 +00:00
Merge pull request #10 from icu-units/get_percision
Add precision to the output of UnitsRouter#route
This commit is contained in:
commit
030bda3ec8
8 changed files with 60 additions and 29 deletions
|
@ -12,7 +12,6 @@
|
|||
#include "uassert.h"
|
||||
#include "unicode/fmtable.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/measure.h"
|
||||
#include "unitconverter.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
|
||||
#include "cmemory.h"
|
||||
#include "unicode/measunit.h"
|
||||
#include "unicode/measure.h"
|
||||
#include "unitconverter.h"
|
||||
#include "unitsdata.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// Forward declarations
|
||||
class Measure;
|
||||
|
||||
namespace units {
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,15 +99,17 @@ void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
|
|||
}
|
||||
|
||||
quantity.roundToInfinity(); // Enables toDouble
|
||||
auto routed = fUnitsRouter.route(quantity.toDouble(), status);
|
||||
micros.outputUnit = routed[0]->getUnit();
|
||||
quantity.setToDouble(routed[0]->getNumber().getDouble());
|
||||
const auto routed = fUnitsRouter.route(quantity.toDouble(), status);
|
||||
const auto& routedUnits = routed.measures;
|
||||
micros.outputUnit = routedUnits[0]->getUnit();
|
||||
quantity.setToDouble(routedUnits[0]->getNumber().getDouble());
|
||||
|
||||
// TODO(units): here we are always overriding Precision. (1) get precision
|
||||
// from fUnitsRouter, (2) ensure we use the UnitPreference skeleton's
|
||||
// precision only when there isn't an explicit override we prefer to use.
|
||||
// This needs to be handled within
|
||||
// NumberFormatterImpl::macrosToMicroGenerator in number_formatimpl.cpp
|
||||
// TODO: Use precision from `routed` result.
|
||||
Precision precision = Precision::integer().withMinDigits(2);
|
||||
UNumberFormatRoundingMode roundingMode;
|
||||
// Temporary until ICU 64?
|
||||
|
|
|
@ -248,9 +248,7 @@ class UnitPreferencesSink : public ResourceSink {
|
|||
dq.setToDecNumber(geq.data(), status);
|
||||
up->geq = dq.toDouble();
|
||||
} else if (uprv_strcmp(key, "skeleton") == 0) {
|
||||
int32_t length;
|
||||
const UChar *s = value.getString(length, status);
|
||||
up->skeleton.appendInvariantChars(s, length, status);
|
||||
up->skeleton = value.getUnicodeString(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#ifndef __GETUNITSDATA_H__
|
||||
#define __GETUNITSDATA_H__
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "charstr.h"
|
||||
#include "cmemory.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
|
@ -96,10 +98,11 @@ class U_I18N_API ConversionRates {
|
|||
// Encapsulates unitPreferenceData information from units resources, specifying
|
||||
// a sequence of output unit preferences.
|
||||
struct U_I18N_API UnitPreference : public UMemory {
|
||||
UnitPreference() : geq(1) {}
|
||||
// Set geq to 1.0 by default
|
||||
UnitPreference() : geq(1.0) {}
|
||||
CharString unit;
|
||||
double geq;
|
||||
CharString skeleton;
|
||||
UnicodeString skeleton;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -37,30 +37,49 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
return;
|
||||
}
|
||||
|
||||
UnicodeString precision = preference.skeleton;
|
||||
|
||||
// For now, we only have "precision-increment" in Units Preferences skeleton.
|
||||
// Therefore, we check if the skeleton starts with "precision-increment" and force the program to
|
||||
// fail otherwise.
|
||||
// NOTE:
|
||||
// It is allowed to have an empty precision.
|
||||
if (!precision.isEmpty() && !precision.startsWith(u"precision-increment", 19)) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
outputUnits_.emplaceBackAndCheckErrorCode(status, complexTargetUnit);
|
||||
converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnit, complexTargetUnit,
|
||||
preference.geq, conversionRates, status);
|
||||
converterPreferences_.emplaceBackAndCheckErrorCode(
|
||||
status, inputUnit, complexTargetUnit, preference.geq, precision, conversionRates, status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) const {
|
||||
RouteResult UnitsRouter::route(double quantity, UErrorCode &status) const {
|
||||
for (int i = 0, n = converterPreferences_.length(); i < n; i++) {
|
||||
const auto &converterPreference = *converterPreferences_[i];
|
||||
|
||||
if (converterPreference.converter.greaterThanOrEqual(quantity, converterPreference.limit)) {
|
||||
return converterPreference.converter.convert(quantity, status);
|
||||
return RouteResult(converterPreference.converter.convert(quantity, status), //
|
||||
converterPreference.precision //
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// In case of the `quantity` does not fit in any converter limit, use the last converter.
|
||||
const auto &lastConverter = (*converterPreferences_[converterPreferences_.length() - 1]).converter;
|
||||
return lastConverter.convert(quantity, status);
|
||||
const auto &lastConverterPreference = (*converterPreferences_[converterPreferences_.length() - 1]);
|
||||
return RouteResult(lastConverterPreference.converter.convert(quantity, status), //
|
||||
lastConverterPreference.precision //
|
||||
);
|
||||
}
|
||||
|
||||
const MaybeStackVector<MeasureUnit> *UnitsRouter::getOutputUnits() const {
|
||||
// TODO: consider pulling this from converterPreferences_ and dropping
|
||||
// outputUnits_?
|
||||
return &outputUnits_;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,14 @@ class Measure;
|
|||
|
||||
namespace units {
|
||||
|
||||
struct RouteResult : UMemory {
|
||||
MaybeStackVector<Measure> measures;
|
||||
UnicodeString precision;
|
||||
|
||||
RouteResult(MaybeStackVector<Measure> measures, UnicodeString precision)
|
||||
: measures(std::move(measures)), precision(std::move(precision)) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the complex unit converter and the limit which representing the smallest value that the
|
||||
* converter should accept. For example, if the converter is converting to `foot+inch` and the limit
|
||||
|
@ -35,15 +43,17 @@ namespace units {
|
|||
struct ConverterPreference : UMemory {
|
||||
ComplexUnitsConverter converter;
|
||||
double limit;
|
||||
UnicodeString precision;
|
||||
|
||||
// In case there is no limit, the limit will be -inf.
|
||||
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, UnicodeString precision,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), precision,
|
||||
ratesInfo, status) {}
|
||||
|
||||
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, double limit,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: converter(source, complexTarget, ratesInfo, status), limit(limit) {}
|
||||
|
||||
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status)
|
||||
: ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), ratesInfo,
|
||||
status) {}
|
||||
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: converter(source, complexTarget, ratesInfo, status), limit(limit), precision(precision) {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -78,7 +88,7 @@ class U_I18N_API UnitsRouter {
|
|||
public:
|
||||
UnitsRouter(MeasureUnit inputUnit, StringPiece locale, StringPiece usage, UErrorCode &status);
|
||||
|
||||
MaybeStackVector<Measure> route(double quantity, UErrorCode &status) const;
|
||||
RouteResult route(double quantity, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Returns the list of possible output units, i.e. the full set of
|
||||
|
@ -90,7 +100,9 @@ class U_I18N_API UnitsRouter {
|
|||
const MaybeStackVector<MeasureUnit> *getOutputUnits() const;
|
||||
|
||||
private:
|
||||
// List of possible output units
|
||||
// List of possible output units. TODO: converterPreferences_ now also has
|
||||
// this data available. Maybe drop outputUnits_ and have getOutputUnits
|
||||
// construct a the list from data in converterPreferences_ instead?
|
||||
MaybeStackVector<MeasureUnit> outputUnits_;
|
||||
|
||||
MaybeStackVector<ConverterPreference> converterPreferences_;
|
||||
|
|
|
@ -617,12 +617,12 @@ void unitPreferencesTestDataLineFn(void *context, char *fields[][2], int32_t fie
|
|||
if (status.errIfFailureAndReset("Failure before router.route")) {
|
||||
return;
|
||||
}
|
||||
MaybeStackVector<Measure> result = router.route(inputAmount, status);
|
||||
auto routeResult = router.route(inputAmount, status);
|
||||
if (status.errIfFailureAndReset("router.route(inputAmount, ...)")) {
|
||||
return;
|
||||
}
|
||||
// TODO: revisit this experimentally chosen precision:
|
||||
checkOutput(unitsTest, msg.data(), expected, result, 0.0000000001);
|
||||
checkOutput(unitsTest, msg.data(), expected, routeResult.measures, 0.0000000001);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue