number_roundingutils.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // © 2017 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. #ifndef __NUMBER_ROUNDINGUTILS_H__
  6. #define __NUMBER_ROUNDINGUTILS_H__
  7. #include "number_types.h"
  8. U_NAMESPACE_BEGIN
  9. namespace number {
  10. namespace impl {
  11. namespace roundingutils {
  12. enum Section {
  13. SECTION_LOWER_EDGE = -1,
  14. SECTION_UPPER_EDGE = -2,
  15. SECTION_LOWER = 1,
  16. SECTION_MIDPOINT = 2,
  17. SECTION_UPPER = 3
  18. };
  19. /**
  20. * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining
  21. * whether the value should be rounded toward infinity or toward zero.
  22. *
  23. * <p>The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK
  24. * showed that ints were demonstrably faster than enums in switch statements.
  25. *
  26. * @param isEven Whether the digit immediately before the rounding magnitude is even.
  27. * @param isNegative Whether the quantity is negative.
  28. * @param section Whether the part of the quantity to the right of the rounding magnitude is
  29. * exactly halfway between two digits, whether it is in the lower part (closer to zero), or
  30. * whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link
  31. * #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}.
  32. * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via
  33. * {@link RoundingMode#ordinal}.
  34. * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary.
  35. * @return true if the number should be rounded toward zero; false if it should be rounded toward
  36. * infinity.
  37. */
  38. inline bool
  39. getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
  40. UErrorCode &status) {
  41. switch (roundingMode) {
  42. case RoundingMode::UNUM_ROUND_UP:
  43. // round away from zero
  44. return false;
  45. case RoundingMode::UNUM_ROUND_DOWN:
  46. // round toward zero
  47. return true;
  48. case RoundingMode::UNUM_ROUND_CEILING:
  49. // round toward positive infinity
  50. return isNegative;
  51. case RoundingMode::UNUM_ROUND_FLOOR:
  52. // round toward negative infinity
  53. return !isNegative;
  54. case RoundingMode::UNUM_ROUND_HALFUP:
  55. switch (section) {
  56. case SECTION_MIDPOINT:
  57. return false;
  58. case SECTION_LOWER:
  59. return true;
  60. case SECTION_UPPER:
  61. return false;
  62. default:
  63. break;
  64. }
  65. break;
  66. case RoundingMode::UNUM_ROUND_HALFDOWN:
  67. switch (section) {
  68. case SECTION_MIDPOINT:
  69. return true;
  70. case SECTION_LOWER:
  71. return true;
  72. case SECTION_UPPER:
  73. return false;
  74. default:
  75. break;
  76. }
  77. break;
  78. case RoundingMode::UNUM_ROUND_HALFEVEN:
  79. switch (section) {
  80. case SECTION_MIDPOINT:
  81. return isEven;
  82. case SECTION_LOWER:
  83. return true;
  84. case SECTION_UPPER:
  85. return false;
  86. default:
  87. break;
  88. }
  89. break;
  90. default:
  91. break;
  92. }
  93. status = U_FORMAT_INEXACT_ERROR;
  94. return false;
  95. }
  96. /**
  97. * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding
  98. * boundary is the point at which a number switches from being rounded down to being rounded up.
  99. * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at
  100. * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR,
  101. * the rounding boundary is at the "edge", and this function would return false.
  102. *
  103. * @param roundingMode The integer version of the {@link RoundingMode}.
  104. * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise.
  105. */
  106. inline bool roundsAtMidpoint(int roundingMode) {
  107. switch (roundingMode) {
  108. case RoundingMode::UNUM_ROUND_UP:
  109. case RoundingMode::UNUM_ROUND_DOWN:
  110. case RoundingMode::UNUM_ROUND_CEILING:
  111. case RoundingMode::UNUM_ROUND_FLOOR:
  112. return false;
  113. default:
  114. return true;
  115. }
  116. }
  117. /**
  118. * Computes the number of fraction digits in a double. Used for computing maxFrac for an increment.
  119. * Calls into the DoubleToStringConverter library to do so.
  120. *
  121. * @param singleDigit An output parameter; set to a number if that is the
  122. * only digit in the double, or -1 if there is more than one digit.
  123. */
  124. digits_t doubleFractionLength(double input, int8_t* singleDigit);
  125. } // namespace roundingutils
  126. /**
  127. * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
  128. *
  129. * This class does not exist in Java: instead, the base Precision class is used.
  130. */
  131. class RoundingImpl {
  132. public:
  133. RoundingImpl() = default; // defaults to pass-through rounder
  134. RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
  135. const CurrencyUnit& currency, UErrorCode& status);
  136. static RoundingImpl passThrough();
  137. /** Required for ScientificFormatter */
  138. bool isSignificantDigits() const;
  139. /**
  140. * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
  141. * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
  142. *
  143. * <p>
  144. * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
  145. * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
  146. * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
  147. * change your multiplier to be -6, and you get 1.0E6, which is correct.
  148. *
  149. * @param input The quantity to process.
  150. * @param producer Function to call to return a multiplier based on a magnitude.
  151. * @return The number of orders of magnitude the input was adjusted by this method.
  152. */
  153. int32_t
  154. chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
  155. UErrorCode &status);
  156. void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
  157. /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
  158. void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
  159. private:
  160. Precision fPrecision;
  161. UNumberFormatRoundingMode fRoundingMode;
  162. bool fPassThrough = true; // default value
  163. };
  164. } // namespace impl
  165. } // namespace number
  166. U_NAMESPACE_END
  167. #endif //__NUMBER_ROUNDINGUTILS_H__
  168. #endif /* #if !UCONFIG_NO_FORMATTING */