fork download
  1. #ifndef TypeHelpersH
  2. #define TypeHelpersH
  3.  
  4. #include <cstddef>
  5. #include <cstdint>
  6. #include <type_traits>
  7.  
  8. // std::is_fundamental [http://w...content-available-to-author-only...s.com/reference/type_traits/is_fundamental/]
  9. enum class ECFundamentalTypeTags {
  10. UNIDENTIFIED,
  11. BOOL,
  12. SIGNED_CHAR,
  13. UNSIGNED_CHAR,
  14. // Signedness of wchar_t is unspecified
  15. // [http://stackoverflow.com/questions/11953363/wchar-t-is-unsigned-or-signed]
  16. WCHAR,
  17.  
  18. //// 'char16_t' AND 'char32_t' SHOULD be a keywords since the C++11,
  19. //// BUT MS VS Community 2013 Upd 5 does NOT supports that
  20. //// AND specifys 'char16_t' AND 'char32_t' as a typdef aliases instead
  21. //// (so they are NOT presented here)
  22.  
  23. SIGNED_SHORT_INT,
  24. UNSIGNED_SHORT_INT,
  25. SIGNED_INT,
  26. UNSIGNED_INT,
  27. SIGNED_LONG_INT,
  28. UNSIGNED_LONG_INT,
  29. SIGNED_LONG_LONG_INT, // C++11
  30. UNSIGNED_LONG_LONG_INT, // C++11
  31. FLOAT,
  32. DOUBLE,
  33. LONG_DOUBLE,
  34. VOID_,
  35. NULLPTR // C++11 std::nullptr_t
  36. };
  37.  
  38. template <typename T, class TypeTags = ECFundamentalTypeTags>
  39. struct TypeTag {
  40. typedef TypeTags TypeTags_;
  41. static const auto TAG = TypeTags::UNIDENTIFIED;
  42. };
  43.  
  44. template <class TypeTags>
  45. struct TypeTag<bool, TypeTags> {
  46. typedef TypeTags TypeTags_;
  47. static const auto TAG = TypeTags::BOOL;
  48. };
  49.  
  50. template <class TypeTags>
  51. struct TypeTag<signed char, TypeTags> {
  52. typedef TypeTags TypeTags_;
  53. static const auto TAG = TypeTags::SIGNED_CHAR;
  54. };
  55.  
  56. template <class TypeTags>
  57. struct TypeTag<unsigned char, TypeTags> {
  58. typedef TypeTags TypeTags_;
  59. static const auto TAG = TypeTags::UNSIGNED_CHAR;
  60. };
  61.  
  62. template <class TypeTags>
  63. struct TypeTag<wchar_t, TypeTags> {
  64. typedef TypeTags TypeTags_;
  65. static const auto TAG = TypeTags::WCHAR;
  66. };
  67.  
  68. template <class TypeTags>
  69. struct TypeTag<signed short int, TypeTags> {
  70. typedef TypeTags TypeTags_;
  71. static const auto TAG = TypeTags::SIGNED_SHORT_INT;
  72. };
  73.  
  74. template <class TypeTags>
  75. struct TypeTag<unsigned short int, TypeTags> {
  76. typedef TypeTags TypeTags_;
  77. static const auto TAG = TypeTags::UNSIGNED_SHORT_INT;
  78. };
  79.  
  80. template <class TypeTags>
  81. struct TypeTag<signed int, TypeTags> {
  82. typedef TypeTags TypeTags_;
  83. static const auto TAG = TypeTags::SIGNED_INT;
  84. };
  85.  
  86. template <class TypeTags>
  87. struct TypeTag<unsigned int, TypeTags> {
  88. typedef TypeTags TypeTags_;
  89. static const auto TAG = TypeTags::UNSIGNED_INT;
  90. };
  91.  
  92. template <class TypeTags>
  93. struct TypeTag<signed long int, TypeTags> {
  94. typedef TypeTags TypeTags_;
  95. static const auto TAG = TypeTags::SIGNED_LONG_INT;
  96. };
  97.  
  98. template <class TypeTags>
  99. struct TypeTag<unsigned long int, TypeTags> {
  100. typedef TypeTags TypeTags_;
  101. static const auto TAG = TypeTags::UNSIGNED_LONG_INT;
  102. };
  103.  
  104. template <class TypeTags>
  105. struct TypeTag<signed long long int, TypeTags> {
  106. typedef TypeTags TypeTags_;
  107. static const auto TAG = TypeTags::SIGNED_LONG_LONG_INT;
  108. };
  109.  
  110. template <class TypeTags>
  111. struct TypeTag<unsigned long long int, TypeTags> {
  112. typedef TypeTags TypeTags_;
  113. static const auto TAG = TypeTags::UNSIGNED_LONG_LONG_INT;
  114. };
  115.  
  116. template <class TypeTags>
  117. struct TypeTag<float, TypeTags> {
  118. typedef TypeTags TypeTags_;
  119. static const auto TAG = TypeTags::FLOAT;
  120. };
  121.  
  122. template <class TypeTags>
  123. struct TypeTag<double, TypeTags> {
  124. typedef TypeTags TypeTags_;
  125. static const auto TAG = TypeTags::DOUBLE;
  126. };
  127.  
  128. template <class TypeTags>
  129. struct TypeTag<long double, TypeTags> {
  130. typedef TypeTags TypeTags_;
  131. static const auto TAG = TypeTags::LONG_DOUBLE;
  132. };
  133.  
  134. template <class TypeTags>
  135. struct TypeTag<void, TypeTags> {
  136. typedef TypeTags TypeTags_;
  137. static const auto TAG = TypeTags::VOID_;
  138. };
  139.  
  140. template <class TypeTags>
  141. struct TypeTag<std::nullptr_t, TypeTags> {
  142. typedef TypeTags TypeTags_;
  143. static const auto TAG = TypeTags::NULLPTR;
  144. };
  145.  
  146. #define TYPE_TAG(Object) TypeTag<typename std::decay<decltype(Object)>::type>::TAG
  147.  
  148. #endif // TypeHelpersH
  149.  
  150. #ifndef MacroUtilsH
  151. #define MacroUtilsH
  152.  
  153. namespace MacroUtils {
  154. #define M_S_(arg) #arg
  155. #define MAKE_STR_(arg) M_S_(arg)
  156. }
  157.  
  158. #endif // MacroUtilsH
  159.  
  160. #ifndef FuncUtilsH
  161. #define FuncUtilsH
  162.  
  163. //// [!] Version 1.012 [!]
  164.  
  165. #include <utility> // for 'std::move', 'std::forward'
  166. #include <type_traits> // for 'std::is_same'
  167.  
  168. /* Use following define guard to avoid multiple definitions:
  169.  
  170. #ifndef <func. / proc. name>_EXEC_MEMBER_<FUNC / PROC>_IF_PRESENT_
  171.   #define <func. / proc. name>_EXEC_MEMBER_<FUNC / PROC>_IF_PRESENT_
  172.   EXEC_MEMBER_<FUNC / PROC>_IF_PRESENT(<func. / proc. name>, <default val.>)
  173. #endif
  174.  
  175. Example:
  176.  
  177. #ifndef getHashIfKnown_EXEC_MEMBER_FUNC_IF_PRESENT_
  178.   #define getHashIfKnown_EXEC_MEMBER_FUNC_IF_PRESENT_
  179.   EXEC_MEMBER_FUNC_IF_PRESENT(getHashIfKnown, size_t())
  180. #endif
  181. */
  182.  
  183. /* [!] Does NOT works correctly with the 'std::reference_wrapper':
  184.   do NOT use 'std::ref' NOR 'std::cref' here [!]
  185. */
  186.  
  187. // If NOT exists - returns the 'DefaultValue',
  188. // which SHOULD be the same type as a decltype(*.FuncName())
  189. // Works with static/const/virtual funcs
  190. #define EXEC_MEMBER_FUNC_IF_PRESENT(FuncName, DefaultValue) namespace FuncName {\
  191.   template <typename TReturnType = decltype(DefaultValue), typename... TArgs>\
  192.   auto ExecIfPresent(TArgs&&... args) -> TReturnType {\
  193.   return std::move(DefaultValue);\
  194.   }\
  195.   \
  196.   template <class C, typename... TArgs>\
  197.   auto ExecIfPresent(C& obj, TArgs&&... args)\
  198.   /*Remove prototype from the overload resolution if such a callable (func./functor) is NOT exist*/\
  199.   -> decltype(obj.FuncName(std::forward<TArgs>(args)...)) /*NOT 'const C& obj' NOR 'C::FuncName()'*/\
  200.   {\
  201.   return std::move(obj.FuncName(std::forward<TArgs>(args)...));\
  202.   }\
  203. };
  204.  
  205. #endif // FuncUtilsH
  206.  
  207. #ifndef MemUtilsH
  208. #define MemUtilsH
  209.  
  210. //// [!] Version 1.003 [!]
  211.  
  212. namespace MemUtils {
  213. #ifndef AUTO_ADJUST_MEM
  214. #define AUTO_ADJUST_MEM(MemSize, Alignment) (MemSize) + (((MemSize) % (Alignment)) ?\
  215.   ((Alignment) - ((MemSize) % (Alignment)))\
  216.   : 0U)
  217. #endif
  218. };
  219.  
  220. #include <cstring>
  221. #include <cassert>
  222.  
  223. #endif // MemUtilsH
  224.  
  225. #ifndef MathUtilsH
  226. #define MathUtilsH
  227.  
  228. //// [!] Version 1.023 [!]
  229.  
  230. #include <cstring>
  231. #include <cassert>
  232. #include <cfloat> // for 'LDBL_DIG'
  233. #include <cstdio>
  234.  
  235. #include <math.h> // for 'modfl'
  236.  
  237. #include <mutex>
  238. #include <atomic>
  239. #include <limits>
  240. #include <algorithm>
  241.  
  242. // Abstract
  243. // [!] In C++14 many of this funcs can be 'constexpr' [!]
  244. class MathUtils {
  245.  
  246. public:
  247.  
  248. // Up to 10^18; returns zero if degree > 18
  249. // Complexity: constant - O(1)
  250. static long long int getTenPositiveDegree(const size_t degree = 0U) throw() {
  251. static const long long int TABLE[] =
  252. // 32 bit
  253. {1LL, 10LL, 100LL, 1000LL, 10000LL, 100000LL, 1000000LL, 10000000LL, 100000000LL, 1000000000LL,
  254. // 64 bit
  255. 10000000000LL, 100000000000LL, 1000000000000LL, 10000000000000LL, 100000000000000LL,
  256. 1000000000000000LL, 10000000000000000LL, 100000000000000000LL, 1000000000000000000LL};
  257. return degree < (sizeof(TABLE) / sizeof(*TABLE)) ? TABLE[degree] : 0LL; // 0 if too high
  258. }
  259.  
  260. // Returns 1 for (0, 21), 6 for (1, 360), 2 for (1, 120), 7 for (0, 7), 4 for (1, 40);
  261. // 0 for (0, 920) OR (3, 10345) OR (0, 0) OR (4, 10) etc
  262. // 'allBelowOrderDigitsIsZero' will be true for
  263. // (0, 7) OR (0, 20) OR (1, 130) OR (2, 12300) OR (0, 0)
  264. // Negative numbers are allowed; order starts from zero
  265. // (for number 3219 zero order digit is 9)
  266. // Complexity: constant - O(1)
  267. static size_t getDigitOfOrder(const size_t order, const long long int num,
  268. bool& allBelowOrderDigitsIsZero) throw()
  269. {
  270. const auto degK = getTenPositiveDegree(order);
  271. const auto shiftedVal = num / degK; // shifting to the speicified order, ignoring remnant
  272. allBelowOrderDigitsIsZero = num == (shiftedVal * degK); // shift back without adding remnant
  273. return static_cast<size_t>(std::abs(shiftedVal % 10LL)); // getting the exact digit
  274. }
  275.  
  276. };
  277.  
  278. #endif // MathUtilsH
  279.  
  280. #ifndef ConvertionUtilsH
  281. #define ConvertionUtilsH
  282.  
  283. //// [!] Version 1.114 [!]
  284.  
  285. #include <cfloat> // for 'LDBL_DIG'
  286. #include <cerrno> // for 'ERANGE'
  287. #include <climits> // for 'LONG_MIN'
  288.  
  289. EXEC_MEMBER_FUNC_IF_PRESENT(truncated, false) // special for limited size storage
  290.  
  291. namespace ConvertionUtils {
  292.  
  293. // A wrapper to the standart 'strtol' with the extended error recognition
  294. // In case of error sets 'errMsg' to the static non-empty str.
  295. // ('strerror_s' can be used then to get a system specific error message (if exists))
  296. static bool strToL(long int& num, const char* const str,
  297. const char*& errMsg, const int base = 10) throw()
  298. {
  299. if (!str) {
  300. num = std::decay<decltype(num)>::type(); // reset
  301. errMsg = "invalid pointer";
  302. return false;
  303. }
  304. if (!*str) {
  305. num = std::decay<decltype(num)>::type(); // reset
  306. errMsg = "empty string";
  307. return false;
  308. }
  309. char* endPtr = nullptr;
  310. /* If this variable is intended to be used for error checking after a std. C library function call,
  311.   it SHOULD be reset by the program to zero before the call
  312.   (since ANY previous call to a library function may have altered its value)
  313.   */
  314. errno = 0; // clear error (NO library function sets its value back to zero once changed)
  315. num = strtol(str, &endPtr, base);
  316. // 'errno' CAN be set on error ('ERANGE'); 'endptr' SHOULD point to the str. terminator
  317. if (errno || *endPtr) { // C++11 requires 'errno' to be implemented in a per-thread basis
  318. if (ERANGE == errno) { // range error
  319. switch (num) {
  320. case LONG_MIN: errMsg = "too low value"; break;
  321. case LONG_MAX: errMsg = "too big value"; break;
  322. default: errMsg = "out of range";
  323. } // functions of the standard library may set 'errno' to ANY value
  324. } else errMsg = "invalid string"; // NOT a range error; some incorrect symb. is met probably
  325. num = std::decay<decltype(num)>::type();
  326. return false;
  327. }
  328. errMsg = "";
  329. return true;
  330. };
  331.  
  332. // In AMERICAN English, many students are taught NOT to use the word "and"
  333. // anywhere in the whole part of a number
  334. // "and" delimiter is used ALSO in Irish, Australian AND New Zealand English
  335. static const char* const ENG_GB_VERBAL_DELIMITER = "and";
  336. static const char DEFAULT_DELIMITER = ' ';
  337.  
  338. // Do NOT add other dialects of english e.g. Irish, Australian, New Zealand, Indian etc
  339. enum ELocale {
  340. L_RU_RU, // Russian Federation Russian
  341. L_EN_US, // United States English
  342. L_EN_GB, // United Kingdom English
  343. COUNT // USED ONLY to get the enum elems count
  344. };
  345.  
  346. struct LocaleSettings {
  347. LocaleSettings() = default;
  348. ~LocaleSettings() = default;
  349. LocaleSettings(const LocaleSettings&) = default;
  350. LocaleSettings& operator=(const LocaleSettings&) = default;
  351.  
  352. static const LocaleSettings DEFAULT_LOCALE_SETTINGS;
  353.  
  354. // Enables some language very specific rules for numbers spelling
  355. // (like pronouncing four-digit numbers in US & UK Eng.)
  356. bool verySpecific = false;
  357. bool positiveSign = false; // add positive sign [for positive nums]
  358. // Если целая часть равна нулю, то она может не читаться: 0.75 (.75) – point seventy five
  359. bool shortFormat = false; // skip mention zero int. / fract. part
  360. bool foldFraction = false; // try find repeated pattern & treat it
  361. ELocale locale = ELocale::L_EN_GB;
  362. size_t precison = size_t(LDBL_DIG); // max. digits count (<= 'LDBL_DIG')
  363. };
  364.  
  365. // TO DO:
  366. // - add ordinal numbers support
  367. // http://e...content-available-to-author-only...a.org/wiki/English_numerals#Ordinal_numbers
  368. // - add multiplicative adverbs support
  369. // http://e...content-available-to-author-only...a.org/wiki/English_numerals#Multiplicative_adverbs
  370. // - add long / short scale support
  371. // https://e...content-available-to-author-only...a.org/wiki/Long_and_short_scales
  372. // - add money support (writing a cheque (or check),
  373. // the number 100 is always written "one hundred", it is never "a hundred")
  374. // [enum: money, count, index, dates, temperatures -
  375. // http://e...content-available-to-author-only...a.org/wiki/English_numerals#Negative_numbers]
  376. // - add specialized numbers support
  377. // http://e...content-available-to-author-only...a.org/wiki/English_numerals#Specialized_numbers
  378. // - add special fractions support: 1/2 — a half [«one half»]; 1/3 — a / one third; 1/4: «one quarter»
  379. // ['HandleSpecificValues' - if 'verySpecific' is true]
  380. // - add 'double o' 'triple o' etc folding support [0.001 — 'nought point double о one']
  381.  
  382. /* NOTES on known, BUT does NOT supported features:
  383.   1) Ten thousand is RARELY called a myriad [do NOT used, as processing by triades]
  384.   2) Americans may pronounce four-digit numbers with non-zero tens AND/OR ones
  385.   as pairs of two-digit numbers without saying "hundred"
  386.   AND inserting "oh" for zero tens: "twenty-six fifty-nine" or "forty-one oh five"
  387.   (currently uses ANOTHER format: 9522 = 'ninety-five hundred twenty-two')
  388.   3) American English: 1,200,000 = 1.2 million = one point two million;
  389.   23,380,000,000 = 23.38 billion = twenty-three point three eight billion
  390.   [such folding currently does NOT supported]
  391.   4) English fractions can ALSO use russian-style representation:
  392.   'one ten-thousandth' (одна десятитысячная) [0.005 five thousandths] [0.54 fifty four hundredths]
  393.   */
  394.  
  395. // 'TStrType' SHOULD support operator '+=', 'empty' AND 'size' methods
  396. // 'ReserveBeforeAdding' can be used to DISABLE possible 'trade-space-for-time' optimization
  397. template<class TStrType, const bool ReserveBeforeAdding = true>
  398. // "Number to the numeric format string" (321 -> "three hundred twenty-one")
  399. // Accpets negative numbers AND fractions
  400. // Complexity: linear in the number's digit count
  401. static bool numToNumFormatStr(long double num, TStrType& str,
  402. LocaleSettings& localeSettings =
  403. LocaleSettings::DEFAULT_LOCALE_SETTINGS,
  404. const char** const errMsg = nullptr) {
  405. auto negativeNum = false;
  406. if (num < 0.0L) {
  407. negativeNum = true;
  408. num = -num; // revert
  409. }
  410. //// Check borders
  411. static const auto VAL_UP_LIMIT_ = 1e100L; // see 'getOrderStr'
  412. if (num >= VAL_UP_LIMIT_) {
  413. if (errMsg) *errMsg = "too big value";
  414. return false;
  415. }
  416. if (ELocale::L_RU_RU == localeSettings.locale) { // for rus. lang. ONLY
  417. static const auto VAL_LOW_LIMIT_RU_ = 10.0L / VAL_UP_LIMIT_;
  418. if (num && num < VAL_LOW_LIMIT_RU_) {
  419. if (errMsg) *errMsg = "too low value";
  420. return false;
  421. }
  422. }
  423. //// Treat sign
  424. const auto delimiter = DEFAULT_DELIMITER;
  425. auto getSignStr = [](const ELocale locale, const bool positive) throw() -> const char* {
  426. switch (locale) {
  427. case ELocale::L_EN_US: return positive ? "positive" : "negative";
  428. case ELocale::L_EN_GB: return positive ? "plus" : "minus";
  429. case ELocale::L_RU_RU: return positive ? "плюс" : "минус";
  430. }
  431. assert(false); // locale error
  432. // Design / implementation error, NOT runtime error!
  433. return "<locale error [" MAKE_STR_(__LINE__) "]>"; // works OK in GCC
  434. };
  435. if (negativeNum || (localeSettings.positiveSign && num)) { // add sign
  436. if (!str.empty()) str += delimiter; // if needed
  437. str += getSignStr(localeSettings.locale, !negativeNum);
  438. }
  439. if (truncated::ExecIfPresent(str)) { // check if truncated
  440. if (errMsg) *errMsg = "too short buffer"; return false;
  441. }
  442. //// Get str. representation in the scientific notation
  443.  
  444. // +2(+3) for rounding without loosing the precison: http://stackoverflow.com/questions/16839658
  445. // (see also https://e...content-available-to-author-only...a.org/wiki/Extended_precision#Need_for_the_80-bit_format)
  446. /* SOME compilers support a 'long double' format that has more precision than 'double'
  447.   (Microsoft MSVC is NOT, BUT Borland C++ BuilderX has 'LDBL_DIG' = 18)
  448.   'LDBL_DIG' is the number of decimal digits that can be represented without losing precision
  449.   */
  450. // 'printf("%.*Lf", 999, 1.1L)': '1.100000000000000000021684043449710088680149056017398834228515625'
  451. /* 'std::numeric_limits<T>::digits10': number of base-10 digits that can be represented by the type 'T'
  452.   without change - ANY number with this many decimal digits can be converted to a value of type 'T'
  453.   AND back to decimal form, without change due to rounding OR overflow
  454.   */
  455. static const size_t MAX_DIGIT_COUNT_ = size_t(LDBL_DIG);
  456. // Normalized form (mantissa is a 1 digit ONLY):
  457. // first digit (one of 'MAX_DIGIT_COUNT_') + '.' + [max. digits AFTER '.' - 1] + 'e+000'
  458. // [https://e...content-available-to-author-only...a.org/wiki/Scientific_notation#Normalized_notation]
  459. static const size_t MAX_STR_LEN_ = 6U + MAX_DIGIT_COUNT_;
  460.  
  461. // +24 to be on a safe side in case if NOT normalized form (unlikely happen) + for str. terminator
  462. static const size_t BUF_SIZE_ = AUTO_ADJUST_MEM(MAX_STR_LEN_ + 24U, 8U);
  463. char strBuf[BUF_SIZE_];
  464. // 21 digits is max. for 'long double' [https://msdn.microsoft.com/ru-ru/library/4hwaceh6.aspx]
  465. // (20 of them can be AFTER decimal point in the normalized scientific notation)
  466. if (localeSettings.precison > MAX_DIGIT_COUNT_) localeSettings.precison = MAX_DIGIT_COUNT_;
  467. const ptrdiff_t len = sprintf(strBuf, "%.*Le", localeSettings.precison, num); // scientific format
  468. // On failure, a negative number is returned
  469. if (len < static_cast<decltype(len)>(localeSettings.precison)) {
  470. if (errMsg) *errMsg = "number to string convertion failed";
  471. return false;
  472. }
  473. //// Fraction repeated pattern recognition [123 from 1.123123, 7 from 123.777 etc]
  474. //// [!] Warning: does NOT work with the zero ending patterns (like '0.15015') [!]
  475. // (add zeroes to the end of the str. to match correctly??)
  476.  
  477. // Return nullptr if a pattern of such a len. is EXISTS (returns last NOT matched occurrence else)
  478. auto testPattern = [](const char* const str, const char* const strEnd,
  479. const size_t patternSize) throw() {
  480. assert(str); // SHOULD NOT be nullptr
  481. auto equal = true;
  482. auto nextOccurance = str + patternSize;
  483. while (true) {
  484. if (memcmp(str, nextOccurance, patternSize)) return nextOccurance; // NOT macthed
  485. nextOccurance += patternSize;
  486. if (nextOccurance >= strEnd) return decltype(nextOccurance)(); // ALL matched, return nullptr
  487. }
  488. };
  489.  
  490. // Retruns pattern size if pattern exist, 0 otherwise
  491. // TO DO: add support for advanced folding: 1.25871871 [find repeated pattern NOT ONLY from start]
  492. // [in cycle: str+1, str+2, ...; get pattern start, pattern len. etc in 'tryFindPatternEx']
  493. // ['сто двадцать целых двадцать пять до периода и шестьдесят семь в периоде']
  494. // [controled by 'enableAdvancedFolding' new option]]
  495. auto tryFindPattern = [&](const char* const str, const size_t totalLen) throw() {
  496. const size_t maxPatternLen = totalLen / size_t(2U);
  497. auto const strEnd = str + totalLen; // past the end
  498. for (auto patternSize = size_t(1U); patternSize <= maxPatternLen; ++patternSize) {
  499. if (totalLen % patternSize) continue; // skip invalid dividers [OPTIMIZATION]
  500. if (!testPattern(str, strEnd, patternSize)) return patternSize;
  501. }
  502. return size_t();
  503. };
  504.  
  505. //// Analyze str. representation in the scientific notation
  506.  
  507. char* currSymbPtr; // ptr. used to iterate over the numeric str.
  508. char* fractPartStart; // in the original scientific representation
  509. char* fractPartEnd; // past the end [will point to the str. terminator, replacing the exp. sign]
  510. long int expVal; // 3 for '1.0e3'
  511. auto fractPartLen = ptrdiff_t();
  512. size_t intPartLen; // real len.
  513. size_t intPartBonusOrder; // of the current digit
  514. size_t fractPartLeadingZeroesCount; // extra zeroes count BEFORE first meaning digit
  515. static const auto DECIMAL_DELIM_ = '.'; // [decimal separator / decimal mark] to use
  516. auto analyzeScientificNotationRepresentation = [&]() throw() {
  517. currSymbPtr = strBuf + len - size_t(1U); // from the end to start (<-)
  518. //// Get exp.
  519. static const auto EXP_SYMB_ = 'e';
  520. while (EXP_SYMB_ != *currSymbPtr) {
  521. --currSymbPtr; // rewind to the exp. start
  522. assert(currSymbPtr > strBuf);
  523. }
  524. fractPartEnd = currSymbPtr;
  525. *currSymbPtr = '\0'; // break str.: 2.22044604925031310000e+016 -> 2.22044604925031310000 +016
  526. const char* errMsg;
  527. const auto result = strToL(expVal, currSymbPtr + size_t(1U), errMsg);
  528. assert(result);
  529. //// Get int. part len.
  530. fractPartStart = currSymbPtr - localeSettings.precison;
  531. intPartLen = fractPartStart - strBuf;
  532. assert(intPartLen);
  533. if (localeSettings.precison) --intPartLen; // treat zero fract. precison ('1e0')
  534. assert((currSymbPtr - strBuf - int(localeSettings.precison) - 1) >= 0);
  535. assert(localeSettings.precison ? DECIMAL_DELIM_ == *(strBuf + intPartLen) : true);
  536. //// Finishing analyse (partition the number): get int. part real len.
  537. if (expVal < 0L) { // negative exp.
  538. if (static_cast<size_t>(-expVal) >= intPartLen) { // NO int. part
  539. fractPartLeadingZeroesCount = -(expVal + static_cast<long int>(intPartLen));
  540. intPartLen = size_t(); // skip processing int. part
  541. } else { // reduce int. part
  542. intPartLen += expVal; // decr. len.
  543. fractPartLeadingZeroesCount = size_t();
  544. }
  545. intPartBonusOrder = size_t();
  546. if (localeSettings.precison) // if fract. part exists [in the scientific represent.]
  547. --fractPartLen; // move delim. into the fract part., so reduce it length
  548. } else { // non-negative exp.: incr. len.
  549. const auto additive =
  550. std::min<decltype(localeSettings.precison)>(expVal, localeSettings.precison);
  551. intPartLen += additive;
  552. fractPartLeadingZeroesCount = size_t();
  553. intPartBonusOrder = expVal - additive;
  554. }
  555. };
  556. analyzeScientificNotationRepresentation();
  557. // Rewind to the fract. start [BEFORE getting fract. part real len.]
  558. currSymbPtr = strBuf + intPartLen +
  559. (expVal > decltype(expVal)() ? size_t(1U) : size_t()); // 1.23e1 = 12.3e0 [move right +1]
  560.  
  561. auto fractPartTrailingZeroesCount = size_t(), fractPartAddedCount = size_t();
  562. char* fractPartRealStart;
  563. auto folded = false; // true if repeated pattern founded
  564. auto calcFractPartRealLen = [&]() throw() {
  565. if (DECIMAL_DELIM_ == *currSymbPtr) ++currSymbPtr; // skip delimiter when it separtes ('1.1e0')
  566. assert(fractPartEnd >= currSymbPtr); // 'currSymbPtr' SHOULD now be a real fract. part start
  567. fractPartRealStart = currSymbPtr;
  568. fractPartLen += fractPartEnd - currSymbPtr; // 'fractPartLen' CAN be negative BEFORE addition
  569. assert(fractPartLen >= ptrdiff_t()); // SHOULD NOT be negative now
  570. if (!fractPartLen) return; // NO fract. part
  571. //// Skip trailing zeroes
  572. auto fractPartCurrEnd = fractPartEnd - size_t(1U); // will point to the last non-zero digit symb.
  573. while ('0' == *fractPartCurrEnd && fractPartCurrEnd >= currSymbPtr) --fractPartCurrEnd;
  574. assert(fractPartCurrEnd >= strBuf); // SHOULD NOT go out of the buf.
  575. fractPartTrailingZeroesCount = fractPartEnd - fractPartCurrEnd - size_t(1U);
  576. assert(fractPartLeadingZeroesCount >= size_t() &&
  577. fractPartLen >= static_cast<ptrdiff_t>(fractPartTrailingZeroesCount));
  578. fractPartLen -= fractPartTrailingZeroesCount;
  579. //// Fraction folding (if needed)
  580. if (fractPartLen > size_t(1U) && localeSettings.foldFraction) {
  581. //// Remove delim. (if needed)
  582. assert(fractPartStart && fractPartStart > strBuf); // SHOULD be setted (delim. founded)
  583. if (fractPartRealStart < fractPartStart) { // move: "12.1e-1" -> "1 21e-1"
  584. currSymbPtr = fractPartStart - size_t(1U);
  585. assert(*currSymbPtr == DECIMAL_DELIM_);
  586. while (currSymbPtr > fractPartRealStart)
  587. *currSymbPtr-- = *(currSymbPtr - size_t(1U)); // reversed move
  588. *currSymbPtr = '\0';
  589. fractPartRealStart = currSymbPtr + size_t(1U); // update, now SHOULD point to the new real start
  590. assert(fractPartLen);
  591. }
  592. //// Actual folding (if needed)
  593. if (fractPartLen > size_t(1U)) {
  594. const auto patternLen = tryFindPattern(fractPartRealStart, fractPartLen);
  595. if (patternLen) {
  596. fractPartLen = patternLen; // actual folding (reduce fract. part len. to the pattern. len)
  597. folded = true;
  598. }
  599. }
  600. }
  601. };
  602. // We are NOT using 'modfl' to get part values trying to optimize by skipping zero parts
  603. calcFractPartRealLen(); // update len.
  604. assert(fractPartLen ? localeSettings.precison : true);
  605. const auto fractPartWillBeMentioned = fractPartLen || !localeSettings.shortFormat;
  606. currSymbPtr = strBuf; // start from the beginning, left-to-right (->)
  607.  
  608. //// Language-specific morphological lambdas (a result of the morphological analysis)
  609. //// [by default they returns numerals in the nominative case]
  610.  
  611. /* Word can have up to a 3 morphems (affixes) in addition to the root:
  612.   1) prefix: placed BEFORE the stem of a word
  613.   2) infix: inserted INSIDE a word stem
  614.   OR
  615.   interfix: [linkage] placed in BETWEEN two morphemes AND does NOT have a semantic meaning
  616.   3) postfix: (suffix OR ending) placed AFTER the stem of a word
  617.   Word = [prefix]<root>[infix / interfix][postfix (suffix, ending)]
  618.  
  619.   Online tools:
  620.   склонение:
  621.   http://m...content-available-to-author-only...r.ru/Demo.aspx
  622.   http://w...content-available-to-author-only...a.ru
  623.   http://n...content-available-to-author-only...e.ru
  624.   spelling:
  625.   http://w...content-available-to-author-only...h.com/saynum.html
  626.   http://e...content-available-to-author-only...5.ru/en/numbers_translation
  627.   http://p...content-available-to-author-only...w.com/numbers/index_en.htm
  628.   http://w...content-available-to-author-only...s.com/explore/reallybignumbers.html
  629.   */
  630. /// RU: returns root, which can be used to add ending of the appropriate case
  631. /// (by default, returns ending for the nominative case) [нолЬ / нолЯ / нолЮ | нулём! нолём?]
  632.  
  633. // 0 - 9 [1]
  634. auto getZeroOrderNumberStr = [&](const size_t currDigit, const size_t order, const char*& postfix,
  635. const LocaleSettings& localeSettings) throw() -> const char* {
  636. static const char* const EN_TABLE[] = // roots
  637. {"", "one", "tw", "th", "fo", "fi", "six", "seven", "eigh", "nine"};
  638. static const char* const EN_POSTFIXES[] = // endings
  639. {"", "", "o", "ree", "ur", "ve", "", "", "t", ""};
  640. static const char* const RU_TABLE[] =
  641. {"нол", "од", "дв", "тр", "четыр", "пят", "шест", "сем", "вос", "девят"};
  642. static const char* const RU_POSTFIXES[] = // восЕМЬ восЬМИ восЕМЬЮ
  643. // одИН одНОГО одНОМУ одНИМ; двА двУХ двУМ двУМЯ; трИ трЕМЯ; четырЕ четырЬМЯ четырЁХ
  644. {"ь", "ин", "а", "и", "е", "ь", "ь", "ь", "емь", "ь"};
  645. // НолЬ нолЯ нолЮ; пятЬ пятЬЮ пятЕРЫХ; шестЬ шестЬЮ шестИ; семЬ семИ семЬЮ; девятЬ девятЬЮ девятИ
  646. static_assert(sizeof(EN_TABLE) == sizeof(RU_TABLE) && sizeof(EN_TABLE) == sizeof(EN_POSTFIXES) &&
  647. sizeof(RU_TABLE) == sizeof(RU_POSTFIXES) &&
  648. size_t(10U) == std::extent<decltype(EN_TABLE)>::value,
  649. "Tables SHOULD have the same size (10)");
  650. assert(currDigit < std::extent<decltype(EN_TABLE)>::value); // is valid digit?
  651. switch (localeSettings.locale) {
  652. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  653. postfix = EN_POSTFIXES[currDigit];
  654. if (!currDigit) { // en.wikipedia.org/wiki/Names_for_the_number_0_in_English
  655. // American English:
  656. // zero: number by itself, decimals, percentages, phone numbers, some fixed expressions
  657. // o (letter): years, addresses, times and temperatures
  658. // nil: sports scores
  659. if (localeSettings.verySpecific) return "o"; // 'oh'
  660. return localeSettings.locale == ELocale::L_EN_US ? "zero" : "nought";
  661. }
  662. return EN_TABLE[currDigit];
  663. case ELocale::L_RU_RU:
  664. postfix = "";
  665. switch (order) {
  666. case size_t(0U): // last digit ['двадцать две целых ноль десятых']
  667. // Один | одНА целая ноль десятых | одна целая одНА десятая
  668. if (!fractPartWillBeMentioned) break;
  669. case size_t(3U): // тысяч[?]
  670. switch (currDigit) {
  671. case size_t(1U): postfix = "на"; break; // 'ста двадцать одНА тысяча'
  672. case size_t(2U): postfix = "е"; break; // 'ста двадцать двЕ тысячи' []
  673. }
  674. break;
  675. }
  676. if (!*postfix) postfix = RU_POSTFIXES[currDigit]; // if NOT setted yet
  677. return RU_TABLE[currDigit];
  678. }
  679. assert(false); // locale error
  680. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  681. };
  682. // 10 - 19 [1]; 20 - 90 [10]
  683. auto getFirstOrderNumberStr = [&](const size_t currDigit, const size_t prevDigit,
  684. const char*& infix, const char*& postfix,
  685. const LocaleSettings& localeSettings) throw() -> const char* {
  686. //// Sub. tables: 10 - 19 [1]; Main tables: 20 - 90 [10]
  687.  
  688. static const char* const EN_SUB_TABLE[] = {"ten", "eleven"}; // exceptions [NO infixes / postfixes]
  689. static const char* const EN_SUB_INFIXES[] = // th+ir+teen; fo+ur+teen; fi+f+teen
  690. {"", "", "", "ir", "ur", "f", "", "", "", ""};
  691. #define ESP_ "teen" // EN_SUB_POSTFIX
  692. static const char* const EN_SUB_POSTFIXES[] = // tw+elve ["a dozen"]; +teen ALL others
  693. {"", "", "elve", ESP_, ESP_, ESP_, ESP_, ESP_, ESP_, ESP_}; // +teen of ALL above 2U (twelve)
  694. static const char* const EN_MAIN_INFIXES[] = // tw+en+ty ["a score"]; th+ir+ty; fo+r+ty; fi+f+ty
  695. {"", "", "en", "ir", "r", "f", "", "", "", ""}; // +ty ALL
  696.  
  697. #define R23I_ "дцат" // RU_20_30_INFIX [+ь]
  698. #define RT1I_ "на" R23I_ // RU_TO_19_INFIX [на+дцат+ь]
  699. static const char* const RU_SUB_INFIXES[] = // +ь; одиннадцатЬ одиннадцатИ одиннадцатЬЮ
  700. // ДесятЬ десятИ десятЬЮ; од и надцат ь / тр и надцат ь; дв е надцат ь; вос ем надцат ь
  701. {"", "ин" RT1I_, "е" RT1I_, "и" RT1I_, RT1I_, RT1I_, RT1I_, RT1I_, "ем" RT1I_, RT1I_};
  702.  
  703. // ДвадцатЬ двадцатЬЮ двадцатЫЙ двадцатОМУ двадцатИ; семьдесят BUT семидесяти!
  704. #define R5T8I_ "ьдесят" // RU_50_TO_80_INFIX [NO postfix]
  705. static const char* const RU_MAIN_INFIXES[] = // дв а дцат ь; тр и дцат ь; пят шест сем +ьдесят
  706. {"", "", "а" R23I_, "и" R23I_, "", R5T8I_, R5T8I_, R5T8I_, "ем" R5T8I_, ""}; // вос ем +ьдесят
  707. static const char* const RU_MAIN_POSTFIXES[] = // дв а дцат ь; тр и дцат ь; пят шест сем +ьдесят
  708. {"", "", "ь", "ь", "", "", "", "", "", "о"}; // сорок; вос ем +ьдесят; девяност о девяност а
  709.  
  710. static_assert(sizeof(EN_SUB_INFIXES) == sizeof(EN_MAIN_INFIXES) &&
  711. sizeof(EN_SUB_POSTFIXES) == sizeof(RU_MAIN_POSTFIXES) &&
  712. sizeof(RU_SUB_INFIXES) == sizeof(RU_MAIN_INFIXES), "Tables SHOULD have the same size");
  713. assert(prevDigit < std::extent<decltype(EN_SUB_POSTFIXES)>::value); // is valid digits?
  714. assert(currDigit < std::extent<decltype(EN_SUB_POSTFIXES)>::value);
  715. switch (localeSettings.locale) {
  716. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  717. switch (prevDigit) {
  718. case size_t(1U): // ten - nineteen
  719. infix = EN_SUB_INFIXES[currDigit], postfix = EN_SUB_POSTFIXES[currDigit];
  720. if (currDigit < size_t(2U)) return EN_SUB_TABLE[currDigit]; // exceptions
  721. break;
  722. default: // twenty - ninety
  723. assert(!prevDigit && currDigit > size_t(1U));
  724. infix = EN_MAIN_INFIXES[currDigit], postfix = "ty"; // +ty for ALL
  725. break;
  726. }
  727. break;
  728. case ELocale::L_RU_RU:
  729. switch (prevDigit) {
  730. case size_t(1U): // десять - девятнадцать
  731. infix = RU_SUB_INFIXES[currDigit], postfix = "ь"; // +ь for ALL
  732. if (!currDigit) return "десят";
  733. break;
  734. default: // двадцать - девяносто
  735. assert(currDigit > size_t(1U));
  736. infix = RU_MAIN_INFIXES[currDigit], postfix = RU_MAIN_POSTFIXES[currDigit];
  737. switch (currDigit) {
  738. case size_t(4U): return "сорок"; // сорокА
  739. case size_t(9U): return "девяност"; // девяностО девяностЫХ девяностЫМ
  740. }
  741. break;
  742. }
  743. break;
  744. default: assert(false); // locale error
  745. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  746. } // END switch (locale)
  747. const char* tempPtr;
  748. return getZeroOrderNumberStr(currDigit, size_t(), tempPtr, localeSettings);
  749. };
  750. // 100 - 900 [100]
  751. auto getSecondOrderNumberStr = [&](const size_t currDigit, const char*& infix, const char*& postfix,
  752. const LocaleSettings& localeSettings) throw() -> const char* {
  753. static const char* const RU_POSTFIXES[] =
  754. {"", "", "сти", "ста", "ста", "сот", "сот", "сот", "сот", "сот"};
  755. static_assert(size_t(10U) == std::extent<decltype(RU_POSTFIXES)>::value,
  756. "Table SHOULD have the size of 10");
  757. assert(currDigit && currDigit < std::extent<decltype(RU_POSTFIXES)>::value);
  758. switch (localeSettings.locale) {
  759. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  760. postfix = " hundred";
  761. return getZeroOrderNumberStr(currDigit, size_t(), infix, localeSettings);
  762. case ELocale::L_RU_RU:
  763. postfix = RU_POSTFIXES[currDigit];
  764. switch (currDigit) {
  765. case size_t(1U): infix = ""; return "сто"; break;
  766. case size_t(2U): {
  767. const char* temp;
  768. infix = "е"; //ALWAYS 'е'
  769. return getZeroOrderNumberStr(currDigit, size_t(), temp, localeSettings); // дв е сти
  770. }
  771. }
  772. return getZeroOrderNumberStr(currDigit, size_t(), infix, localeSettings);
  773. } // END switch (locale)
  774. assert(false); // locale error
  775. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  776. };
  777. // Up to 10^99 [duotrigintillions]
  778. auto getOrderStr = [](size_t order, const size_t preLastDigit, const size_t lastDigit,
  779. const char*& postfix, const LocaleSettings& localeSettings)
  780. throw() -> const char* {
  781. // https://e...content-available-to-author-only...a.org/wiki/Names_of_large_numbers
  782. static const char* const EN_TABLE[] = // uses short scale (U.S., part of Canada, modern British)
  783. {"", "thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion",
  784. "septillion", "octillion", "nonillion", "decillion", "undecillion", "duodecillion" /*10^39*/,
  785. "tredecillion", "quattuordecillion", "quindecillion", "sedecillion", "septendecillion",
  786. "octodecillion", "novemdecillion ", "vigintillion", "unvigintillion", "duovigintillion",
  787. "tresvigintillion", "quattuorvigintillion", "quinquavigintillion", "sesvigintillion",
  788. "septemvigintillion", "octovigintillion", "novemvigintillion", "trigintillion" /*10^93*/,
  789. "untrigintillion", "duotrigintillion"};
  790. // https://r...content-available-to-author-only...a.org/wiki/Именные_названия_степеней_тысячи
  791. static const char* const RU_TABLE[] = // SS: short scale, LS: long scale
  792. {"", "тысяч", "миллион", "миллиард" /*SS: биллион*/, "триллион" /*LS: биллион*/,
  793. "квадриллион" /*LS: биллиард*/, "квинтиллион" /*LS: триллион*/,
  794. "секстиллион" /*LS: триллиард*/, "септиллион" /*LS: квадриллион*/, "октиллион", "нониллион",
  795. "дециллион", "ундециллион", "додециллион", "тредециллион", "кваттуордециллион" /*10^45*/,
  796. "квиндециллион", "седециллион", "септдециллион", "октодециллион", "новемдециллион",
  797. "вигинтиллион", "анвигинтиллион", "дуовигинтиллион", "тревигинтиллион", "кватторвигинтиллион",
  798. "квинвигинтиллион", "сексвигинтиллион", "септемвигинтиллион", "октовигинтиллион" /*10^87*/,
  799. "новемвигинтиллион", "тригинтиллион", "антригинтиллион", "дуотригинтиллион"}; // 10^99
  800. static_assert(sizeof(EN_TABLE) == sizeof(RU_TABLE), "Tables SHOULD have the same size");
  801. static const size_t MAX_ORDER_ =
  802. (std::extent<decltype(EN_TABLE)>::value - size_t(1U)) * size_t(3U); // first empty
  803.  
  804. static const char* const RU_THOUSAND_POSTFIXES[] = // десять двадцать сто двести тысяч
  805. // Одна тысячА | две три четыре тысячИ | пять шесть семь восемь девять тысяч
  806. {"", "а", "и", "и", "и", "", "", "", "", ""};
  807. static const char* const RU_MILLIONS_AND_BIGGER_POSTFIXES[] = // один миллион; два - четыре миллионА
  808. // Пять шесть семь восемь девять миллионОВ [миллиардОВ триллионОВ etc]
  809. // Десять двадцать сто двести миллионОВ миллиардОВ etc
  810. {"ов", "", "а", "а", "а", "ов", "ов", "ов", "ов", "ов"};
  811. static_assert(size_t(10U) == std::extent<decltype(RU_THOUSAND_POSTFIXES)>::value &&
  812. size_t(10U) == std::extent<decltype(RU_MILLIONS_AND_BIGGER_POSTFIXES)>::value,
  813. "Tables SHOULD have the size of 10");
  814. switch (localeSettings.locale) {
  815. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  816. postfix = "";
  817. if (size_t(2U) == order) return "hundred"; // 0U: ones, 1U: tens
  818. order /= 3U; // 0 - 1: empty, 3 - 5: thousands, 6 - 8: millions, 9 - 11: billions etc
  819. assert(order < std::extent<decltype(EN_TABLE)>::value);
  820. return EN_TABLE[order]; // [0, 33]
  821. case ELocale::L_RU_RU:
  822. assert(preLastDigit < size_t(10U) && lastDigit < size_t(10U));
  823. if (size_t(3U) == order) { // determine actual postfix first
  824. if (size_t(1U) != preLastDigit) {
  825. postfix = RU_THOUSAND_POSTFIXES[lastDigit];
  826. } else postfix = ""; // 'тринадцать тысяч'
  827. } else if (order > size_t(3U)) { // != 3U
  828. if (size_t(1U) == preLastDigit) { // десять одиннадцать+ миллионОВ миллиардОВ etc
  829. postfix = "ов";
  830. } else postfix = RU_MILLIONS_AND_BIGGER_POSTFIXES[lastDigit];
  831. }
  832. order /= 3U; // 6 - 8: миллионы, 9 - 11: миллиарды etc
  833. assert(order < std::extent<decltype(RU_TABLE)>::value);
  834. return RU_TABLE[order]; // [0, 33]
  835. }
  836. assert(false); // locale error
  837. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  838. };
  839.  
  840. // 'intPartPreLastDigit' AND 'intPartLastDigit' CAN be negative (in case of NO int. part)
  841. auto getFractionDelimiter = [](const ptrdiff_t intPartPreLastDigit, const ptrdiff_t intPartLastDigit,
  842. const char*& postfix, const bool folded,
  843. const LocaleSettings& localeSettings) throw() -> const char* {
  844. assert(intPartPreLastDigit < ptrdiff_t(10) && intPartLastDigit < ptrdiff_t(10));
  845. postfix = "";
  846. switch (localeSettings.locale) {
  847. case ELocale::L_EN_US: case ELocale::L_EN_GB: return "point"; // also 'decimal'
  848. case ELocale::L_RU_RU: // "целые" НЕ употребляются в учебниках!
  849. if (intPartLastDigit < ptrdiff_t() && localeSettings.shortFormat) return ""; // NO int. part
  850. if (folded) postfix = "и";
  851. return ptrdiff_t(1) == intPartLastDigit ?
  852. (ptrdiff_t(1) == intPartPreLastDigit ? "целых" : "целая") : // одинадцать целЫХ | одна целАЯ
  853. "целых"; // ноль, пять - девять целЫХ; две - четыре целЫХ; десять цел ых
  854. }
  855. assert(false); // locale error
  856. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  857. };
  858.  
  859. auto getFoldedFractionEnding = [](const LocaleSettings& localeSettings) throw() {
  860. // Also possibly 'continuous', 'recurring'; 'reoccurring' (Australian)
  861. switch (localeSettings.locale) {
  862. case ELocale::L_EN_US: return "to infinity"; // also 'into infinity', 'to the infinitive'
  863. case ELocale::L_EN_GB: return "repeating"; // also 'repeated'
  864. case ELocale::L_RU_RU: return "в периоде";
  865. }
  866. assert(false); // locale error
  867. return "<locale error [" MAKE_STR_(__LINE__) "]>";
  868. };
  869.  
  870. //// Language-specific processing lambdas
  871.  
  872. auto getMinDigitsSubPartSizeToAddOrder = [](const LocaleSettings& localeSettings) throw() {
  873. switch (localeSettings.locale) {
  874. case ELocale::L_EN_US: case ELocale::L_EN_GB: return size_t(2U); // hundreds
  875. case ELocale::L_RU_RU: return size_t(3U); // тысячи
  876. }
  877. assert(false); // locale error
  878. return size_t();
  879. };
  880.  
  881. // Returns zero (NOT set, undefined) if NOT spec. case
  882. auto getSpecificCaseSubPartSize = [](const long double& num,
  883. const LocaleSettings& localeSettings) throw() {
  884. switch (localeSettings.locale) {
  885. /*
  886.   In American usage, four-digit numbers with non-zero hundreds
  887.   are often named using multiples of "hundred"
  888.   AND combined with tens AND/OR ones:
  889.   "One thousand one", "Eleven hundred three", "Twelve hundred twenty-five",
  890.   "Four thousand forty-two", or "Ninety-nine hundred ninety-nine"
  891.   */
  892. case ELocale::L_EN_US:
  893. if (num < 10000.0L) {
  894. bool zeroTensAndOnes;
  895. const auto hundreds =
  896. MathUtils::getDigitOfOrder(size_t(2U), static_cast<long long int>(num), zeroTensAndOnes);
  897. if (hundreds && !zeroTensAndOnes) return size_t(2U); // if none-zero hundreds
  898. }
  899. break;
  900. // In British usage, this style is common for multiples of 100 between 1,000 and 2,000
  901. // (e.g. 1,500 as "fifteen hundred") BUT NOT for higher numbers
  902. case ELocale::L_EN_GB:
  903. if (num >= 1000.0L && num < 2001.0L) {
  904. // If ALL digits of order below 2U [0, 1] is zero
  905. if (!(static_cast<size_t>(num) % size_t(100U))) return size_t(2U); // if is multiples of 100
  906. }
  907. break;
  908. }
  909. return size_t();
  910. };
  911.  
  912. auto getIntSubPartSize = [&]() throw() {
  913. auto subPartSize = size_t();
  914. if (localeSettings.verySpecific)
  915. subPartSize = getSpecificCaseSubPartSize(num, localeSettings); // CAN alter digits subpart size
  916. if (!subPartSize) { // NOT set previously
  917. switch (localeSettings.locale) { // triads by default
  918. // For eng. numbers step = 1 can be ALSO used: 64.705 — 'six four point seven nought five'
  919. case ELocale::L_EN_US: case ELocale::L_EN_GB: case ELocale::L_RU_RU: subPartSize = size_t(3U);
  920. }
  921. }
  922. return subPartSize;
  923. };
  924.  
  925. auto getFractSubPartSize = [](const LocaleSettings& localeSettings) throw() {
  926. switch (localeSettings.locale) {
  927. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  928. // Step = 2 OR 3 can be ALSO used: 14.65 - 'one four point sixty-five'
  929. return size_t(1U); // point one two seven
  930. case ELocale::L_RU_RU: return size_t(3U); // сто двадцать семь сотых
  931. }
  932. assert(false); // locale error
  933. return size_t();
  934. };
  935.  
  936. // Currently there is NO specific handling for 'short format' AND 'very specific' options
  937. auto estimatePossibleLength = [](const size_t digitsPartSize, const bool fractPart,
  938. const LocaleSettings& localeSettings) throw() {
  939. // If processing by the one digit per time; EN GB uses 'nought' instead of 'zero'
  940. static const auto EN_US_AVG_CHAR_PER_DIGIT_NAME_ = size_t(4U); // 40 / 10 ['zero' - 'nine']
  941. static size_t AVG_SYMB_PER_DIGIT_[ELocale::COUNT]; // for ALL langs; if processing by triads
  942.  
  943. struct ArrayIniter { // 'AVG_SYMB_PER_DIGIT_' initer
  944. ArrayIniter() throw() {
  945. //// All this value is a result of the statistical analysis
  946. AVG_SYMB_PER_DIGIT_[ELocale::L_EN_GB] = size_t(10U); // 'one hundred and twenty two thousand'
  947. AVG_SYMB_PER_DIGIT_[ELocale::L_EN_US] = size_t(9U); // 'one hundred twenty two thousand'
  948. AVG_SYMB_PER_DIGIT_[ELocale::L_RU_RU] = size_t(8U); // 'сто двадцать две тысячи'
  949. }
  950. }; static const ArrayIniter INITER_; // static init. is a thread safe in C++11
  951.  
  952. static const auto RU_DELIM_LEN_ = size_t(5U); // "целых" / "целая"
  953. // Frequent postfixes (up to trillions: 'десятитриллионных')
  954. static const auto RU_MAX_FREQ_FRACT_POSTFIX_LEN_ = size_t(17U);
  955.  
  956. switch (localeSettings.locale) {
  957. case ELocale::L_EN_US: case ELocale::L_EN_GB:
  958. if (!fractPart) return AVG_SYMB_PER_DIGIT_[localeSettings.locale] * digitsPartSize;
  959. // For the fract part [+1 for the spacer]
  960. return (EN_US_AVG_CHAR_PER_DIGIT_NAME_ + size_t(1U)) * digitsPartSize;
  961. case ELocale::L_RU_RU: // RU RU processes fract. part by the triads (like an int. part)
  962. {
  963. size_t len_ = AVG_SYMB_PER_DIGIT_[ELocale::L_RU_RU] * digitsPartSize;
  964. if (fractPart && digitsPartSize) len_ += RU_DELIM_LEN_ + RU_MAX_FREQ_FRACT_POSTFIX_LEN_;
  965. return len_;
  966. }
  967. }
  968. assert(false); // locale error
  969. return size_t();
  970. };
  971.  
  972. auto addFractionPrefix = [&]() {
  973. switch (localeSettings.locale) {
  974. case ELocale::L_EN_US: case ELocale::L_EN_GB: // 'nought nought nought' for 1.0003
  975. {
  976. const char* postfix;
  977. for (auto leadingZeroIdx = size_t(); leadingZeroIdx < fractPartLeadingZeroesCount;) {
  978. assert(str.size()); // NOT empty
  979. str += delimiter;
  980. str += getZeroOrderNumberStr(size_t(), leadingZeroIdx, postfix, localeSettings);
  981. str += postfix;
  982. ++leadingZeroIdx;
  983. }
  984. return;
  985. }
  986. case ELocale::L_RU_RU: return; // NO specific prefix
  987. }
  988. assert(false); // locale error
  989. };
  990.  
  991. size_t currDigit, prevDigit;
  992. // 'order' is an order of the last digit of a fractional part + 1 (1 based idx.)
  993. // [1 for the first, 2 for the second etc]
  994. auto addFractionEnding = [&](const size_t orderExt) {
  995. if (folded) { // add postifx for the folded fraction
  996. auto const ending = getFoldedFractionEnding(localeSettings);
  997. if (*ending) { // if NOT empty
  998. str += delimiter;
  999. str += ending;
  1000. }
  1001. return;
  1002. }
  1003. //// Add 'normal' postifx
  1004. switch (localeSettings.locale) {
  1005. case ELocale::L_EN_US: case ELocale::L_EN_GB: break; // NO specific ending currently
  1006. case ELocale::L_RU_RU: {
  1007. auto toAdd = "";
  1008. //// Add prefix / root
  1009. assert(orderExt); // SHOULD NOT be zero
  1010. const size_t subOrder = orderExt % size_t(3U);
  1011. switch (subOrder) { // zero suborder - empty prefix
  1012. case size_t(1U): // ДЕСЯТ ая(ых) | ДЕСЯТ И тысячная(ых) ДЕСЯТ И миллиардная(ых)
  1013. toAdd = orderExt < size_t(3U) ? "десят" : "десяти"; break;
  1014. case size_t(2U): // СОТ ая(ых) | СТО тысячная(ых) СТО миллиардная(ых)
  1015. toAdd = orderExt < size_t(3U) ? "сот" : "сто"; break;
  1016. }
  1017. if (*toAdd) {
  1018. str += delimiter;
  1019. str += toAdd;
  1020. }
  1021. //// Add root (if NOT yet) + part of the postfix (if needed)
  1022. if (orderExt > size_t(2U)) { // from 'тысяч н ая ых'
  1023. if (!*toAdd) str += delimiter; // deim. is NOT added yet
  1024. const char* temp;
  1025. str += getOrderStr(orderExt, size_t(), size_t(), temp, localeSettings);
  1026. str += "н"; // 'десят И тысяч Н ая ых', 'сто тысяч Н ая ых'
  1027. }
  1028. //// Add postfix
  1029. assert(prevDigit < size_t(10U) && currDigit < size_t(10U));
  1030. if (size_t(1U) == prevDigit) { // одинадцать двенадцать девятнадцать сотЫХ десятитысячнЫХ
  1031. toAdd = "ых";
  1032. } else { // NOT 1U prev. digit
  1033. if (size_t(1U) == currDigit) {
  1034. toAdd = "ая"; // одна двадцать одна десятАЯ, тридцать одна стотысячнАЯ
  1035. } else toAdd = "ых"; // ноль десятых; двадцать две тридцать пять девяносто девять тясячнЫХ
  1036. }
  1037. str += toAdd;
  1038. }
  1039. break;
  1040. default: // locale NOT present
  1041. assert(false); // locale error
  1042. str += "<locale error [" MAKE_STR_(__LINE__) "]>";
  1043. }
  1044. };
  1045.  
  1046. // Also for 'and' in EN GB
  1047. const auto minDigitsSubPartSizeToAddOrder = getMinDigitsSubPartSizeToAddOrder(localeSettings);
  1048. auto totalAddedCount = size_t();
  1049. // ONLY up to 3 digits
  1050. auto processDigitOfATriad = [&](const size_t subOrder, const size_t order, size_t& currAddedCount,
  1051. const size_t normalDigitsSubPartSize, const bool fractPart) {
  1052. auto addFirstToZeroOrderDelim = [&]() {
  1053. char delim_;
  1054. switch (localeSettings.locale) { // choose delim.
  1055. case ELocale::L_EN_US: case ELocale::L_EN_GB: delim_ = '-'; break; // 'thirty-four'
  1056. case ELocale::L_RU_RU: default : delim_ = delimiter; break; // 'тридцать четыре'
  1057. }
  1058. str += delim_;
  1059. };
  1060. auto addDelim = [&](const char delim) {
  1061. if (ELocale::L_EN_GB == localeSettings.locale) {
  1062. // In AMERICAN English, many students are taught NOT to use the word "and"
  1063. // anywhere in the whole part of a number
  1064. if (totalAddedCount && normalDigitsSubPartSize >= minDigitsSubPartSizeToAddOrder) {
  1065. str += delim;
  1066. str += ENG_GB_VERBAL_DELIMITER;
  1067. }
  1068. }
  1069. str += delim;
  1070. };
  1071. assert(subOrder < size_t(3U) && prevDigit < size_t(10U) && currDigit < size_t(10U));
  1072. const char* infix, *postfix;
  1073. switch (subOrder) {
  1074. case size_t(): // ones ('three' / 'три') AND numbers like 'ten' / 'twelve'
  1075. if (size_t(1U) == prevDigit) { // 'ten', 'twelve' etc
  1076. if (!str.empty()) addDelim(delimiter); // if needed
  1077. str += getFirstOrderNumberStr(currDigit, prevDigit, infix, postfix, localeSettings);
  1078. str += infix, str += postfix;
  1079. ++currAddedCount, ++totalAddedCount;
  1080. } else if (currDigit || size_t(1U) == normalDigitsSubPartSize) { // prev. digit is NOT 1
  1081. //// Simple digits like 'one'
  1082. if (prevDigit) { // NOT zero
  1083. assert(prevDigit > size_t(1U));
  1084. addFirstToZeroOrderDelim();
  1085. } else if (!str.empty()) addDelim(delimiter); // prev. digit IS zero
  1086. str += getZeroOrderNumberStr(currDigit, order, postfix, localeSettings);
  1087. str += postfix;
  1088. ++currAddedCount, ++totalAddedCount;
  1089. }
  1090. break;
  1091.  
  1092. case size_t(1U): // tens ['twenty' / 'двадцать']
  1093. if (currDigit > size_t(1U)) { // numbers like ten / twelve would be proceeded later
  1094. if (!str.empty()) addDelim(delimiter); // if needed
  1095. str += getFirstOrderNumberStr(currDigit, size_t(), infix, postfix, localeSettings);
  1096. str += infix, str += postfix;
  1097. ++currAddedCount, ++totalAddedCount;
  1098. } // if 'currDigit' is '1U' - skip (would be proceeded later)
  1099. break;
  1100.  
  1101. case size_t(2U): // hundred(s?)
  1102. if (!currDigit) break; // zero = empty
  1103. if (!str.empty()) str += delimiter; // if needed
  1104. switch (localeSettings.locale) {
  1105. case ELocale::L_EN_US: case ELocale::L_EN_GB: // 'three hundred'
  1106. str += getZeroOrderNumberStr(currDigit, order, postfix, localeSettings);
  1107. str += postfix;
  1108. str += delimiter;
  1109. {
  1110. const char* postfix_; // NO postfix expected, just a placeholder var.
  1111. str += getOrderStr(size_t(2U), size_t(0U), currDigit, postfix_, localeSettings);
  1112. assert(postfix_ && !*postfix_);
  1113. }
  1114. break;
  1115. case ELocale::L_RU_RU: // 'триста'
  1116. str += getSecondOrderNumberStr(currDigit, infix, postfix, localeSettings);
  1117. str += infix, str += postfix;
  1118. break;
  1119. }
  1120. ++currAddedCount, ++totalAddedCount;
  1121. break;
  1122. } // 'switch (subOrder)' END
  1123. };
  1124.  
  1125. //// Generic processing lambdas
  1126.  
  1127. auto intPartPreLastDigit = ptrdiff_t(-1), intPartLastDigit = ptrdiff_t(-1); // NO part by default
  1128. auto addFractionDelimiter = [&]() {
  1129. const char* postfix;
  1130. auto const fractionDelim =
  1131. getFractionDelimiter(intPartPreLastDigit, intPartLastDigit, postfix, folded, localeSettings);
  1132. if (*fractionDelim) { // if NOT empty
  1133. if (!str.empty()) str += delimiter;
  1134. str += fractionDelim;
  1135. }
  1136. if (*postfix) {
  1137. if (*fractionDelim) str += delimiter;
  1138. str += postfix;
  1139. }
  1140. };
  1141.  
  1142. auto addedCount = size_t(); // during processing curr. part
  1143. auto emptySubPartsCount = size_t();
  1144. // Part order is an order of the last digit of the part (zero for 654, 3 for 456 of the 456654 etc)
  1145. // Part (integral OR fractional) of the number is consists of the subparts of specified size
  1146. // (usually 3 OR 1; for ENG.: 3 for int. part., 1 for fract. part)
  1147. // 'subPartOrderExt' SHOULD exists ONLY for a LAST subpart
  1148. auto processDigitsSubPart = [&](const size_t currDigitsSubPartSize,
  1149. const size_t normalDigitsSubPartSize,
  1150. const size_t order, size_t subPartOrderExt, const bool fractPart) {
  1151. assert(currDigitsSubPartSize && currDigitsSubPartSize <= size_t(3U));
  1152. auto currAddedCount = size_t(); // reset
  1153. auto emptySubPart = true; // true if ALL prev. digits of the subpart is zero
  1154. prevDigit = std::decay<decltype(prevDigit)>::type(); // reset
  1155. for (size_t subOrder = currDigitsSubPartSize - size_t(1U);;) {
  1156. if (DECIMAL_DELIM_ != *currSymbPtr) { // skip decimal delim.
  1157. currDigit = *currSymbPtr - '0'; // assuming ANSI ASCII
  1158. PPOCESS_DIGIT_:
  1159. assert(*currSymbPtr >= '0' && currDigit < size_t(10U));
  1160. emptySubPart &= !currDigit;
  1161. processDigitOfATriad(subOrder + subPartOrderExt, order, currAddedCount,
  1162. normalDigitsSubPartSize, fractPart);
  1163. if (subPartOrderExt) { // treat unpresented digits [special service]
  1164. --subPartOrderExt;
  1165. prevDigit = currDigit;
  1166. currDigit = std::decay<decltype(currDigit)>::type(); // remove ref. from type
  1167. goto PPOCESS_DIGIT_; // don't like 'goto'? take a nyan cat here: =^^=
  1168. }
  1169. if (!subOrder) { // zero order digit
  1170. ++currSymbPtr; // shift to the symb. after the last in an int. part
  1171. break;
  1172. }
  1173. --subOrder, prevDigit = currDigit;
  1174. }
  1175. ++currSymbPtr;
  1176. }
  1177. if (emptySubPart) ++emptySubPartsCount; // update stats
  1178. // Add order str. AFTER part (if exist)
  1179. if (currAddedCount && normalDigitsSubPartSize >= minDigitsSubPartSizeToAddOrder) {
  1180. const char* postfix;
  1181. auto const orderStr = getOrderStr(order, prevDigit, currDigit, postfix, localeSettings);
  1182. assert(orderStr && postfix);
  1183. if (*orderStr) { // if NOT empty (CAN be empty for zero order [EN, RU])
  1184. assert(str.size()); // NOT zero
  1185. str += delimiter, str += orderStr, str += postfix;
  1186. ++currAddedCount;
  1187. }
  1188. }
  1189. addedCount += currAddedCount;
  1190. };
  1191.  
  1192. size_t intPartAddedCount, strLenWithoutFractPart;
  1193. // Strategy used to process both integral AND fractional parts of the number
  1194. // 'digitsPartSize' is a total part. len. in digits (i. e. 1 for 4, 3 for 123, 6 for 984532 etc)
  1195. // [CAN be zero in some cases]
  1196. // 'partBonusOrder' will be 3 for 124e3, 9 for 1.2e10, 0 for 87654e0 etc
  1197. // 'fractPart' flag SHOULD be true if processing fraction part
  1198. auto processDigitsPart = [&](size_t digitsPartSize, const size_t digitsSubPartSize,
  1199. size_t partBonusOrder, const bool fractPart) {
  1200. currDigit = size_t(), prevDigit = size_t(); // reset
  1201. if (digitsPartSize) {
  1202. assert(digitsSubPartSize); // SHOULD be NOT zero
  1203. size_t currDigitsSubPartSize =
  1204. (digitsPartSize + partBonusOrder) % digitsSubPartSize; // 2 for 12561, 1 for 9 etc
  1205. if (!currDigitsSubPartSize) currDigitsSubPartSize = digitsSubPartSize; // if zero remanider
  1206. // Will be 2 for '12.34e4' ('1234e2' = '123 400' - two last unpresented zeroes); 1 for 1e1
  1207. auto subPartOrderExt = size_t(); // used ONLY for a last subpart
  1208.  
  1209. // OPTIMIZATION HINT: redesign to preallocate for the whole str., NOT for a diffirent parts?
  1210. if (ReserveBeforeAdding) // optimization [CAN acquire more / less space then really required]
  1211. str.reserve(str.length() + estimatePossibleLength(digitsPartSize, fractPart, localeSettings));
  1212. do {
  1213. if (currDigitsSubPartSize > digitsPartSize) { // if last AND unnormal [due to the '%']
  1214. subPartOrderExt = currDigitsSubPartSize - digitsPartSize;
  1215. partBonusOrder -= subPartOrderExt;
  1216. currDigitsSubPartSize = digitsPartSize; // correct
  1217. }
  1218. digitsPartSize -= currDigitsSubPartSize;
  1219. processDigitsSubPart(currDigitsSubPartSize, digitsSubPartSize,
  1220. digitsPartSize + partBonusOrder, subPartOrderExt, fractPart);
  1221. currDigitsSubPartSize = digitsSubPartSize; // set default [restore]
  1222. } while (digitsPartSize);
  1223. }
  1224. auto mentionZeroPart = [&]() {
  1225. if (!str.empty()) str += delimiter;
  1226. const char* postfix;
  1227. str += getZeroOrderNumberStr(size_t(), size_t(), postfix, localeSettings);
  1228. str += postfix;
  1229. ++totalAddedCount;
  1230. };
  1231. if (!addedCount) { // NO part
  1232. if (!localeSettings.shortFormat || folded) { // NOT skip mention zero parts
  1233. if (fractPart) {
  1234. addFractionDelimiter(); // 'ноль целых'
  1235. } else intPartLastDigit = ptrdiff_t(); // now. IS int. part
  1236. mentionZeroPart();
  1237. ++addedCount;
  1238. } else if (fractPart) { // short format AND now processing fraction part
  1239. assert(!folded); // NO fract. part - SHOULD NOT be folded
  1240. assert(strLenWithoutFractPart <= str.size()); // SHOULD NOT incr. len.
  1241. if (!intPartAddedCount) { // NO int. part [zero point zero -> zero] <EXCEPTION>
  1242. mentionZeroPart(); // do NOT incr. 'addedCount'!!
  1243. }
  1244. }
  1245. }
  1246. };
  1247.  
  1248. //// Process int. part
  1249.  
  1250. processDigitsPart(intPartLen, getIntSubPartSize(), intPartBonusOrder, false);
  1251. if (truncated::ExecIfPresent(str)) { // check if truncated
  1252. if (errMsg) *errMsg = "too short buffer"; return false;
  1253. }
  1254. if (intPartLen) { // if int. part exist
  1255. assert(currSymbPtr > strBuf);
  1256. intPartLastDigit = *(currSymbPtr - ptrdiff_t(1)) - '0';
  1257. assert(intPartLastDigit > ptrdiff_t(-1) && intPartLastDigit < ptrdiff_t(10));
  1258. if (intPartLen > size_t(1U)) { // there is also prelast digit
  1259. auto intPartPreLastDigitPtr = currSymbPtr - ptrdiff_t(2);
  1260. if (DECIMAL_DELIM_ == *intPartPreLastDigitPtr) --intPartPreLastDigitPtr; // skip delim.: 2.3e1
  1261. assert(intPartPreLastDigitPtr >= strBuf); // check borders
  1262. intPartPreLastDigit = *intPartPreLastDigitPtr - '0';
  1263. assert(intPartPreLastDigit > ptrdiff_t(-1) && intPartPreLastDigit < ptrdiff_t(10));
  1264. }
  1265. }
  1266. strLenWithoutFractPart = str.size(); // remember (for future use)
  1267. intPartAddedCount = addedCount;
  1268. addedCount = decltype(addedCount)(); // reset
  1269.  
  1270. //// Process fract. part
  1271.  
  1272. if (fractPartLen) {
  1273. addFractionDelimiter();
  1274. addFractionPrefix(); // if needed
  1275. currSymbPtr = fractPartRealStart; // might be required if folded [in SOME cases]
  1276. }
  1277. processDigitsPart(fractPartLen, getFractSubPartSize(localeSettings), size_t(), true);
  1278. if (addedCount) { // smth. added (even if zero part)
  1279. fractPartAddedCount = addedCount;
  1280. //// Add specific ending (if needed, like 'десятимиллионная')
  1281. assert(fractPartLen >= decltype(fractPartLen)());
  1282. size_t fractPartLastDigitOrderExt = fractPartLeadingZeroesCount + fractPartLen;
  1283. if (!fractPartLastDigitOrderExt) fractPartLastDigitOrderExt = size_t(1U); // at least one
  1284. addFractionEnding(fractPartLastDigitOrderExt);
  1285. }
  1286. assert(totalAddedCount); // SHOULD NOT be zero
  1287. if (truncated::ExecIfPresent(str)) { // check if truncated
  1288. if (errMsg) *errMsg = "too short buffer"; return false;
  1289. } return true;
  1290. }
  1291. };
  1292.  
  1293. #endif // ConvertionUtilsH
  1294.  
  1295. const ConvertionUtils::LocaleSettings ConvertionUtils::LocaleSettings::DEFAULT_LOCALE_SETTINGS;
  1296.  
  1297. #include <iostream>
  1298. #include <string>
  1299.  
  1300. int main() {
  1301. std::string str;
  1302. ConvertionUtils::LocaleSettings localeSettings;
  1303. auto errMsg = "";
  1304. std::cout.precision(LDBL_DIG);
  1305.  
  1306. auto num = 6437268689.4272L;
  1307. localeSettings.locale = ConvertionUtils::ELocale::L_EN_US;
  1308. ConvertionUtils::numToNumFormatStr(num, str, localeSettings, &errMsg);
  1309. std::cout << num << " =>\n " << str << std::endl << std::endl;
  1310.  
  1311. num = 1200.25672567L;
  1312. str.clear();
  1313. localeSettings.locale = ConvertionUtils::ELocale::L_EN_GB;
  1314. localeSettings.foldFraction = true;
  1315. localeSettings.verySpecific = true;
  1316. ConvertionUtils::numToNumFormatStr(num, str, localeSettings, &errMsg);
  1317. std::cout << num << " =>\n " << str << std::endl << std::endl;
  1318.  
  1319. num = 1.0000300501L;
  1320. str.clear();
  1321. localeSettings.locale = ConvertionUtils::ELocale::L_RU_RU;
  1322. ConvertionUtils::numToNumFormatStr(num, str, localeSettings, &errMsg);
  1323. std::cout << num << " =>\n " << str << std::endl << std::endl;
  1324.  
  1325. num = 9432654671318.0e45L;
  1326. str.clear();
  1327. localeSettings.shortFormat = true;
  1328. localeSettings.locale = ConvertionUtils::ELocale::L_RU_RU;
  1329. ConvertionUtils::numToNumFormatStr(num, str, localeSettings, &errMsg);
  1330. std::cout << num << " =>\n " << str;
  1331.  
  1332. return 0;
  1333. }
Success #stdin #stdout 0s 3436KB
stdin
Standard input is empty
stdout
6437268689.4272 =>
 six billion four hundred thirty-seven million two hundred sixty-eight thousand six hundred eighty-nine point four two seven two

1200.25672567 =>
 twelve hundred point two five six seven repeating

1.0000300501 =>
 одна целая триста тысяч пятьсот одна десятимиллиардная

9.432654671318e+57 =>
 девять октодециллионов четыреста тридцать два септдециллиона шестьсот пятьдесят четыре седециллиона шестьсот семьдесят один квиндециллион триста восемнадцать кваттуордециллионов