number_scientific.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. #include <cstdlib>
  6. #include "number_scientific.h"
  7. #include "number_utils.h"
  8. #include "formatted_string_builder.h"
  9. #include "unicode/unum.h"
  10. #include "number_microprops.h"
  11. using namespace icu;
  12. using namespace icu::number;
  13. using namespace icu::number::impl;
  14. // NOTE: The object lifecycle of ScientificModifier and ScientificHandler differ greatly in Java and C++.
  15. //
  16. // During formatting, we need to provide an object with state (the exponent) as the inner modifier.
  17. //
  18. // In Java, where the priority is put on reducing object creations, the unsafe code path re-uses the
  19. // ScientificHandler as a ScientificModifier, and the safe code path pre-computes 25 ScientificModifier
  20. // instances. This scheme reduces the number of object creations by 1 in both safe and unsafe.
  21. //
  22. // In C++, MicroProps provides a pre-allocated ScientificModifier, and ScientificHandler simply populates
  23. // the state (the exponent) into that ScientificModifier. There is no difference between safe and unsafe.
  24. ScientificModifier::ScientificModifier() : fExponent(0), fHandler(nullptr) {}
  25. void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) {
  26. // ScientificModifier should be set only once.
  27. U_ASSERT(fHandler == nullptr);
  28. fExponent = exponent;
  29. fHandler = handler;
  30. }
  31. int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex,
  32. UErrorCode &status) const {
  33. // FIXME: Localized exponent separator location.
  34. int i = rightIndex;
  35. // Append the exponent separator and sign
  36. i += output.insert(
  37. i,
  38. fHandler->fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kExponentialSymbol),
  39. {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SYMBOL_FIELD},
  40. status);
  41. if (fExponent < 0 && fHandler->fSettings.fExponentSignDisplay != UNUM_SIGN_NEVER) {
  42. i += output.insert(
  43. i,
  44. fHandler->fSymbols
  45. ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kMinusSignSymbol),
  46. {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
  47. status);
  48. } else if (fExponent >= 0 && fHandler->fSettings.fExponentSignDisplay == UNUM_SIGN_ALWAYS) {
  49. i += output.insert(
  50. i,
  51. fHandler->fSymbols
  52. ->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol),
  53. {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_SIGN_FIELD},
  54. status);
  55. }
  56. // Append the exponent digits (using a simple inline algorithm)
  57. int32_t disp = std::abs(fExponent);
  58. for (int j = 0; j < fHandler->fSettings.fMinExponentDigits || disp > 0; j++, disp /= 10) {
  59. auto d = static_cast<int8_t>(disp % 10);
  60. i += utils::insertDigitFromSymbols(
  61. output,
  62. i - j,
  63. d,
  64. *fHandler->fSymbols,
  65. {UFIELD_CATEGORY_NUMBER, UNUM_EXPONENT_FIELD},
  66. status);
  67. }
  68. return i - rightIndex;
  69. }
  70. int32_t ScientificModifier::getPrefixLength() const {
  71. // TODO: Localized exponent separator location.
  72. return 0;
  73. }
  74. int32_t ScientificModifier::getCodePointCount() const {
  75. // NOTE: This method is only called one place, NumberRangeFormatterImpl.
  76. // The call site only cares about != 0 and != 1.
  77. // Return a very large value so that if this method is used elsewhere, we should notice.
  78. return 999;
  79. }
  80. bool ScientificModifier::isStrong() const {
  81. // Scientific is always strong
  82. return true;
  83. }
  84. bool ScientificModifier::containsField(Field field) const {
  85. (void)field;
  86. // This method is not used for inner modifiers.
  87. UPRV_UNREACHABLE_EXIT;
  88. }
  89. void ScientificModifier::getParameters(Parameters& output) const {
  90. // Not part of any plural sets
  91. output.obj = nullptr;
  92. }
  93. bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const {
  94. auto* _other = dynamic_cast<const ScientificModifier*>(&other);
  95. if (_other == nullptr) {
  96. return false;
  97. }
  98. // TODO: Check for locale symbols and settings as well? Could be less efficient.
  99. return fExponent == _other->fExponent;
  100. }
  101. // Note: Visual Studio does not compile this function without full name space. Why?
  102. icu::number::impl::ScientificHandler::ScientificHandler(const Notation *notation, const DecimalFormatSymbols *symbols,
  103. const MicroPropsGenerator *parent) :
  104. fSettings(notation->fUnion.scientific), fSymbols(symbols), fParent(parent) {}
  105. void ScientificHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
  106. UErrorCode &status) const {
  107. fParent->processQuantity(quantity, micros, status);
  108. if (U_FAILURE(status)) { return; }
  109. // Do not apply scientific notation to special doubles
  110. if (quantity.isInfinite() || quantity.isNaN()) {
  111. micros.modInner = &micros.helpers.emptyStrongModifier;
  112. return;
  113. }
  114. // Treat zero as if it had magnitude 0
  115. int32_t exponent;
  116. if (quantity.isZeroish()) {
  117. if (fSettings.fRequireMinInt && micros.rounder.isSignificantDigits()) {
  118. // Show "00.000E0" on pattern "00.000E0"
  119. micros.rounder.apply(quantity, fSettings.fEngineeringInterval, status);
  120. exponent = 0;
  121. } else {
  122. micros.rounder.apply(quantity, status);
  123. exponent = 0;
  124. }
  125. } else {
  126. exponent = -micros.rounder.chooseMultiplierAndApply(quantity, *this, status);
  127. }
  128. // Use MicroProps's helper ScientificModifier and save it as the modInner.
  129. ScientificModifier &mod = micros.helpers.scientificModifier;
  130. mod.set(exponent, this);
  131. micros.modInner = &mod;
  132. // Change the exponent only after we select appropriate plural form
  133. // for formatting purposes so that we preserve expected formatted
  134. // string behavior.
  135. quantity.adjustExponent(exponent);
  136. // We already performed rounding. Do not perform it again.
  137. micros.rounder = RoundingImpl::passThrough();
  138. }
  139. int32_t ScientificHandler::getMultiplier(int32_t magnitude) const {
  140. int32_t interval = fSettings.fEngineeringInterval;
  141. int32_t digitsShown;
  142. if (fSettings.fRequireMinInt) {
  143. // For patterns like "000.00E0" and ".00E0"
  144. digitsShown = interval;
  145. } else if (interval <= 1) {
  146. // For patterns like "0.00E0" and "@@@E0"
  147. digitsShown = 1;
  148. } else {
  149. // For patterns like "##0.00"
  150. digitsShown = ((magnitude % interval + interval) % interval) + 1;
  151. }
  152. return digitsShown - magnitude - 1;
  153. }
  154. #endif /* #if !UCONFIG_NO_FORMATTING */