You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.5 KiB
218 lines
7.5 KiB
// © 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;
|
|
|
|
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, Usage, 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 */
|