tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

number_roundingutils.h (8462B)


      1 // © 2017 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 __NUMBER_ROUNDINGUTILS_H__
      8 #define __NUMBER_ROUNDINGUTILS_H__
      9 
     10 #include "number_types.h"
     11 #include "string_segment.h"
     12 
     13 U_NAMESPACE_BEGIN
     14 namespace number::impl {
     15 namespace roundingutils {
     16 
     17 enum Section {
     18    SECTION_LOWER_EDGE = -1,
     19    SECTION_UPPER_EDGE = -2,
     20    SECTION_LOWER = 1,
     21    SECTION_MIDPOINT = 2,
     22    SECTION_UPPER = 3
     23 };
     24 
     25 /**
     26 * Converts a rounding mode and metadata about the quantity being rounded to a boolean determining
     27 * whether the value should be rounded toward infinity or toward zero.
     28 *
     29 * <p>The parameters are of type int because benchmarks on an x86-64 processor against OpenJDK
     30 * showed that ints were demonstrably faster than enums in switch statements.
     31 *
     32 * @param isEven Whether the digit immediately before the rounding magnitude is even.
     33 * @param isNegative Whether the quantity is negative.
     34 * @param section Whether the part of the quantity to the right of the rounding magnitude is
     35 *     exactly halfway between two digits, whether it is in the lower part (closer to zero), or
     36 *     whether it is in the upper part (closer to infinity). See {@link #SECTION_LOWER}, {@link
     37 *     #SECTION_MIDPOINT}, and {@link #SECTION_UPPER}.
     38 * @param roundingMode The integer version of the {@link RoundingMode}, which you can get via
     39 *     {@link RoundingMode#ordinal}.
     40 * @param status Error code, set to U_FORMAT_INEXACT_ERROR if the rounding mode is kRoundUnnecessary.
     41 * @return true if the number should be rounded toward zero; false if it should be rounded toward
     42 *     infinity.
     43 */
     44 inline bool
     45 getRoundingDirection(bool isEven, bool isNegative, Section section, RoundingMode roundingMode,
     46                     UErrorCode &status) {
     47    if (U_FAILURE(status)) {
     48        return false;
     49    }
     50    switch (roundingMode) {
     51        case RoundingMode::UNUM_ROUND_UP:
     52            // round away from zero
     53            return false;
     54 
     55        case RoundingMode::UNUM_ROUND_DOWN:
     56            // round toward zero
     57            return true;
     58 
     59        case RoundingMode::UNUM_ROUND_CEILING:
     60            // round toward positive infinity
     61            return isNegative;
     62 
     63        case RoundingMode::UNUM_ROUND_FLOOR:
     64            // round toward negative infinity
     65            return !isNegative;
     66 
     67        case RoundingMode::UNUM_ROUND_HALFUP:
     68            switch (section) {
     69                case SECTION_MIDPOINT:
     70                    return false;
     71                case SECTION_LOWER:
     72                    return true;
     73                case SECTION_UPPER:
     74                    return false;
     75                default:
     76                    break;
     77            }
     78            break;
     79 
     80        case RoundingMode::UNUM_ROUND_HALFDOWN:
     81            switch (section) {
     82                case SECTION_MIDPOINT:
     83                    return true;
     84                case SECTION_LOWER:
     85                    return true;
     86                case SECTION_UPPER:
     87                    return false;
     88                default:
     89                    break;
     90            }
     91            break;
     92 
     93        case RoundingMode::UNUM_ROUND_HALFEVEN:
     94            switch (section) {
     95                case SECTION_MIDPOINT:
     96                    return isEven;
     97                case SECTION_LOWER:
     98                    return true;
     99                case SECTION_UPPER:
    100                    return false;
    101                default:
    102                    break;
    103            }
    104            break;
    105 
    106        case RoundingMode::UNUM_ROUND_HALF_ODD:
    107            switch (section) {
    108                case SECTION_MIDPOINT:
    109                    return !isEven;
    110                case SECTION_LOWER:
    111                    return true;
    112                case SECTION_UPPER:
    113                    return false;
    114                default:
    115                    break;
    116            }
    117            break;
    118 
    119        case RoundingMode::UNUM_ROUND_HALF_CEILING:
    120            switch (section) {
    121                case SECTION_MIDPOINT:
    122                    return isNegative;
    123                case SECTION_LOWER:
    124                    return true;
    125                case SECTION_UPPER:
    126                    return false;
    127                default:
    128                    break;
    129            }
    130            break;
    131 
    132        case RoundingMode::UNUM_ROUND_HALF_FLOOR:
    133            switch (section) {
    134                case SECTION_MIDPOINT:
    135                    return !isNegative;
    136                case SECTION_LOWER:
    137                    return true;
    138                case SECTION_UPPER:
    139                    return false;
    140                default:
    141                    break;
    142            }
    143            break;
    144 
    145        default:
    146            break;
    147    }
    148 
    149    status = U_FORMAT_INEXACT_ERROR;
    150    return false;
    151 }
    152 
    153 /**
    154 * Gets whether the given rounding mode's rounding boundary is at the midpoint. The rounding
    155 * boundary is the point at which a number switches from being rounded down to being rounded up.
    156 * For example, with rounding mode HALF_EVEN, HALF_UP, or HALF_DOWN, the rounding boundary is at
    157 * the midpoint, and this function would return true. However, for UP, DOWN, CEILING, and FLOOR,
    158 * the rounding boundary is at the "edge", and this function would return false.
    159 *
    160 * @param roundingMode The integer version of the {@link RoundingMode}.
    161 * @return true if rounding mode is HALF_EVEN, HALF_UP, or HALF_DOWN; false otherwise.
    162 */
    163 inline bool roundsAtMidpoint(int roundingMode) {
    164    switch (roundingMode) {
    165        case RoundingMode::UNUM_ROUND_UP:
    166        case RoundingMode::UNUM_ROUND_DOWN:
    167        case RoundingMode::UNUM_ROUND_CEILING:
    168        case RoundingMode::UNUM_ROUND_FLOOR:
    169            return false;
    170 
    171        default:
    172            return true;
    173    }
    174 }
    175 
    176 } // namespace roundingutils
    177 
    178 
    179 /**
    180 * Encapsulates a Precision and a RoundingMode and performs rounding on a DecimalQuantity.
    181 *
    182 * This class does not exist in Java: instead, the base Precision class is used.
    183 */
    184 class RoundingImpl {
    185  public:
    186    RoundingImpl() = default;  // defaults to pass-through rounder
    187 
    188    RoundingImpl(const Precision& precision, UNumberFormatRoundingMode roundingMode,
    189                 const CurrencyUnit& currency, UErrorCode& status);
    190 
    191    static RoundingImpl passThrough();
    192 
    193    /** Required for ScientificFormatter */
    194    bool isSignificantDigits() const;
    195 
    196    /**
    197     * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate multiplier (magnitude
    198     * adjustment), applies the adjustment, rounds, and returns the chosen multiplier.
    199     *
    200     * <p>
    201     * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier boundary, we
    202     * need to re-do the rounding. For example, to display 999,999 in Engineering notation with 2 sigfigs, first you
    203     * guess the multiplier to be -3. However, then you end up getting 1000E3, which is not the correct output. You then
    204     * change your multiplier to be -6, and you get 1.0E6, which is correct.
    205     *
    206     * @param input The quantity to process.
    207     * @param producer Function to call to return a multiplier based on a magnitude.
    208     * @return The number of orders of magnitude the input was adjusted by this method.
    209     */
    210    int32_t
    211    chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
    212                             UErrorCode &status);
    213 
    214    void apply(impl::DecimalQuantity &value, UErrorCode &status) const;
    215 
    216    /** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
    217    void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
    218 
    219  private:
    220    Precision fPrecision;
    221    UNumberFormatRoundingMode fRoundingMode;
    222    bool fPassThrough = true;  // default value
    223 
    224    // Permits access to fPrecision.
    225    friend class units::UnitsRouter;
    226 
    227    // Permits access to fPrecision.
    228    friend class UnitConversionHandler;
    229 };
    230 
    231 /**
    232 * Parses Precision-related skeleton strings without knowledge of MacroProps
    233 * - see blueprint_helpers::parseIncrementOption().
    234 *
    235 * Referencing MacroProps means needing to pull in the .o files that have the
    236 * destructors for the SymbolsWrapper, StringProp, and Scale classes.
    237 */
    238 void parseIncrementOption(const StringSegment &segment, Precision &outPrecision, UErrorCode &status);
    239 
    240 } // namespace number::impl
    241 U_NAMESPACE_END
    242 
    243 #endif //__NUMBER_ROUNDINGUTILS_H__
    244 
    245 #endif /* #if !UCONFIG_NO_FORMATTING */