ICU-13495 Optimizing chooseMultiplierAndApply method implementation.
X-SVN-Rev: 40732
This commit is contained in:
parent
6d9ed3698b
commit
5c054df085
@ -251,28 +251,39 @@ void Rounder::setLocaleData(const CurrencyUnit ¤cy, UErrorCode &status) {
|
||||
int32_t
|
||||
Rounder::chooseMultiplierAndApply(impl::DecimalQuantity &input, const impl::MultiplierProducer &producer,
|
||||
UErrorCode &status) {
|
||||
// TODO: Make a better and more efficient implementation.
|
||||
// TODO: Avoid the object creation here.
|
||||
DecimalQuantity copy(input);
|
||||
|
||||
// Do not call this method with zero.
|
||||
U_ASSERT(!input.isZero());
|
||||
int32_t magnitude = input.getMagnitude();
|
||||
int32_t multiplier = producer.getMultiplier(magnitude);
|
||||
|
||||
// Perform the first attempt at rounding.
|
||||
int magnitude = input.getMagnitude();
|
||||
int multiplier = producer.getMultiplier(magnitude);
|
||||
input.adjustMagnitude(multiplier);
|
||||
apply(input, status);
|
||||
|
||||
// If the number turned to zero when rounding, do not re-attempt the rounding.
|
||||
if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) {
|
||||
magnitude += 1;
|
||||
input = copy;
|
||||
multiplier = producer.getMultiplier(magnitude);
|
||||
input.adjustMagnitude(multiplier);
|
||||
U_ASSERT(input.getMagnitude() == magnitude + multiplier - 1);
|
||||
apply(input, status);
|
||||
U_ASSERT(input.getMagnitude() == magnitude + multiplier);
|
||||
// If the number rounded to zero, exit.
|
||||
if (input.isZero() || U_FAILURE(status)) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
// If the new magnitude after rounding is the same as it was before rounding, then we are done.
|
||||
// This case applies to most numbers.
|
||||
if (input.getMagnitude() == magnitude + multiplier) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
// If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
|
||||
// The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
|
||||
// we do not need to make any more adjustments.
|
||||
int _multiplier = producer.getMultiplier(magnitude + 1);
|
||||
if (multiplier == _multiplier) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
// We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
|
||||
// Fix the magnitude and re-apply the rounding strategy.
|
||||
input.adjustMagnitude(_multiplier - multiplier);
|
||||
apply(input, status);
|
||||
return _multiplier;
|
||||
}
|
||||
|
||||
/** This is the method that contains the actual rounding logic. */
|
||||
|
@ -230,10 +230,21 @@ class U_I18N_API MicroPropsGenerator {
|
||||
virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros, UErrorCode& status) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface used by compact notation and scientific notation to choose a multiplier while rounding.
|
||||
*/
|
||||
class MultiplierProducer {
|
||||
public:
|
||||
virtual ~MultiplierProducer() = default;
|
||||
|
||||
/**
|
||||
* Maps a magnitude to a multiplier in powers of ten. For example, in compact notation in English, a magnitude of 5
|
||||
* (e.g., 100,000) should return a multiplier of -3, since the number is displayed in thousands.
|
||||
*
|
||||
* @param magnitude
|
||||
* The power of ten of the input number.
|
||||
* @return The shift in powers of ten.
|
||||
*/
|
||||
virtual int32_t getMultiplier(int32_t magnitude) const = 0;
|
||||
};
|
||||
|
||||
|
@ -836,6 +836,20 @@ class U_I18N_API Rounder : public UMemory {
|
||||
/** Version of {@link #apply} that obeys minInt constraints. Used for scientific notation compatibility mode. */
|
||||
void apply(impl::DecimalQuantity &value, int32_t minInt, UErrorCode status);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
@ -6,5 +6,13 @@ package com.ibm.icu.impl.number;
|
||||
* An interface used by compact notation and scientific notation to choose a multiplier while rounding.
|
||||
*/
|
||||
public interface MultiplierProducer {
|
||||
/**
|
||||
* Maps a magnitude to a multiplier in powers of ten. For example, in compact notation in English, a magnitude of 5
|
||||
* (e.g., 100,000) should return a multiplier of -3, since the number is displayed in thousands.
|
||||
*
|
||||
* @param magnitude
|
||||
* The power of ten of the input number.
|
||||
* @return The shift in powers of ten.
|
||||
*/
|
||||
int getMultiplier(int magnitude);
|
||||
}
|
||||
|
@ -485,29 +485,54 @@ public abstract class Rounder implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) {
|
||||
// TODO: Make a better and more efficient implementation.
|
||||
// TODO: Avoid the object creation here.
|
||||
DecimalQuantity copy = input.createCopy();
|
||||
|
||||
// Do not call this method with zero.
|
||||
assert !input.isZero();
|
||||
|
||||
// Perform the first attempt at rounding.
|
||||
int magnitude = input.getMagnitude();
|
||||
int multiplier = producer.getMultiplier(magnitude);
|
||||
input.adjustMagnitude(multiplier);
|
||||
apply(input);
|
||||
|
||||
// If the number turned to zero when rounding, do not re-attempt the rounding.
|
||||
if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) {
|
||||
magnitude += 1;
|
||||
input.copyFrom(copy);
|
||||
multiplier = producer.getMultiplier(magnitude);
|
||||
input.adjustMagnitude(multiplier);
|
||||
assert input.getMagnitude() == magnitude + multiplier - 1;
|
||||
apply(input);
|
||||
assert input.getMagnitude() == magnitude + multiplier;
|
||||
// If the number rounded to zero, exit.
|
||||
if (input.isZero()) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
// If the new magnitude after rounding is the same as it was before rounding, then we are done.
|
||||
// This case applies to most numbers.
|
||||
if (input.getMagnitude() == magnitude + multiplier) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
// If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000:
|
||||
// The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't,
|
||||
// we do not need to make any more adjustments.
|
||||
int _multiplier = producer.getMultiplier(magnitude + 1);
|
||||
if (multiplier == _multiplier) {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
// We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000".
|
||||
// Fix the magnitude and re-apply the rounding strategy.
|
||||
input.adjustMagnitude(_multiplier - multiplier);
|
||||
apply(input);
|
||||
return _multiplier;
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
Loading…
Reference in New Issue
Block a user