units_converter.h (9235B)
1 // © 2020 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 4 #include "unicode/utypes.h" 5 6 #if !UCONFIG_NO_FORMATTING 7 #ifndef __UNITS_CONVERTER_H__ 8 #define __UNITS_CONVERTER_H__ 9 10 #include "cmemory.h" 11 #include "fixedstring.h" 12 #include "measunit_impl.h" 13 #include "unicode/errorcode.h" 14 #include "unicode/stringpiece.h" 15 #include "unicode/uobject.h" 16 #include "units_converter.h" 17 #include "units_data.h" 18 19 U_NAMESPACE_BEGIN 20 namespace units { 21 22 /* Internal Structure */ 23 24 // Constants corresponding to unitConstants in CLDR's units.xml. 25 enum Constants { 26 CONSTANT_FT2M, // ft_to_m 27 CONSTANT_PI, // PI 28 CONSTANT_GRAVITY, // Gravity of earth (9.80665 m/s^2), "g". 29 CONSTANT_G, // Newtonian constant of gravitation, "G". 30 CONSTANT_GAL_IMP2M3, // Gallon imp to m3 31 CONSTANT_LB2KG, // Pound to Kilogram 32 CONSTANT_GLUCOSE_MOLAR_MASS, 33 CONSTANT_ITEM_PER_MOLE, 34 CONSTANT_METERS_PER_AU, 35 CONSTANT_SEC_PER_JULIAN_YEAR, 36 CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND, 37 CONSTANT_SHO_TO_M3, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 38 CONSTANT_TSUBO_TO_M2, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 39 CONSTANT_SHAKU_TO_M, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 40 CONSTANT_AMU, // Atomic Mass Unit https://www.nist.gov/pml/special-publication-811/nist-guide-si-chapter-5-units-outside-si#table7 41 42 // Must be the last element. 43 CONSTANTS_COUNT 44 }; 45 46 // These values are a hard-coded subset of unitConstants in the units 47 // resources file. A unit test checks that all constants in the resource 48 // file are at least recognised by the code. Derived constants' values or 49 // hard-coded derivations are not checked. 50 // In ICU4J, these constants live in UnitConverter.Factor.getConversionRate(). 51 static const double constantsValues[CONSTANTS_COUNT] = { 52 0.3048, // CONSTANT_FT2M 53 411557987.0 / 131002976.0, // CONSTANT_PI 54 9.80665, // CONSTANT_GRAVITY 55 6.67408E-11, // CONSTANT_G 56 0.00454609, // CONSTANT_GAL_IMP2M3 57 0.45359237, // CONSTANT_LB2KG 58 180.1557, // CONSTANT_GLUCOSE_MOLAR_MASS 59 6.02214076E+23, // CONSTANT_ITEM_PER_MOLE 60 149597870700, // CONSTANT_METERS_PER_AU 61 31557600, // CONSTANT_SEC_PER_JULIAN_YEAR 62 299792458, // CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND 63 2401.0 / (1331.0 * 1000.0), 64 400.0 / 121.0, 65 4.0 / 121.0, 66 1.66053878283E-27, // CONSTANT_AMU 67 }; 68 69 typedef enum Signum { 70 NEGATIVE = -1, 71 POSITIVE = 1, 72 } Signum; 73 74 /* Represents a conversion factor */ 75 struct U_I18N_API_CLASS Factor { 76 double factorNum = 1; 77 double factorDen = 1; 78 double offset = 0; 79 bool reciprocal = false; 80 81 // Exponents for the symbolic constants 82 int32_t constantExponents[CONSTANTS_COUNT] = {}; 83 84 void multiplyBy(const Factor &rhs); 85 void divideBy(const Factor &rhs); 86 void divideBy(const uint64_t constant); 87 88 // Apply the power to the factor. 89 void power(int32_t power); 90 91 // Apply SI or binary prefix to the Factor. 92 void applyPrefix(UMeasurePrefix unitPrefix); 93 94 // Does an in-place substitution of the "symbolic constants" based on 95 // constantExponents (resetting the exponents). 96 // 97 // In ICU4J, see UnitConverter.Factor.getConversionRate(). 98 U_I18N_API void substituteConstants(); 99 }; 100 101 struct ConversionInfo { 102 double conversionRate; 103 double offset; 104 bool reciprocal; 105 }; 106 107 /* 108 * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3". 109 */ 110 void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum, 111 Factor &factor, UErrorCode &status); 112 113 /** 114 * Represents the conversion rate between `source` and `target`. 115 * TODO ICU-22683: COnsider moving the handling of special mappings (e.g. beaufort) to a separate 116 * struct. 117 */ 118 struct ConversionRate : public UMemory { 119 const MeasureUnitImpl source; 120 const MeasureUnitImpl target; 121 FixedString specialSource; 122 FixedString specialTarget; 123 double factorNum = 1; 124 double factorDen = 1; 125 double sourceOffset = 0; 126 double targetOffset = 0; 127 bool reciprocal = false; 128 129 ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) 130 : source(std::move(source)), target(std::move(target)), specialSource(), specialTarget() {} 131 }; 132 133 enum Convertibility { 134 RECIPROCAL, 135 CONVERTIBLE, 136 UNCONVERTIBLE, 137 }; 138 139 MeasureUnitImpl extractCompoundBaseUnit(const MeasureUnitImpl& source, 140 const ConversionRates& conversionRates, 141 UErrorCode& status); 142 143 /** 144 * Check if the convertibility between `source` and `target`. 145 * For example: 146 * `meter` and `foot` are `CONVERTIBLE`. 147 * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. 148 * `meter` and `pound` are `UNCONVERTIBLE`. 149 * 150 * NOTE: 151 * Only works with SINGLE and COMPOUND units. If one of the units is a 152 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 153 */ 154 Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, 155 const MeasureUnitImpl &target, 156 const ConversionRates &conversionRates, 157 UErrorCode &status); 158 159 /** 160 * Converts from a source `MeasureUnit` to a target `MeasureUnit`. 161 * 162 * NOTE: 163 * Only works with SINGLE and COMPOUND units. If one of the units is a 164 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 165 */ 166 class U_I18N_API_CLASS UnitsConverter : public UMemory { 167 public: 168 /** 169 * Constructor of `UnitConverter`. 170 * NOTE: 171 * - source and target must be under the same category 172 * - e.g. meter to mile --> both of them are length units. 173 * NOTE: 174 * This constructor creates an instance of `ConversionRates` internally. 175 * 176 * @param sourceIdentifier represents the source unit identifier. 177 * @param targetIdentifier represents the target unit identifier. 178 * @param status 179 */ 180 U_I18N_API UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, 181 UErrorCode &status); 182 183 /** 184 * Constructor of `UnitConverter`. 185 * NOTE: 186 * - source and target must be under the same category 187 * - e.g. meter to mile --> both of them are length units. 188 * 189 * @param source represents the source unit. 190 * @param target represents the target unit. 191 * @param ratesInfo Contains all the needed conversion rates. 192 * @param status 193 */ 194 U_I18N_API UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, 195 const ConversionRates &ratesInfo, UErrorCode &status); 196 197 /** 198 * Compares two single units and returns 1 if the first one is greater, -1 if the second 199 * one is greater and 0 if they are equal. 200 * 201 * NOTE: 202 * Compares only single units that are convertible. 203 */ 204 static int32_t compareTwoUnits(const MeasureUnitImpl &firstUnit, const MeasureUnitImpl &SecondUnit, 205 const ConversionRates &ratesInfo, UErrorCode &status); 206 207 /** 208 * Convert a measurement expressed in the source unit to a measurement 209 * expressed in the target unit. 210 * 211 * @param inputValue the value to be converted. 212 * @return the converted value. 213 */ 214 U_I18N_API double convert(double inputValue) const; 215 216 /** 217 * The inverse of convert(): convert a measurement expressed in the target 218 * unit to a measurement expressed in the source unit. 219 * 220 * @param inputValue the value to be converted. 221 * @return the converted value. 222 */ 223 U_I18N_API double convertInverse(double inputValue) const; 224 225 U_I18N_API ConversionInfo getConversionInfo() const; 226 227 private: 228 ConversionRate conversionRate_; 229 230 /** 231 * Initialises the object. 232 */ 233 void init(const ConversionRates &ratesInfo, UErrorCode &status); 234 235 /** 236 * Convert from what should be discrete scale values for a particular unit like beaufort 237 * to a corresponding value in the base unit (which can have any decimal value, like meters/sec). 238 * This can handle different scales, specified by minBaseForScaleValues[]. 239 */ 240 double scaleToBase(double scaleValue, double minBaseForScaleValues[], int scaleMax) const; 241 242 /** 243 * Convert from a value in the base unit (which can have any decimal value, like meters/sec) to a corresponding 244 * discrete value in a scale (like beaufort), where each scale value represents a range of base values. 245 * This can handle different scales, specified by minBaseForScaleValues[]. 246 */ 247 double baseToScale(double baseValue, double minBaseForScaleValues[], int scaleMax) const; 248 249 }; 250 251 } // namespace units 252 U_NAMESPACE_END 253 254 #endif //__UNITS_CONVERTER_H__ 255 256 #endif /* #if !UCONFIG_NO_FORMATTING */