// 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 ----------------------------------