units_complexconverter.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // © 2020 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. #include "unicode/utypes.h"
  4. #if !UCONFIG_NO_FORMATTING
  5. #include <cmath>
  6. #include "cmemory.h"
  7. #include "number_decimalquantity.h"
  8. #include "number_roundingutils.h"
  9. #include "putilimp.h"
  10. #include "uarrsort.h"
  11. #include "uassert.h"
  12. #include "unicode/fmtable.h"
  13. #include "unicode/localpointer.h"
  14. #include "unicode/measunit.h"
  15. #include "unicode/measure.h"
  16. #include "units_complexconverter.h"
  17. #include "units_converter.h"
  18. U_NAMESPACE_BEGIN
  19. namespace units {
  20. ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &targetUnit,
  21. const ConversionRates &ratesInfo, UErrorCode &status)
  22. : units_(targetUnit.extractIndividualUnitsWithIndices(status)) {
  23. if (U_FAILURE(status)) {
  24. return;
  25. }
  26. U_ASSERT(units_.length() != 0);
  27. // Just borrowing a pointer to the instance
  28. MeasureUnitImpl *biggestUnit = &units_[0]->unitImpl;
  29. for (int32_t i = 1; i < units_.length(); i++) {
  30. if (UnitsConverter::compareTwoUnits(units_[i]->unitImpl, *biggestUnit, ratesInfo, status) > 0 &&
  31. U_SUCCESS(status)) {
  32. biggestUnit = &units_[i]->unitImpl;
  33. }
  34. if (U_FAILURE(status)) {
  35. return;
  36. }
  37. }
  38. this->init(*biggestUnit, ratesInfo, status);
  39. }
  40. ComplexUnitsConverter::ComplexUnitsConverter(StringPiece inputUnitIdentifier,
  41. StringPiece outputUnitsIdentifier, UErrorCode &status) {
  42. if (U_FAILURE(status)) {
  43. return;
  44. }
  45. MeasureUnitImpl inputUnit = MeasureUnitImpl::forIdentifier(inputUnitIdentifier, status);
  46. MeasureUnitImpl outputUnits = MeasureUnitImpl::forIdentifier(outputUnitsIdentifier, status);
  47. this->units_ = outputUnits.extractIndividualUnitsWithIndices(status);
  48. U_ASSERT(units_.length() != 0);
  49. this->init(inputUnit, ConversionRates(status), status);
  50. }
  51. ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
  52. const MeasureUnitImpl &outputUnits,
  53. const ConversionRates &ratesInfo, UErrorCode &status)
  54. : units_(outputUnits.extractIndividualUnitsWithIndices(status)) {
  55. if (U_FAILURE(status)) {
  56. return;
  57. }
  58. U_ASSERT(units_.length() != 0);
  59. this->init(inputUnit, ratesInfo, status);
  60. }
  61. void ComplexUnitsConverter::init(const MeasureUnitImpl &inputUnit,
  62. const ConversionRates &ratesInfo,
  63. UErrorCode &status) {
  64. // Sorts units in descending order. Therefore, we return -1 if
  65. // the left is bigger than right and so on.
  66. auto descendingCompareUnits = [](const void *context, const void *left, const void *right) {
  67. UErrorCode status = U_ZERO_ERROR;
  68. const auto *leftPointer = static_cast<const MeasureUnitImplWithIndex *const *>(left);
  69. const auto *rightPointer = static_cast<const MeasureUnitImplWithIndex *const *>(right);
  70. // Multiply by -1 to sort in descending order
  71. return (-1) * UnitsConverter::compareTwoUnits((**leftPointer).unitImpl, //
  72. (**rightPointer).unitImpl, //
  73. *static_cast<const ConversionRates *>(context), //
  74. status);
  75. };
  76. uprv_sortArray(units_.getAlias(), //
  77. units_.length(), //
  78. sizeof units_[0], /* NOTE: we have already asserted that the units_ is not empty.*/ //
  79. descendingCompareUnits, //
  80. &ratesInfo, //
  81. false, //
  82. &status //
  83. );
  84. // In case the `outputUnits` are `UMEASURE_UNIT_MIXED` such as `foot+inch`. In this case we need more
  85. // converters to convert from the `inputUnit` to the first unit in the `outputUnits`. Then, a
  86. // converter from the first unit in the `outputUnits` to the second unit and so on.
  87. // For Example:
  88. // - inputUnit is `meter`
  89. // - outputUnits is `foot+inch`
  90. // - Therefore, we need to have two converters:
  91. // 1. a converter from `meter` to `foot`
  92. // 2. a converter from `foot` to `inch`
  93. // - Therefore, if the input is `2 meter`:
  94. // 1. convert `meter` to `foot` --> 2 meter to 6.56168 feet
  95. // 2. convert the residual of 6.56168 feet (0.56168) to inches, which will be (6.74016
  96. // inches)
  97. // 3. then, the final result will be (6 feet and 6.74016 inches)
  98. for (int i = 0, n = units_.length(); i < n; i++) {
  99. if (i == 0) { // first element
  100. unitsConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, units_[i]->unitImpl,
  101. ratesInfo, status);
  102. } else {
  103. unitsConverters_.emplaceBackAndCheckErrorCode(status, units_[i - 1]->unitImpl,
  104. units_[i]->unitImpl, ratesInfo, status);
  105. }
  106. if (U_FAILURE(status)) {
  107. return;
  108. }
  109. }
  110. }
  111. UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) const {
  112. U_ASSERT(unitsConverters_.length() > 0);
  113. // First converter converts to the biggest quantity.
  114. double newQuantity = unitsConverters_[0]->convert(quantity);
  115. return newQuantity >= limit;
  116. }
  117. MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity,
  118. icu::number::impl::RoundingImpl *rounder,
  119. UErrorCode &status) const {
  120. // TODO: return an error for "foot-and-foot"?
  121. MaybeStackVector<Measure> result;
  122. int sign = 1;
  123. if (quantity < 0 && unitsConverters_.length() > 1) {
  124. quantity *= -1;
  125. sign = -1;
  126. }
  127. // For N converters:
  128. // - the first converter converts from the input unit to the largest unit,
  129. // - the following N-2 converters convert to bigger units for which we want integers,
  130. // - the Nth converter (index N-1) converts to the smallest unit, for which
  131. // we keep a double.
  132. MaybeStackArray<int64_t, 5> intValues(unitsConverters_.length() - 1, status);
  133. if (U_FAILURE(status)) {
  134. return result;
  135. }
  136. uprv_memset(intValues.getAlias(), 0, (unitsConverters_.length() - 1) * sizeof(int64_t));
  137. for (int i = 0, n = unitsConverters_.length(); i < n; ++i) {
  138. quantity = (*unitsConverters_[i]).convert(quantity);
  139. if (i < n - 1) {
  140. // If quantity is at the limits of double's precision from an
  141. // integer value, we take that integer value.
  142. int64_t flooredQuantity;
  143. if (uprv_isNaN(quantity)) {
  144. // With clang on Linux: floor does not support NaN, resulting in
  145. // a giant negative number. For now, we produce "0 feet, NaN
  146. // inches". TODO(icu-units#131): revisit desired output.
  147. flooredQuantity = 0;
  148. } else {
  149. flooredQuantity = static_cast<int64_t>(floor(quantity * (1 + DBL_EPSILON)));
  150. }
  151. intValues[i] = flooredQuantity;
  152. // Keep the residual of the quantity.
  153. // For example: `3.6 feet`, keep only `0.6 feet`
  154. double remainder = quantity - flooredQuantity;
  155. if (remainder < 0) {
  156. // Because we nudged flooredQuantity up by eps, remainder may be
  157. // negative: we must treat such a remainder as zero.
  158. quantity = 0;
  159. } else {
  160. quantity = remainder;
  161. }
  162. }
  163. }
  164. applyRounder(intValues, quantity, rounder, status);
  165. // Initialize empty result. We use a MaybeStackArray directly so we can
  166. // assign pointers - for this privilege we have to take care of cleanup.
  167. MaybeStackArray<Measure *, 4> tmpResult(unitsConverters_.length(), status);
  168. if (U_FAILURE(status)) {
  169. return result;
  170. }
  171. // Package values into temporary Measure instances in tmpResult:
  172. for (int i = 0, n = unitsConverters_.length(); i < n; ++i) {
  173. if (i < n - 1) {
  174. Formattable formattableQuantity(intValues[i] * sign);
  175. // Measure takes ownership of the MeasureUnit*
  176. MeasureUnit *type = new MeasureUnit(units_[i]->unitImpl.copy(status).build(status));
  177. tmpResult[units_[i]->index] = new Measure(formattableQuantity, type, status);
  178. } else { // LAST ELEMENT
  179. Formattable formattableQuantity(quantity * sign);
  180. // Measure takes ownership of the MeasureUnit*
  181. MeasureUnit *type = new MeasureUnit(units_[i]->unitImpl.copy(status).build(status));
  182. tmpResult[units_[i]->index] = new Measure(formattableQuantity, type, status);
  183. }
  184. }
  185. // Transfer values into result and return:
  186. for(int32_t i = 0, n = unitsConverters_.length(); i < n; ++i) {
  187. U_ASSERT(tmpResult[i] != nullptr);
  188. result.emplaceBackAndCheckErrorCode(status, *tmpResult[i]);
  189. delete tmpResult[i];
  190. }
  191. return result;
  192. }
  193. void ComplexUnitsConverter::applyRounder(MaybeStackArray<int64_t, 5> &intValues, double &quantity,
  194. icu::number::impl::RoundingImpl *rounder,
  195. UErrorCode &status) const {
  196. if (uprv_isInfinite(quantity) || uprv_isNaN(quantity)) {
  197. // Inf and NaN can't be rounded, and calculating `carry` below is known
  198. // to fail on Gentoo on HPPA and OpenSUSE on riscv64. Nothing to do.
  199. return;
  200. }
  201. if (rounder == nullptr) {
  202. // Nothing to do for the quantity.
  203. return;
  204. }
  205. number::impl::DecimalQuantity decimalQuantity;
  206. decimalQuantity.setToDouble(quantity);
  207. rounder->apply(decimalQuantity, status);
  208. if (U_FAILURE(status)) {
  209. return;
  210. }
  211. quantity = decimalQuantity.toDouble();
  212. int32_t lastIndex = unitsConverters_.length() - 1;
  213. if (lastIndex == 0) {
  214. // Only one element, no need to bubble up the carry
  215. return;
  216. }
  217. // Check if there's a carry, and bubble it back up the resulting intValues.
  218. int64_t carry = static_cast<int64_t>(floor(unitsConverters_[lastIndex]->convertInverse(quantity) * (1 + DBL_EPSILON)));
  219. if (carry <= 0) {
  220. return;
  221. }
  222. quantity -= unitsConverters_[lastIndex]->convert(static_cast<double>(carry));
  223. intValues[lastIndex - 1] += carry;
  224. // We don't use the first converter: that one is for the input unit
  225. for (int32_t j = lastIndex - 1; j > 0; j--) {
  226. carry = static_cast<int64_t>(floor(unitsConverters_[j]->convertInverse(static_cast<double>(intValues[j])) * (1 + DBL_EPSILON)));
  227. if (carry <= 0) {
  228. return;
  229. }
  230. intValues[j] -= static_cast<int64_t>(round(unitsConverters_[j]->convert(static_cast<double>(carry))));
  231. intValues[j - 1] += carry;
  232. }
  233. }
  234. } // namespace units
  235. U_NAMESPACE_END
  236. #endif /* #if !UCONFIG_NO_FORMATTING */