// bslalg_numericformatterutil.h -*-C++-*- #ifndef INCLUDED_BSLALG_NUMERICFORMATTERUTIL #define INCLUDED_BSLALG_NUMERICFORMATTERUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a utility for formatting numbers into strings. // //@CLASSES: // bslalg::NumericFormatterUtil: namespace for 'toChars' and support functions // //@DESCRIPTION: This component, 'bslalg_numericformatterutil' provides a // namespace 'struct', 'bslalg::NumericFormatterUtil', containing the // overloaded function 'toChars', that converts integral and floating point // types into ASCII strings. // ///Shortest (Textual) Decimal Representation for Binary Floating Point Values ///-------------------------------------------------------------------------- // The floating point 'toChars' implementations (for 'float' and 'double') of // this component provide the shortest (textual) decimal representation that // can (later) be parsed back to the original binary value (i.e., a // "round-trip" conversion). Such round-tripping enables precise, and // human-friendly (textual) communication protocols, and storage formats that // use minimal necessary bandwidth or storage. // // Scientific notation, when chosen, always uses the minimum number of // fractional digits necessary to restore the exact binary floating point // value. The shortest *decimal* notation of a binary floating point number is // text that has enough decimal !fractional! digits so that there can be no // ambiguity in which binary representation value is closest to it. Notice // that the previous sentence only addresses the number of *fractional* digits // in the decimal notation. Floating point values that are mathematically // integer are always written as their exact integer value in decimal notation. // For large integers it would not strictly be necessary to use the exact // decimal value as many integers (differing in some lower-decimal digits) may // resolve to the same binary value, but readers may not expect integers to be // "rounded", so C and C++ chose to standardize on the exact value. // // Note that strictly speaking the C++-defined shortest round trip // representation is not the shortest *possible* one as the C++ scientific // notation is defined to possibly contain up to two extra characters: the sign // of the exponent is always written (even for positive exponents), and at // least 2 decimal digits of the exponent are always written. // // More information about the difficulty of rendering binary floating point // numbers as decimals can be found at // https://bloomberg.github.io/bde/articles/binary_decimal_conversion.html . // In short, IEEE-754 double precision binary floating point numbers ('double') // are guaranteed to round-trip when represented by 17 significant decimal // digits, while single precisions ('float') needs 9 digits. However those // numbers are the *maximum* decimal digits that *may* be necessary, and in // fact many values can be precisely represented precisely by less. 'toChars' // renders the minimum number of digits needed, so that the value can later be // restored. // ///Default Floating Point Format ///----------------------------- // The default floating point format (that is used when no 'format' argument is // present in the signature) uses the shortest representation from the decimal // notation and the scientific notation, favoring decimal notation in case of a // tie. // ///Special Floating Point Values ///----------------------------- // Floating point values may also be special-numerical or non-numerical(*) // values in addition to what we consider normal numbers. // // The special numerical value is really just one, and that is negative zero. // // For non-numerical special value both IEEE-754 and W3C XML Schema Definition // Language (XSD) 1.1(**) 'numericalSpecialRep' requires there to be three // distinct values supported: positive infinity, negative infinity, and NaN. // We represent those values according to the XSD lexical mapping // specification. That also means that these values will round trip in text // *only* if the reader algorithm recognizes those representations. // //.. // +-------------------+----------------+ // | Special Value | Textual Repr. | // +-------------------+----------------+ // | positive zero | "0", "0e+00" | // +-------------------+----------------+ // | negative zero | "-0", "-0e+00" | // +-------------------+----------------+ // | positive infinity | "+INF" | // +-------------------+----------------+ // | negative infinity | "-INF" | // +-------------------+----------------+ // | Not-a-number | "NaN" | // +-------------------+----------------+ //.. // // (*) Non-numerical values do not represent a specific mathematical value. Do // not confuse non-numerical values with Not-a-Number. NaN is just one of // the possible non-numerical values. The positive and negative infinity // represent *all* values too large (in their absolute value) to store. NaN // represents all other values that cannot be represented by a real number. // Non-numerical values normally come from computation results such as the // square root of -1 resulting in Not-a-Number. // // (**) https://www.w3.org/TR/xmlschema11-2/ // ///Usage ///----- // In this section we show the intended use of this component. // ///Example 1: Writing an Integer to a 'streambuf' /// - - - - - - - - - - - - - - - - - - - - - - - // Suppose we want to define a function that writes an 'int' to a 'streambuf'. // We can use 'bsl::to_chars' to write the 'int' to a buffer, then write the // buffer to the 'streambuf'. // // First, we declare our function: //.. // void writeJsonScalar(std::streambuf *result, int value) // // Write the specified 'value', in decimal, to the specified 'result'. // { //.. // Then, we declare a buffer long enough to store any 'int' value in decimal. //.. // char buffer[bslalg::NumericFormatterUtil:: // ToCharsMaxLength<int>::k_VALUE]; // // size large enough to write 'INT_MIN', the // // worst-case value, in decimal. //.. // Next, we call the function: //.. // char *ret = bslalg::NumericFormatterUtil::toChars( // buffer, // buffer + sizeof buffer, // value); //.. // Then, we check that the buffer was long enough, which should always be the // case: //.. // assert(0 != ret); //.. // Now, we write our buffer to the 'streambuf': //.. // result->sputn(buffer, ret - buffer); // } //.. // Finally, we use an output string stream buffer to exercise the // 'writeJsonScalar' function for 'int': //.. // std::ostringstream oss; // std::streambuf* sb = oss.rdbuf(); // // writeJsonScalar(sb, 0); // assert("0" == oss.str()); // // oss.str(""); // writeJsonScalar(sb, 99); // assert("99" == oss.str()); // // oss.str(""); // writeJsonScalar(sb, -1234567890); // worst case: max string length // assert("-1234567890" == oss.str()); //.. // ///Example 2: Writing the Minimal Form of a 'double' ///- - - - - - - - - - - - - - - - - - - - - - - - - // Suppose we want to store a floating point number using decimal text (such as // JSON) for later retrieval, using the minimum number of digits that ensures // we can later restore the same binary floating point value. // // First, we declare our writer function: //.. // void writeJsonScalar(std::streambuf *result, // double value, // bool stringNonNumericValues = false) // // Write the specified 'value' in the shortest round-trip decimal // // format into the specified 'result'. Write non-numeric values // // according to the optionally specified 'stringNonNumericValues' // // either as strings "NaN", "+Infinity", or "-Infinity" when // // 'stringNonNumericValues' is 'true', or a null when it is 'false' or // // not specified. // { //.. // Then, we handle non-numeric values ('toChars' would write them the XSD way): //.. // if (isnan(value) || isinf(value)) { // if (false == stringNonNumericValues) { // JSON standard output // result->sputn("null", 4); // } // else { // Frequent JSON extension // if (isnan(value)) { // result->sputn("\"NaN\"", 5); // } // else if (isinf(value)) { // result->sputn(value < 0 ? "\"-" : "\"+", 2); // result->sputn("Infinity\"", 9); // } // } // return; // RETURN // } //.. // Next, we declare a buffer long enough to store any 'double' value written in // this minimal-length form: //.. // char buffer[bslalg::NumericFormatterUtil:: // ToCharsMaxLength<double>::k_VALUE]; // // large enough to write the longest 'double' // // without a null terminator character. //.. // Then, we call the function: //.. // char *ret = bslalg::NumericFormatterUtil::toChars( // buffer, // buffer + sizeof buffer, // value); //.. // Finally, we can write our buffer to the 'streambuf': //.. // result->sputn(buffer, ret - buffer); // } //.. // Finally, we use the output string stream buffer defined earlier to exercise // the floating point 'writeJsonScalar' function: //.. // oss.str(""); // writeJsonScalar(sb, 20211017.0); // assert("20211017" == oss.str()); // // oss.str(""); // writeJsonScalar(sb, 3.1415926535897932); // assert("3.141592653589793" == oss.str()); // // oss.str(""); // writeJsonScalar(sb, 2e5); // assert("2e+05" == oss.str()); // // oss.str(""); // Non-numeric are written as null by default // writeJsonScalar(sb, std::numeric_limits<double>::quiet_NaN()); // assert("null" == oss.str()); oss.str(""); // // oss.str(""); // Non-numeric can be printed as strings // writeJsonScalar(sb, std::numeric_limits<double>::quiet_NaN(), true); // assert("\"NaN\"" == oss.str()); oss.str(""); //.. // ///Example 3: Determining The Necessary Minimum Buffer Size /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Suppose you are writing code that uses 'bslalg::NumericFormatterUtil' to // convert values to text. Determining the necessary buffer sizes to ensure // successful conversions, especially for floating point types, is non-trivial, // and frankly usually strikes as a distraction in the flow of the work. This // component provides the 'ToCharsMaxLength' 'struct' "overloaded" template // that parallels the overloaded 'toChars' function variants and provides the // well-vetted and tested minimum sufficient buffer size values as compile time // constants. // // Determining the sufficient buffer size for any conversion starts with // determining "What type are we converting?" and "Do we use an argument to // control the conversion, and is that argument a compile time time constant? // // First, because of the descriptive type names we may want to start by locally // shortening them using a 'typedef': //.. // typedef bslalg::NumericFormatterUtil NfUtil; //.. // Next, we determine the sufficient buffer size for converting a 'long' to // decimal. 'long' is a type that has different 'sizeof' on different 64 bit // platforms, so it is especially convenient to have that difference hidden: //.. // const size_t k_LONG_DEC_SIZE = NfUtil::ToCharsMaxLength<long>::k_VALUE; // // Sufficient buffer size to convert any 'long' value to decimal text. //.. // Then, we can write the longest possible 'long' successfully into a buffer: //.. // char longDecimalBuffer[k_LONG_DEC_SIZE]; // // We can write any 'long' in decimal into this buffer using // // 'NfUtil::toChars' safely. // // char *p = NfUtil::toChars(longDecimalBuffer, // longDecimalBuffer + sizeof longDecimalBuffer, // LONG_MIN); // assert(p != 0); //.. // Next, we can get the sufficient size for conversion of an 'unsigned int' to // octal: //.. // const size_t k_UINT_OCT_SIZE = NfUtil::ToCharsMaxLength<unsigned, // 8>::k_VALUE; //.. // Then, if we do not know what 'base' value 'toChars' will use we have to, // assume the longest, which is always base 2: //.. // const size_t k_SHRT_MAX_SIZE = NfUtil::ToCharsMaxLength<short, 2>::k_VALUE; //.. // Now, floating point types have an optional 'format' argument instead of a // 'base', with "default" format as the default, and "fixed" and "scientific" // formats are selectable when a 'format' argument is specified: //.. // const size_t k_DBL_DFL_SIZE = NfUtil::ToCharsMaxLength<double>::k_VALUE; // // const size_t k_FLT_DEC_SIZE = NfUtil::ToCharsMaxLength< // float, // NfUtil::e_FIXED>::k_VALUE; // // const size_t k_DBL_SCI_SIZE = NfUtil::ToCharsMaxLength< // double, // NfUtil::e_SCIENTIFIC>::k_VALUE; //.. // Finally, the longest floating point format is 'e_FIXED', so if the 'format' // argument is not known at compile time, 'e_FIXED' should be used: //.. // const size_t k_DBL_MAX_SIZE = NfUtil::ToCharsMaxLength< // double, // NfUtil::e_FIXED>::k_VALUE; //.. #include <bslscm_version.h> #include <bslmf_assert.h> #include <bslmf_conditional.h> #include <bslmf_isintegral.h> #include <bslmf_issame.h> #include <bslmf_removecv.h> #include <bsls_assert.h> #include <bsls_keyword.h> #include <limits> // 'bsl' cannot be used in 'bslalg' namespace BloombergLP { namespace bslalg { // =========================== // struct NumericFormatterUtil // =========================== struct NumericFormatterUtil { // Namespace 'struct' for free functions supporting 'to_chars'. private: // PRIVATE CLASS METHODS static char *toCharsImpl(char *first, char *last, unsigned value, int base) BSLS_KEYWORD_NOEXCEPT; // Write the specified 'value' into the character buffer starting a the // specified 'first' and ending at the specified 'last', rendering the // value in the specified base 'base'. On success, return a the // address one past the lowest order digit written, on failure, return // 0. The only reason for failure is if the range '[ first, last )' is // not large enough to contain the result. The written result is to // begin at 'first' with leftover room following the return value. The // behavior is undefined unless 'first <= last' and 'base' is in the // range '[ 2 .. 36 ]'. static char *toCharsImpl(char *first, char *last, unsigned long long int value, int base) BSLS_KEYWORD_NOEXCEPT; // Write the specified 'value' into the character buffer starting a the // specified 'first' and ending at the specified 'last', ing the value // in the specified base 'base'. On success, return a the address one // past the lowest order digit written, on failure, return 0. The only // reason for failure is if the range '[ first, last )' is not large // enough to contain the result. The written result is to begin at // 'first' with leftover room following the return value. The behavior // is undefined unless 'first <= last' and 'base' is in the range // '[ 2 .. 36 ]'. template <class TYPE> static char *toCharsIntegral(char *first, char *last, TYPE value, int base) BSLS_KEYWORD_NOEXCEPT; // Write the textual representation of the specified 'value' in the // specified 'base' into the character buffer starting a the specified // 'first' and ending at the specified 'last'. Return the address one // past the lowest order digit written on success, or 0 on failure. // The only possible reason for failure is if the range // '[ first, last )' is not large enough to contain the result. The // written result is to begin at 'first' with leftover room following // the return value. The behavior is undefined unless 'first < last' // and 'base' is in the range '[ 2 .. 36 ]'. The behavior is also // undefined unless the specified 'TYPE' is a fundamental integral type // not larger than 64 bits. static char *toCharsDecimal(char *first, char *last, double value) BSLS_KEYWORD_NOEXCEPT; static char *toCharsDecimal(char *first, char *last, float value) BSLS_KEYWORD_NOEXCEPT; // Write the textual representation of the specified 'value' in decimal // notation into the character buffer starting a the specified 'first' // and ending at the specified 'last'. Return the address one past the // lowest order digit written on success, or 0 on failure. The only // possible reason for failure is if the range '[ first, last )' is not // large enough to contain the result. The written result is to begin // at 'first' with leftover room following the return value. static char *toCharsScientific(char *first, char *last, double value) BSLS_KEYWORD_NOEXCEPT; static char *toCharsScientific(char *first, char *last, float value) BSLS_KEYWORD_NOEXCEPT; // Write the textual representation of the specified 'value' in // scientific notation into the character buffer starting a the // specified 'first' and ending at the specified 'last'. Return the // address one past the lowest order digit of the exponent written on // success, or 0 on failure. The only possible reason for failure is // if the range '[ first, last )' is not large enough to contain the // result. The written result is to begin at 'first' with leftover // room following the return value. private: // NOT IMPLEMENTED static char *toChars(char*, char*, bool, int = 10) BSLS_KEYWORD_NOEXCEPT BSLS_KEYWORD_DELETED; // This deleted/private method declaration exists to prevent 'toChars' // being called with a 'bool' input argument. private: // PRIVATE TYPES enum { k_MAXLEN_ARG_DEFAULT = -256 }; // This enumerator is used internally to achieve "type safe" // pseudo-overloading of the 'ToCharsMaxLength' template to mimic the // different 'toChars' class method overloads. // PRIVATE METAFUNCTIONS template <class FLT_TYPE, int FORMAT> struct FltMaxLen; // This 'struct' template implements a meta-function to determine the // minimum sufficient size (in characters) of an output buffer to // successfully convert any value of the specified 'FLT_TYPE' floating // point type into text of the specified 'FORMAT' using 'toChars'. // Format may be any of the 'Format' enumerator values or an // unspecified value for default format. The floating point types are // assumed to be IEEE-754 binary types. The result ("return value") is // a member enumerator 'k_VALUE'. This meta-function is private and // contains no defensive checks. template <bool IS_SIGNED, unsigned SIZEOF, int BASE> struct IntMaxLen; // This 'struct' template implements a meta-function to determine the // minimum sufficient size (in characters) of an output buffer to // successfully convert any value of a fundamental integer type to text // into the specified 'BASE' using 'toChars'. The integer type is // described by the specified 'IS_SIGNED' and 'SIZEOF' parameters, and // is assumed to be two's complement. The result ("return value") is // an enumerator 'k_VALUE'. The meta-function is private and contains // no defensive checks. template <class TYPE> struct IsSupportedFloatingPoint; // This 'struct' template implements the meta-function to determine if // the specified 'TYPE' is a supported floating point type for the // 'toChars' overloaded method-set. The result ("return value") is an // enumerator 'k_SUPPORTED' that has a non-zero value if 'TYPE' is a // supported floating point type, or zero otherwise. template <class TYPE> struct IsSupportedIntegral; // This 'struct' template implements the meta-function to determine if // the specified 'TYPE' is a supported integral type for the 'toChars' // overloaded method-set. The result ("return value") is an enumerator // 'k_SUPPORTED' that has a non-zero value if 'TYPE' is a supported // integral type, or zero otherwise. public: // PUBLIC TYPES enum Format { // This enumeration lists the supported, explicitly specified 'toChars' // formatting options for floating point values. (The default format // option, shortest round-trip, is specified implicitly by omitting the // 'format' argument.) Additional enumerators are reserved for future // use: //: 'e_HEX = 0x100' //: 'e_GENERAL = e_SCIENTIFIC | e_FIXED' // and correspond to the C++17 'std::chars_format' 'hex', and 'general' // enumerators. See {Adding 'Format's} in the {Implementation Notes} // of the implementation (.cpp) file. e_SCIENTIFIC = 0x40, e_FIXED = 0x80 }; // PUBLIC METAFUNCTIONS template <class TYPE, int ARG = k_MAXLEN_ARG_DEFAULT> struct ToCharsMaxLength; // This 'struct' template implements the meta-function to determine the // minimum sufficient size of a buffer to successfully convert any // numeric value of a specified 'TYPE' supported by one of the // 'toChars' function overloads in 'NumericFormatterUtil'. The // meta-function allows specifying an argument value to the 'toChars' // overloads, as a non-type template parameter. That value stands for // the 'base' parameter for integral conversions, and the 'format' // parameter for floating point conversions. A second non-type // template parameter is reserved for further addition in case the // 'precision' parameter overloads are implemented for floating point // conversions (in addition to the 'format' parameter). The // compile-time "return value" of 'ToCharsMaxLength' is an enumerator // name 'k_VALUE'. For usage examples see {Example 3: Determining The // Required Buffer Size}. // PUBLIC CLASS METHODS static char *toChars(char *first, char *last, char value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, signed char value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, unsigned char value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, signed short int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, unsigned short int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, signed int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, unsigned int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, signed long int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, unsigned long int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, signed long long int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, unsigned long long int value, int base = 10) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, double value) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, float value) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, double value, Format format) BSLS_KEYWORD_NOEXCEPT; static char *toChars(char *first, char *last, float value, Format format) BSLS_KEYWORD_NOEXCEPT; // Write the specified 'value' into the character buffer starting a the // specified 'first' and ending at the specified 'last', 'last' not // included. In integer conversions, if the optionally specified // 'base' argument is not present or specified, base 10 is used. In // floating point conversions, if the optionally specified 'format' // argument is not present or specified, the {Default Floating Point // Format} is used. If a 'format' argument is specified ('e_DECIMAL' // or 'e_SCIENTIFIC'), the {Shortest (Textual) Decimal Representation // for Binary Floating Point Values} is used in that format (that will // produce the exact binary floating point 'value' when converted back // to the original type from text), but see possible exceptions under // {Special Floating Point Values}. Return the address one past the // last character (lowest order digit or last digit of the exponent) // written on success, or '0' on failure. The only reason for failure // is when the range '[ first, last )' is not large enough to contain // the result. The written result is to begin at 'first' with leftover // room following the return value. The behavior is undefined unless // 'first <= last', and 'base' is in the range '[ 2 .. 36 ]'. Note // that the type 'bool' for the 'value' parameter is explicitly // disabled in the "NOT IMPLEMENTED" 'private' section, because 'bool' // would otherwise be promoted to 'int' and printed as '0' or '1', // instead of the (possibly) expected 'false' and 'true'; and 'bool' // is not an integral or numeric type either. Also note that these // functions do !not! null-terminate the result. }; // ----------------------------------------------- // template struct NumericFormatterUtil::FltMaxLen // ----------------------------------------------- template <class FLT_TYPE, int FORMAT> struct NumericFormatterUtil::FltMaxLen { private: // PRIVATE TYPES typedef typename bsl::remove_cv<FLT_TYPE>::type NoCvT; // For more readable lines below. public: // PUBLIC TYPES enum Enum { // The default and 'e_SCIENTIFIC' formats have the same maximum size, // so we simplify the expression. k_VALUE = bsl::is_same<NoCvT, double>::value ? FORMAT == NumericFormatterUtil::e_FIXED ? 327 : 24 : FORMAT == NumericFormatterUtil::e_FIXED ? 48 : 15 }; }; // ----------------------------------------------- // template struct NumericFormatterUtil::IntMaxLen // ----------------------------------------------- #define BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(issigned, bytes, \ val02, val03, val04, val05, val06, val07, val08, val09, val10, \ val11, val12, val13, val14, val15, val16, val17, val18, val19, val20, \ val21, val22, val23, val24, val25, val26, val27, val28, val29, val30, \ val31, val32, val33, val34, val35, val36) \ template <int BASE> \ struct NumericFormatterUtil::IntMaxLen<issigned, bytes, BASE> { \ enum Enum { \ k_VALUE = (BASE == 2) ? val02 \ : (BASE == 3) ? val03 \ : (BASE == 4) ? val04 \ : (BASE == 5) ? val05 \ : (BASE == 6) ? val06 \ : (BASE == 7) ? val07 \ : (BASE == 8) ? val08 \ : (BASE == 9) ? val09 \ : (BASE == 10) ? val10 \ : (BASE == 11) ? val11 \ : (BASE == 12) ? val12 \ : (BASE == 13) ? val13 \ : (BASE == 14) ? val14 \ : (BASE == 15) ? val15 \ : (BASE == 16) ? val16 \ : (BASE == 17) ? val17 \ : (BASE == 18) ? val18 \ : (BASE == 19) ? val19 \ : (BASE == 20) ? val20 \ : (BASE == 21) ? val21 \ : (BASE == 22) ? val22 \ : (BASE == 23) ? val23 \ : (BASE == 24) ? val24 \ : (BASE == 25) ? val25 \ : (BASE == 26) ? val26 \ : (BASE == 27) ? val27 \ : (BASE == 28) ? val28 \ : (BASE == 29) ? val29 \ : (BASE == 30) ? val30 \ : (BASE == 31) ? val31 \ : (BASE == 32) ? val32 \ : (BASE == 33) ? val33 \ : (BASE == 34) ? val34 \ : (BASE == 35) ? val35 \ : (BASE == 36) ? val36 \ : val10 /* default value */ \ }; \ } // BDE_VERIFY pragma: push // Relax mandatory tag rules for macro-created // BDE_VERIFY pragma: -KS00 // template specializations BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(true, 1, // int8_t 9, // base 2: "-10000000" 6, // base 3: "-11202" 5, // base 4: "-2000" 5, // base 5: "-1003" 4, // base 6: "-332" 4, // base 7: "-242" 4, // base 8: "-200" 4, // base 9: "-152" 4, // base 10: "-128" 4, // base 11: "-107" 3, // base 12: "-a8" 3, // base 13: "-9b" 3, // base 14: "-92" 3, // base 15: "-88" 3, // base 16: "-80" 3, // base 17: "-79" 3, // base 18: "-72" 3, // base 19: "-6e" 3, // base 20: "-68" 3, // base 21: "-62" 3, // base 22: "-5i" 3, // base 23: "-5d" 3, // base 24: "-58" 3, // base 25: "-53" 3, // base 26: "-4o" 3, // base 27: "-4k" 3, // base 28: "-4g" 3, // base 29: "-4c" 3, // base 30: "-48" 3, // base 31: "-44" 3, // base 32: "-40" 3, // base 33: "-3t" 3, // base 34: "-3q" 3, // base 35: "-3n" 3); // base 36: "-3k" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(false, 1, // uint8_t 8, // base 2: "11111111" 6, // base 3: "100110" 4, // base 4: "3333" 4, // base 5: "2010" 4, // base 6: "1103" 3, // base 7: "513" 3, // base 8: "377" 3, // base 9: "313" 3, // base 10: "255" 3, // base 11: "212" 3, // base 12: "193" 3, // base 13: "168" 3, // base 14: "143" 3, // base 15: "120" 2, // base 16: "ff" 2, // base 17: "f0" 2, // base 18: "e3" 2, // base 19: "d8" 2, // base 20: "cf" 2, // base 21: "c3" 2, // base 22: "bd" 2, // base 23: "b2" 2, // base 24: "af" 2, // base 25: "a5" 2, // base 26: "9l" 2, // base 27: "9c" 2, // base 28: "93" 2, // base 29: "8n" 2, // base 30: "8f" 2, // base 31: "87" 2, // base 32: "7v" 2, // base 33: "7o" 2, // base 34: "7h" 2, // base 35: "7a" 2); // base 36: "73" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(true, 2, // int16_t 17, // base 2: "-1000000000000000" 11, // base 3: "-1122221122" 9, // base 4: "-20000000" 8, // base 5: "-2022033" 7, // base 6: "-411412" 7, // base 7: "-164351" 7, // base 8: "-100000" 6, // base 9: "-48848" 6, // base 10: "-32768" 6, // base 11: "-2268a" 6, // base 12: "-16b68" 6, // base 13: "-11bb8" 5, // base 14: "-bd28" 5, // base 15: "-9a98" 5, // base 16: "-8000" 5, // base 17: "-6b69" 5, // base 18: "-5b28" 5, // base 19: "-4eec" 5, // base 20: "-41i8" 5, // base 21: "-3b68" 5, // base 22: "-31fa" 5, // base 23: "-2flg" 5, // base 24: "-28l8" 5, // base 25: "-22ai" 5, // base 26: "-1mc8" 5, // base 27: "-1hph" 5, // base 28: "-1dm8" 5, // base 29: "-19rr" 5, // base 30: "-16c8" 5, // base 31: "-1331" 5, // base 32: "-1000" 4, // base 33: "-u2w" 4, // base 34: "-sbq" 4, // base 35: "-qq8" 4); // base 36: "-pa8" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(false, 2, // uint16_t 16, // base 2: "1111111111111111" 11, // base 3: "10022220020" 8, // base 4: "33333333" 7, // base 5: "4044120" 7, // base 6: "1223223" 6, // base 7: "362031" 6, // base 8: "177777" 6, // base 9: "108806" 5, // base 10: "65535" 5, // base 11: "45268" 5, // base 12: "31b13" 5, // base 13: "23aa2" 5, // base 14: "19c51" 5, // base 15: "14640" 4, // base 16: "ffff" 4, // base 17: "d5d0" 4, // base 18: "b44f" 4, // base 19: "9aa4" 4, // base 20: "83gf" 4, // base 21: "71cf" 4, // base 22: "638j" 4, // base 23: "58k8" 4, // base 24: "4hif" 4, // base 25: "44la" 4, // base 26: "3iof" 4, // base 27: "38o6" 4, // base 28: "2rgf" 4, // base 29: "2jqo" 4, // base 30: "2cof" 4, // base 31: "2661" 4, // base 32: "1vvv" 4, // base 33: "1r5u" 4, // base 34: "1mnh" 4, // base 35: "1ihf" 4); // base 36: "1ekf" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(true, 4, // int32_t 33, // base 2: "-10000000000000000000000000000000" 21, // base 3: "-12112122212110202102" 17, // base 4: "-2000000000000000" 15, // base 5: "-13344223434043" 13, // base 6: "-553032005532" 13, // base 7: "-104134211162" 12, // base 8: "-20000000000" 11, // base 9: "-5478773672" 11, // base 10: "-2147483648" 10, // base 11: "-a02220282" 10, // base 12: "-4bb2308a8" 10, // base 13: "-282ba4aab" 10, // base 14: "-1652ca932" 9, // base 15: "-c87e66b8" 9, // base 16: "-80000000" 9, // base 17: "-53g7f549" 9, // base 18: "-3928g3h2" 9, // base 19: "-27c57h33" 9, // base 20: "-1db1f928" 9, // base 21: "-140h2d92" 8, // base 22: "-ikf5bf2" 8, // base 23: "-ebelf96" 8, // base 24: "-b5gge58" 8, // base 25: "-8jmdnkn" 8, // base 26: "-6oj8ioo" 8, // base 27: "-5ehnckb" 8, // base 28: "-4clm98g" 8, // base 29: "-3hk7988" 8, // base 30: "-2sb6cs8" 8, // base 31: "-2d09uc2" 8, // base 32: "-2000000" 8, // base 33: "-1lsqtl2" 8, // base 34: "-1d8xqrq" 8, // base 35: "-15v22un" 7); // base 36: "-zik0zk" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(false, 4, // uint32_t 32, // base 2: "11111111111111111111111111111111" 21, // base 3: "102002022201221111210" 16, // base 4: "3333333333333333" 14, // base 5: "32244002423140" 13, // base 6: "1550104015503" 12, // base 7: "211301422353" 11, // base 8: "37777777777" 11, // base 9: "12068657453" 10, // base 10: "4294967295" 10, // base 11: "1904440553" 9, // base 12: "9ba461593" 9, // base 13: "535a79888" 9, // base 14: "2ca5b7463" 9, // base 15: "1a20dcd80" 8, // base 16: "ffffffff" 8, // base 17: "a7ffda90" 8, // base 18: "704he7g3" 8, // base 19: "4f5aff65" 8, // base 20: "3723ai4f" 8, // base 21: "281d55i3" 8, // base 22: "1fj8b183" 8, // base 23: "1606k7ib" 7, // base 24: "mb994af" 7, // base 25: "hek2mgk" 7, // base 26: "dnchbnl" 7, // base 27: "b28jpdl" 7, // base 28: "8pfgih3" 7, // base 29: "76beigf" 7, // base 30: "5qmcpqf" 7, // base 31: "4q0jto3" 7, // base 32: "3vvvvvv" 7, // base 33: "3aokq93" 7, // base 34: "2qhxjlh" 7, // base 35: "2br45qa" 7); // base 36: "1z141z3" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(true, 8, // int64_t 65, // base 2: "-10000000000000000000....00000000000000000000" 41, // base 3: "-2021110011022210012102010021220101220222" 33, // base 4: "-20000000000000000000000000000000" 29, // base 5: "-1104332401304422434310311213" 26, // base 6: "-1540241003031030222122212" 24, // base 7: "-22341010611245052052301" 23, // base 8: "-1000000000000000000000" 21, // base 9: "-67404283172107811828" 20, // base 10: "-9223372036854775808" 20, // base 11: "-1728002635214590698" 19, // base 12: "-41a792678515120368" 19, // base 13: "-10b269549075433c38" 18, // base 14: "-4340724c6c71dc7a8" 18, // base 15: "-160e2ad3246366808" 17, // base 16: "-8000000000000000" 17, // base 17: "-33d3d8307b214009" 17, // base 18: "-16agh595df825fa8" 16, // base 19: "-ba643dci0ffeehi" 16, // base 20: "-5cbfjia3fh26ja8" 16, // base 21: "-2heiciiie82dh98" 16, // base 22: "-1adaibb21dckfa8" 15, // base 23: "-i6k448cf4192c3" 15, // base 24: "-acd772jnc9l0l8" 15, // base 25: "-64ie1focnn5g78" 15, // base 26: "-3igoecjbmca688" 15, // base 27: "-27c48l5b37oaoq" 15, // base 28: "-1bk39f3ah3dmq8" 14, // base 29: "-q1se8f0m04isc" 14, // base 30: "-hajppbc1fc208" 14, // base 31: "-bm03i95hia438" 14, // base 32: "-8000000000000" 14, // base 33: "-5hg4ck9jd4u38" 14, // base 34: "-3tdtk1v8j6tpq" 14, // base 35: "-2pijmikexrxp8" 14); // base 36: "-1y2p0ij32e8e8" BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE(false, 8, // uint64_t 64, // base 2: "11111111111111111111....11111111111111111111" 41, // base 3: "11112220022122120101211020120210210211220" 32, // base 4: "33333333333333333333333333333333" 28, // base 5: "2214220303114400424121122430" 25, // base 6: "3520522010102100444244423" 23, // base 7: "45012021522523134134601" 22, // base 8: "1777777777777777777777" 21, // base 9: "145808576354216723756" 20, // base 10: "18446744073709551615" 19, // base 11: "335500516a429071284" 18, // base 12: "839365134a2a240713" 18, // base 13: "219505a9511a867b72" 17, // base 14: "8681049adb03db171" 17, // base 15: "2c1d56b648c6cd110" 16, // base 16: "ffffffffffffffff" 16, // base 17: "67979g60f5428010" 16, // base 18: "2d3fgb0b9cg4bd2f" 16, // base 19: "141c8786h1ccaagg" 15, // base 20: "b53bjh07be4dj0f" 15, // base 21: "5e8g4ggg7g56dif" 15, // base 22: "2l4lf104353j8kf" 15, // base 23: "1ddh88h2782i515" 14, // base 24: "l12ee5fn0ji1if" 14, // base 25: "c9c336o0mlb7ef" 14, // base 26: "7b7n2pcniokcgf" 14, // base 27: "4eo8hfam6fllmo" 14, // base 28: "2nc6j26l66rhof" 14, // base 29: "1n3rsh11f098rn" 14, // base 30: "14l9lkmo30o40f" 13, // base 31: "nd075ib45k86f" 13, // base 32: "fvvvvvvvvvvvv" 13, // base 33: "b1w8p7j5q9r6f" 13, // base 34: "7orp63sh4dphh" 13, // base 35: "5g24a25twkwff" 13); // base 36: "3w5e11264sgsf" // BDE_VERIFY pragma: pop // End of macro-created template specializations #undef BSLALG_NUMERICFORMATTERUTIL_INTMAXLEN_ONE // ============================================================== // template struct NumericFormatterUtil::IsSupportedFloatingPoint // ============================================================== template <class TYPE> struct NumericFormatterUtil::IsSupportedFloatingPoint { private: // PRIVATE TYPES typedef typename bsl::remove_cv<TYPE>::type NoCvT; // For more readable lines below. public: // PUBLIC TYPES enum Enum { k_SUPPORTED = bsl::is_same<NoCvT, float>::value || bsl::is_same<NoCvT, double>::value }; }; // --------------------------------------------------------- // template struct NumericFormatterUtil::IsSupportedIntegral // --------------------------------------------------------- template <class TYPE> struct NumericFormatterUtil::IsSupportedIntegral { private: // PRIVATE TYPES typedef typename bsl::remove_cv<TYPE>::type NoCvT; // For more readable lines below. public: // PUBLIC TYPES enum Enum { k_SUPPORTED = bsl::is_same<NoCvT, char >::value || bsl::is_same<NoCvT, signed char >::value || bsl::is_same<NoCvT, unsigned char >::value || bsl::is_same<NoCvT, signed short int >::value || bsl::is_same<NoCvT, unsigned short int >::value || bsl::is_same<NoCvT, signed int >::value || bsl::is_same<NoCvT, unsigned int >::value || bsl::is_same<NoCvT, signed long int >::value || bsl::is_same<NoCvT, unsigned long int >::value || bsl::is_same<NoCvT, signed long long int >::value || bsl::is_same<NoCvT, unsigned long long int >::value }; }; // ------------------------------------------------------ // template struct NumericFormatterUtil::ToCharsMaxLength // ------------------------------------------------------ template <class TYPE, int ARG> struct NumericFormatterUtil::ToCharsMaxLength { private: // PRIVATE TYPES typedef const bool Cbool; // This type alias exists to make the lines below shorter, therefore // easier to read at a glance. // PRIVATE CONSTANTS // Type Related static Cbool k_INTEGRAL_TYPE = IsSupportedIntegral<TYPE>::k_SUPPORTED; static Cbool k_FLOAT_TYPE = IsSupportedFloatingPoint<TYPE>::k_SUPPORTED; static Cbool k_UNSUPPORTED_INPUT_TYPE = !k_INTEGRAL_TYPE && !k_FLOAT_TYPE; // Argument Related static Cbool k_ARG_NOT_BASE = (ARG < 2 || ARG > 37); static Cbool k_ARG_NOT_FORMAT = (ARG != e_FIXED && ARG != e_SCIENTIFIC); static Cbool k_ARGUMENT_VALUE_IS_WRONG = !( // We negate the valid cases // for easier human consumption (ARG == k_MAXLEN_ARG_DEFAULT) || // The default value is always valid, regardless of the type (k_INTEGRAL_TYPE && !k_ARG_NOT_BASE) || // Integral types with a valid 'base' value (2-36) are supported (k_FLOAT_TYPE && !k_ARG_NOT_FORMAT) || // Floating point types with a valid 'format' are supported (k_UNSUPPORTED_INPUT_TYPE && !(k_ARG_NOT_BASE && k_ARG_NOT_FORMAT))); // In case of an unsupported type we accept any argument value that // would be supported for any valid (supported) type. This way we // avoid burdening the user (programmer) with unhelpful extra // compiler error messages. // CONTRACT VERIFICATION BSLMF_ASSERT(false == k_UNSUPPORTED_INPUT_TYPE); BSLMF_ASSERT(false == k_ARGUMENT_VALUE_IS_WRONG); public: // PUBLIC TYPES enum ValueType { k_VALUE = k_INTEGRAL_TYPE ? IntMaxLen<std::numeric_limits<TYPE>::is_signed, static_cast<unsigned>(sizeof(TYPE)), ARG>::k_VALUE : k_FLOAT_TYPE ? FltMaxLen<TYPE, ARG>::k_VALUE : -1 // In case of bad 'TYPE' or 'ARG' }; }; // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ // --------------------------- // struct NumericFormatterUtil // --------------------------- // PPRIVATE CLASS METHODS template <class TYPE> inline char *NumericFormatterUtil::toCharsIntegral(char *first, char *last, TYPE value, int base) BSLS_KEYWORD_NOEXCEPT { BSLS_ASSERT_SAFE(2 <= base); BSLS_ASSERT_SAFE(base <= 36); BSLS_ASSERT_SAFE(first <= last); BSLMF_ASSERT(bsl::is_integral<TYPE>::value); BSLMF_ASSERT(sizeof(TYPE) <= sizeof(unsigned long long int)); typedef typename bsl::conditional<(sizeof(unsigned) < sizeof(TYPE)), unsigned long long int, unsigned>::type VirtualUnsignedType; if (first == last) { // The ISO equivalent of this function allows empty ranges, so we shall // allow them, too. The early return is necessary due to the sign // "trick" below. return 0; // RETURN } // Note that if 'value' is a negative value and 'TYPE' is smaller than // 'VirtualUnsignedType', assigning it here will extend the sign, even // though 'VirtualUnsignedType' is an unsigned type. VirtualUnsignedType uValue = value; if (value < 0) { uValue = ~uValue + 1; // Absolute value -- note this works even for // 'numeric_limits<TYPE>::min()'. *first++ = '-'; } return toCharsImpl(first, last, uValue, base); } // PUBLIC CLASS METHODS inline char *NumericFormatterUtil::toChars(char *first, char *last, char value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, signed char value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, unsigned char value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char * NumericFormatterUtil::toChars(char *first, char *last, signed short int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, unsigned short int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, signed int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, unsigned int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, signed long int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, unsigned long int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, signed long long int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, unsigned long long int value, int base) BSLS_KEYWORD_NOEXCEPT { return toCharsIntegral(first, last, value, base); } inline char *NumericFormatterUtil::toChars(char *first, char *last, double value, Format format) BSLS_KEYWORD_NOEXCEPT { switch (format) { case e_FIXED: return toCharsDecimal(first, last, value); // RETURN case e_SCIENTIFIC: return toCharsScientific(first, last, value);// RETURN } BSLS_ASSERT_INVOKE_NORETURN("Invalid 'format' argument value."); return 0; // To avoid warning from AIX xlC } inline char *NumericFormatterUtil::toChars(char *first, char *last, float value, Format format) BSLS_KEYWORD_NOEXCEPT { switch (format) { case e_FIXED: return toCharsDecimal(first, last, value); // RETURN case e_SCIENTIFIC: return toCharsScientific(first, last, value);// RETURN } BSLS_ASSERT_INVOKE_NORETURN("Invalid 'format' argument value."); return 0; // To avoid warning from AIX xlC } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2021 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------