257 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 2017 and later: Unicode, Inc. and others.
 | |
| // License & terms of use: http://www.unicode.org/copyright.html
 | |
| 
 | |
| #include "unicode/utypes.h"
 | |
| 
 | |
| #if !UCONFIG_NO_FORMATTING
 | |
| #ifndef __NUMBER_ROUNDINGUTILS_H__
 | |
| #define __NUMBER_ROUNDINGUTILS_H__
 | |
| 
 | |
| #include "number_types.h"
 | |
| #include "string_segment.h"
 | |
| 
 | |
| U_NAMESPACE_BEGIN
 | |
| namespace number {
 | |
| namespace impl {
 | |
| namespace roundingutils {
 | |
| 
 | |
| enum Section {
 | |
|     SECTION_LOWER_EDGE = -1,
 | |
|     SECTION_UPPER_EDGE = -2,
 | |
|     SECTION_LOWER = 1,
 | |
|     SECTION_MIDPOINT = 2,
 | |
|     SECTION_UPPER = 3
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining
 | |
|  * whether the value should be rounded toward infinity or toward zero.
 | |
|  *
 | |
|  * <p>The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK
 | |
|  * showed that ints were demonstrably faster than enums in switch statements.
 | |
|  *
 | |
|  * @param isEven Whether the digit immediately before the rounding magnitude is even.
 | |
|  * @param isNegative Whether the quantity is negative.
 | |
|  * @param section Whether the part of the quantity to the right of the rounding magnitude is
 | |
|  *     exactly halfway between two digits, whether it is in the lower part (closer to zero), or
 | |
|  *     whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link
 | |
|  *     #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}.
 | |
|  * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via
 | |
|  *     {@link RoundingMode#ordinal}.
 | |
|  * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary.
 | |
|  * @return true if the number should be rounded toward zero; false if it should be rounded toward
 | |
|  *     infinity.
 | |
|  */
 | |
| inline bool
 | |
| getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
 | |
|                      UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return false;
 | |
|     }
 | |
|     switch (roundingMode) {
 | |
|         case RoundingMode::UNUM_ROUND_UP:
 | |
|             // round away from zero
 | |
|             return false;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_DOWN:
 | |
|             // round toward zero
 | |
|             return true;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_CEILING:
 | |
|             // round toward positive infinity
 | |
|             return isNegative;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_FLOOR:
 | |
|             // round toward negative infinity
 | |
|             return !isNegative;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALFUP:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return false;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALFDOWN:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return true;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALFEVEN:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return isEven;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALF_ODD:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return !isEven;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALF_CEILING:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return isNegative;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         case RoundingMode::UNUM_ROUND_HALF_FLOOR:
 | |
|             switch (section) {
 | |
|                 case SECTION_MIDPOINT:
 | |
|                     return !isNegative;
 | |
|                 case SECTION_LOWER:
 | |
|                     return true;
 | |
|                 case SECTION_UPPER:
 | |
|                     return false;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     status = U_FORMAT_INEXACT_ERROR;
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding
 | |
|  * boundary is the point at which a number switches from being rounded down to being rounded up.
 | |
|  * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at
 | |
|  * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR,
 | |
|  * the rounding boundary is at the "edge", and this function would return false.
 | |
|  *
 | |
|  * @param roundingMode The integer version of the {@link RoundingMode}.
 | |
|  * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise.
 | |
|  */
 | |
| inline bool roundsAtMidpoint(int roundingMode) {
 | |
|     switch (roundingMode) {
 | |
|         case RoundingMode::UNUM_ROUND_UP:
 | |
|         case RoundingMode::UNUM_ROUND_DOWN:
 | |
|         case RoundingMode::UNUM_ROUND_CEILING:
 | |
|         case RoundingMode::UNUM_ROUND_FLOOR:
 | |
|             return false;
 | |
| 
 | |
|         default:
 | |
|             return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Computes the number of fraction digits in a double. Used for computing maxFrac for an increment.
 | |
|  * Calls into the DoubleToStringConverter library to do so.
 | |
|  *
 | |
|  * @param singleDigit An output parameter; set to a number if that is the
 | |
|  *        only digit in the double, or -1 if there is more than one digit.
 | |
|  */
 | |
| digits_t doubleFractionLength(double input, int8_t* singleDigit);
 | |
| 
 | |
| } // namespace roundingutils
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
 | |
|  *
 | |
|  * This class does not exist in Java: instead, the base Precision class is used.
 | |
|  */
 | |
| class RoundingImpl {
 | |
|   public:
 | |
|     RoundingImpl() = default;  // defaults to pass-through rounder
 | |
| 
 | |
|     RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
 | |
|                  const CurrencyUnit& currency, UErrorCode& status);
 | |
| 
 | |
|     static RoundingImpl passThrough();
 | |
| 
 | |
|     /** Required for ScientificFormatter */
 | |
|     bool isSignificantDigits() const;
 | |
| 
 | |
|     /**
 | |
|      * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
 | |
|      * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
 | |
|      *
 | |
|      * <p>
 | |
|      * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
 | |
|      * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
 | |
|      * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
 | |
|      * change your multiplier to be -6, and you get 1.0E6, which is correct.
 | |
|      *
 | |
|      * @param input The quantity to process.
 | |
|      * @param producer Function to call to return a multiplier based on a magnitude.
 | |
|      * @return The number of orders of magnitude the input was adjusted by this method.
 | |
|      */
 | |
|     int32_t
 | |
|     chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
 | |
|                              UErrorCode &status);
 | |
| 
 | |
|     void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
 | |
| 
 | |
|     /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
 | |
|     void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
 | |
| 
 | |
|   private:
 | |
|     Precision fPrecision;
 | |
|     UNumberFormatRoundingMode fRoundingMode;
 | |
|     bool fPassThrough = true;  // default value
 | |
| 
 | |
|     // Permits access to fPrecision.
 | |
|     friend class units::UnitsRouter;
 | |
| 
 | |
|     // Permits access to fPrecision.
 | |
|     friend class UnitConversionHandler;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Parses Precision-related skeleton strings without knowledge of MacroProps
 | |
|  * - see blueprint_helpers::parseIncrementOption().
 | |
|  *
 | |
|  * Referencing MacroProps means needing to pull in the .o files that have the
 | |
|  * destructors for the SymbolsWrapper, StringProp, and Scale classes.
 | |
|  */
 | |
| void parseIncrementOption(const StringSegment &segment, Precision &outPrecision, UErrorCode &status);
 | |
| 
 | |
| } // namespace impl
 | |
| } // namespace number
 | |
| U_NAMESPACE_END
 | |
| 
 | |
| #endif //__NUMBER_ROUNDINGUTILS_H__
 | |
| 
 | |
| #endif /* #if !UCONFIG_NO_FORMATTING */
 |