// bdldfp_decimalconvertutil.h                                        -*-C++-*-
#ifndef INCLUDED_BDLDFP_DECIMALCONVERTUTIL
#define INCLUDED_BDLDFP_DECIMALCONVERTUTIL

#include <bsls_ident.h>
BSLS_IDENT("$Id$")

//@PURPOSE: Provide decimal floating-point conversion functions.
//
//@CLASSES:
//  bdldfp::DecimalConvertUtil: Namespace for decimal FP conversion functions
//
//@SEE_ALSO: bdldfp_decimal, bdldfp_decimalplatform
//
//@DESCRIPTION: This component provides namespace,
// `bdldfp::DecimalConvertUtil`, containing functions that are able to convert
// between the native decimal types of the platform and various other possible
// representations, such as binary floating-point, network encoding formats.
//
///Encoding Formats
///----------------
// This utility contains functions to encode decimal values to and from three
// different encoding formats:
//
// * the IEEE decimal interchange format using decimal encoding for the
//   significant (also known as the Densely Packed Decimal format, see IEEE
//   754. - 2008, section 3.5.2, for more details)
// * the multi-width encoding format, which is a custom format that can encode
//   subsets of decimal values using a smaller number of bytes
// * the variable-width encoding format, which is a custom format that is
//   similar to the multi-width encoding format with the main difference being
//   that it self describes its own width
//
// 64-bit decimal values encoded by the IEEE decimal interchange format always
// uses 8 bytes, which can be inefficient.  The two custom encoding formats
// provided by this to enable more space efficient encoding of values commonly
// encountered by financial applications.
//
// In the full IEEE encoding, 50 bits are used for the trailing bits of the
// mantissa, 13 bit is used for the combination field (exponent + special
// states to indicate NaN and Inf values + leading bits of the mantissa), and 1
// bit is used for the significant.  The basic idea for the custom encoding
// formats is that the mantissa and exponent of many values (in typical
// financial applications) can fit into fewer bits than those provided by the
// full encoding.  We can define a set of narrow formats to encode these
// smaller values without loss of precision.  For example, a ticker values less
// than 100 dollars with a 2 decimal places of precision can be encoded using a
// 2 bytes encoding, using no sign bit, 3 bits for the exponent, and 13 bits
// for the mantissa.
//
///IEEE Decimal Interchange Format
///- - - - - - - - - - - - - - - -
// The IEEE decimal interchange format is defined by the IEEE standard.  64 bit
// decimal values encoded by this format always uses 8 bytes.  The
// `decimalFromNetwork` and `decimalToNetwork` functions can be used encode to
// and decode from this format.
//
///Multi-Width Encoding Format
///- - - - - - - - - - - - - -
// The multi-width encoding format uses a set of narrow encoding formats having
// sizes smaller than that used by the for IEEE format.  Each of the narrower
// encoding format is used to encode a subset of values that can be represented
// by the full format.  The following configuration is used to encode 64-bit
// decimal values:
//
// ```
// |------|----------|----------|-----|----------|----------------|
// | size | S (bits) | E (bits) |   B | T (bits) | max signficand |
// |------|----------|----------|-----|----------|----------------|
// |   1* |        0 |        1 |  -2 |        7 |            127 |
// |   2  |        0 |        2 |  -3 |       14 |          16383 |
// |   3  |        0 |        3 |  -6 |       21 |        2097151 |
// |   4  |        1 |        5 | -16 |       26 |       67108863 |
// |   5  |        1 |        5 | -16 |       34 |    17179869183 |
// |------|-------------------------------------------------------|
// |    8 |            FULL IEEE INTERCHANGE FORMAT**             |
// |------|-------------------------------------------------------|
//
// S = sign, E = exponent, B = bias, T = significant
//
// * 1 byte encoding will be supported by the decoder but not the encoder. This
//   is done due to the relatively large performance impact of adding the 1
//   byte encoding to the encoder (10%). Preserving the encoding size in the
//   decoder allows us to easily enable this encoding size at a later time.
//
// ** If the value to be encoded can not fit in the 5-byte encoding or is -Inf,
//    +Inf, or Nan, then the full 8-byte IEEE format will be used.
// ```
//
// Since the multi-width encoding format consists of subformats having varying
// widths, the size of the subformat used must be supplied long with the
// encoding to the decode function.  This is not required for either the IEEE
// format or the variable-width encoding format.
//
// The `decimal64ToMultiWidthEncoding` and `decimal64FromMultiWidthEncoding`
// can be used to encode to and decode from this format.  Currently, only
// 64-bit decimal values are supported by this encoding format.
//
///Variable-Width Encoding Formats
///- - - - - - - - - - - - - - - -
// The variable-width encoding format can encode decimal values using a
// variable number of bytes, similar to the multi-width encoding format.  The
// difference is that the variable-width encoding format can self-describe its
// own size using special state (typically, predicate bits), so the decode
// function does not require the size of the encoding to work.  The following
// configuration is used to encode 64-bit decimal values:
//
// ```
// |------|------------|---|---|-----|----|-----------------|
// | size |          P | S | E |   B |  T | max significant |
// |------|------------|---|---|-----|----|-----------------|
// |    2 |        0b0 | 0 | 2 |  -2 | 13 |            8191 |
// |    3 |       0b10 | 0 | 3 |  -4 | 19 |          524287 |
// |    4 |       0b11 | 1 | 5 | -16 | 24 |        16777215 |
// |------|------------|------------------------------------|
// |    9 | 0b11111111 |        FULL IEEE FORMAT*           |
// |------|------------|------------------------------------|
//
// P = predicate (bit values)
// S = sign (bits), E = exponent (bits), B = bias
// T = significant (bits)
//
// * If the value to be encoded can not fit in the 4-byte encoding or is -Inf,
//   +Inf, or Nan, then the full 8-byte IEEE format will be used prefixed by a
//   1 byte predicate having the value of 0xFF.
// ```
//
// The `decimal64ToVariableWidthEncoding` and
// `decimal64FromVariableWidthEncoding` can be used to encode to and decode
// from this format.  Currently, only 64-bit decimal values are supported by
// this encoding format.
//
///Conversion between Binary to Decimal
///------------------------------------
// The desire to convert numbers from binary to decimal format is fraught with
// misunderstanding, and is often accompanied by ill-conceived attempts to
// "correct rounding errors" and otherwise coerce results into aesthetically
// pleasing forms.  In the Bloomberg environment, there is the additional
// complication of IBM/Perkin-Elmer/Interdata floating point.  This is a
// floating-point format that uses a base-16 rather than a base-2 underlying
// representation, and the Bloomberg environment contains numbers that began as
// decimals, were converted to IBM format, and then were converted from IBM
// format to IEEE format.
//
// Generically, when a decimal is converted to floating-point (using, for
// example, scanf from text, or `DecimalConvertUtil::decimalToDouble`), the
// result is the representable binary number nearest in value to that decimal.
// If there are two such, the one whose least significant bit is 0 is generally
// chosen.  (This is also true for conversion of decimals to 32-bit
// Perkin-Elmer format, but when the Perkin-Elmer float is then converted to
// IEEE float, the resulting value is not the same as converting the decimal to
// IEEE float directly, because the Perkin-Elmer format can lose up to three
// bits of representation compared to the IEEE format.)  Unless the decimal
// value is exactly a multiple of a power of two (e.g., 3.4375 = 55 * 1/16),
// the converted binary value cannot equal the decimal value, it can only be
// close.  This utility provides `decimal{,32,64,128}To{Float,Double}`
// functions to convert decimal floating-point numbers to their closest binary
// floating-point values.
//
// When converting from decimal to binary, it is hoped that two different
// decimal values (in the representable range of the target binary type)
// convert to two different binary values.  There is a maximum number of
// significant digits for which this will be true.  For example, all decimals
// with 6 significant digits convert to distinct `float` values, but 8589973000
// and 8589974000, with 7 significant digits, convert to the same `float`
// value.  Similarly, all decimals with 15 significant digits convert to unique
// `double` values but 900719925474.0992 and 900719925474.0993, with 16
// significant digits, convert to the same `double` value.  Over restricted
// ranges, the maximum may be higher - for example, every decimal value with 7
// or fewer significant digits between 1e-3 and 8.5e9 converts to a unique
// `float` value.
//
// Because binary floating-point values are generally not equal to their
// decimal progenitors, "converting from binary to decimal" does not have a
// single meaning, and programmers who seek such an operation therefore need to
// know and specify the conversion they want.  Common examples of conversions a
// programmer might seek are listed below:
//
// 1. Express the value as its nearest decimal value.
//   - For this conversion, use the conversion constructors:
//   - `Decimal{32,64,128}(value)`
// 2. Express the value rounded to a given number of significant digits.  (The
//    significant digits of a decimal number are the digits with all leading
//    and trailing 0s removed; e.g., 0.00103, 10.3 and 10300 each have 3
//    significant digits.)  This conversion is the one that leads programmers
//    to complain about "rounding error" (for example, .1f rounded to 9 digits
//    is .100000001) but is the appropriate one to use when the programmer
//    knows that the binary value was originally converted from a decimal value
//    with that many significant digits.
//   - For this conversion, use:
//   - `Decimal{32,64,128}From{Float,Double}(value, digits)`
// 3. Express the value using the minimum number of significant digits for the
//    type of the binary such that converting the decimal value back to binary
//    will yield the same value.  (Note that 17 digits are needed for `double`
//    and 9 for `float`, so not all decimal types can hold such a result.)
//   - For this conversion, use:
//   - `Decimal{64,128}FromFloat(value, 9)` or
//   - `Decimal128FromDouble(value, 17)`
// 4. Express the value using a number of decimal places that restores the
//    original decimal value from which the binary value was converted,
//    assuming that the original decimal value had sufficiently few significant
//    digits so that no two values with that number of digits would convert to
//    the same binary value.  (That number is 15 for `double` and 6 for `float`
//    in general but 7 over a limited range that spans `[1e-3 .. 8.5e9]`).
//   - For this conversion, use:
//   - `Decimal{32,64,128}From{Float,Double}(value)`
// 5. Express the value as the shortest decimal number that converts back
//    exactly to the binary value.  For example. given the binary value
//:   0x3DCCCCCD above, that corresponding shortest decimal value is
//:   (unsurprisingly) .1, while the next lower value 0x3DCCCCCC has the
//:   shortest decimal .099999994 and the next higher value 0x3DCCCCCE has the
//:   shortest decimal .010000001.  This is the most visually appealing result,
//:   but can be expensive and slow to compute.
//:
//   - For this conversion, use:
//   - `Decimal{32,64,128}From{Float,Double}(value, -1)`
// 6. Express the value using a number of decimal places that restores the
//    original decimal value assuming that it is a `float` which originated as
//    an IBM/Perkin-Elmer/Interdata `float` value itself originally converted
//    from a decimal value.
//   - For this conversion, use:
//   - `Decimal{32,64,128}FromFloat(value, 6)`
// 7. Express the value exactly as a decimal.  For example, the decimal
//    value .1 converts to the 32-bit IEEE float value 0x3DCCCCCD, which has
//    the exact value .100000001490116119384765625.  This conversion is seldom
//    useful, except perhaps for debugging, since the exact value may have over
//   1000. digits, and as well cannot be represented as a decimal
//    floating-point type since those types do not have enough digits.
//   - For this conversion, use `sprintf` into a large-enough buffer:
//   - `char buf[2000]; double value; sprintf(buf, "%.1100f", value);`
//   - The result will have trailing 0s, which may be trimmed.
// 8. Express the value rounded to a given number of decimal places.  (The
//    decimal places of a decimal number are the number of digits after the
//    decimal point, with trailing 0s removed; .01, 10.01, and 1000.01 each
//    have two decimal places.)  This conversion can be problematic when the
//    integer portion of the value is large, as there may not be enough
//    precision remaining to deliver a meaningful number of decimal places.  As
//    seen above, for example, for numbers near one trillion, there is not
//    enough precision in a `double` for 4 decimal places.
//   - For this conversion, use `sprintf` into a large-enough buffer:
//   - `char buf[2000]; double value; sprintf(buf, "%.*f", places, value);`
//
///Usage
///-----
// This section shows the intended use of this component.
//
///Example 1: Sending Decimals As Octets Using Network Format
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose you have two communicating entities (programs) that talk to each
// other using a binary (as opposed to text) protocol.  In such protocol it is
// important to establish a so-called network format, and convert to and from
// that format in the protocol layer.  The sender (suppose that it is an IBM
// server that has just finished an expensive calculation involving millions
// of numbers and needs to send the result to its client) will need to convert
// the data to network format before sending:
// ```
// unsigned char  msgbuffer[256];
// unsigned char *next = msgbuffer;
//
// BDEC::Decimal64 number(BDLDFP_DECIMAL_DD(1.234567890123456e-42));
// unsigned char   expected[] = {
//                           0x25, 0x55, 0x34, 0xb9, 0xc1, 0xe2, 0x8e, 0x56 };
//
// next = bdldfp::DecimalConvertUtil::decimalToNetwork(next, number);
//
// assert(memcmp(msgbuffer, expected, sizeof(number)) == 0);
// ```
// The receiver/client shall then restore the number from network format:
// ```
// unsigned char  msgbuffer[] ={
//                           0x25, 0x55, 0x34, 0xb9, 0xc1, 0xe2, 0x8e, 0x56 };
// unsigned char *next = msgbuffer;
//
// BDEC::Decimal64 number;
// BDEC::Decimal64 expected(BDLDFP_DECIMAL_DD(1.234567890123456e-42));
//
// next = bdldfp::DecimalConvertUtil::decimalFromNetwork(number, next);
//
// assert(number == expected);
// ```
//
///Example 2: Storing/Sending Decimals In Binary Floating-Point
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose you have two communicating entities (programs) that talk to each
// other using a legacy protocol that employs binary floating-point formats to
// send/receive numbers.  So your application layer will have to store the
// decimal into a binary FP variable, ensure that it can be restored (in other
// words that it has "fit" into the binary type) when sending, and restore the
// decimal number (from the binary type) when receiving:
// ```
// const BDEC::Decimal64 number(BDLDFP_DECIMAL_DD(1.23456789012345e-42));
//
// typedef bdldfp::DecimalConvertUtil Util;
// double dbl = Util::decimalToDouble(number);
//
// if (Util::decimal64FromDouble(dbl) != number) {
//     // Do what is appropriate for the application
// }
// ```
// Note that the above assert would probably be a lot more complicated if
// statement in production code.  It may actually be acceptable to put the
// decimal onto the wire with certain amount of imprecision.
//
// The receiver would then restore the number using the appropriate
// `decimal64FromDouble` function:
// ```
// BDEC::Decimal64 restored = Util::decimal64FromDouble(dbl);
//
// assert(number == restored);
// ```

#include <bdlscm_version.h>

#include <bdldfp_decimal.h>
#include <bdldfp_decimalconvertutil_inteldfp.h>
#include <bdldfp_decimalimputil.h>
#include <bdldfp_decimalimputil_inteldfp.h>
#include <bdldfp_decimalplatform.h>
#include <bdldfp_decimalutil.h>
#include <bdldfp_intelimpwrapper.h>

#include <bsls_assert.h>
#include <bsls_byteorder.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>
#include <bsls_types.h>

#include <bsl_cstring.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsl_c_signal.h>  // Formerly transitively included via decContext.h
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bdldfp {
                        // ========================
                        // class DecimalConvertUtil
                        // ========================

/// This `struct` provides a namespace for utility functions that convert
/// between the decimal floating-point types of `bdldfp_decimal` and various
/// other formats.
struct DecimalConvertUtil {

  private:
    // PRIVATE TYPES
#if defined(BDLDFP_DECIMALPLATFORM_INTELDFP)
    typedef DecimalConvertUtil_IntelDfp  Imp;
#else
    BDLDFP_DECIMALPLATFORM_COMPILER_ERROR;
#endif

    // PRIVATE CLASS DATA

                    // 'bid64' REPRESENTATION CONSTANTS

    static const int k_EXPONENT_SHIFT_SMALL64 = 53; // exponent position
    static const int k_EXPONENT_MASK64 = 0x3ff;     // exponent mask bits
    static const unsigned long long k_SPECIAL_ENCODING_MASK64 =
        0x6000000000000000ull;                      // special when non-zero
    static const unsigned long long k_SMALL_COEFF_MASK64 =
        0x001fffffffffffffull;                      // mask for mantissa

    // PRIVATE CLASS METHODS

    /// If the specified `value` is NaN, +infinity, -infinity, or its
    /// unbiased exponent is 384, return a non-zero value and leave all
    /// output parameters unmodified.  Otherwise, partition the `value` into
    /// sign, biased exponent, and mantissa compartments, and load the
    /// corresponding values into the specified `isNegative`,
    /// `biasedExponent`, and `mantissa`.  Return 0.  Note that a non-zero
    /// value does not indicate that `value` can not be partitioned, just
    /// that it can not be partitioned by this function.  Also note that the
    /// bias for `Decimal64` is 398.
    static int decimal64ToUnpackedSpecial(bool                *isNegative,
                                          int                 *biasedExponent,
                                          bsls::Types::Uint64 *mantissa,
                                          bdldfp::Decimal64    value);

    /// Return a `Decimal64` object that has the specified `mantissa`,
    /// `exponent`, and a sign based on the specified `isNegative`.  The
    /// behavior is undefined unless `isNegative`, `mantissa`, and the
    /// biased exponent were originally obtained from
    /// `decimal64ToUnpackedSpecial`.  Note that `exponent` should be
    /// unbiased, so 398 should be subtracted from the biased exponent
    /// gotten from `decimal64ToUnpackedSpecial`.
    static bdldfp::Decimal64 decimal64FromUnpackedSpecial(
                                                bool                isNegative,
                                                bsls::Types::Uint64 mantissa,
                                                int                 exponent);


    /// Return a `Decimal64` object that has the specified `mantissa`,
    /// `exponent`.  The behavior is undefined unless `isNegative`,
    /// `mantissa`, and the biased exponent were originally obtained from
    /// `decimal64ToUnpackedSpecial`.  Note that `exponent` should be
    /// unbiased, so 398 should be subtracted from the biased exponent
    /// gotten from `decimal64ToUnpackedSpecial`.
    static bdldfp::Decimal64 decimal64FromUnpackedSpecial(int mantissa,
                                                          int exponent);

  public:
    // CLASS METHODS

                        // decimalToDouble functions

    static double decimal32ToDouble (Decimal32  decimal);
    static double decimal64ToDouble (Decimal64  decimal);

    /// @DEPRECATED: Use `deciamalToDouble` instead.
    static double decimal128ToDouble(Decimal128 decimal);

    /// Return a `double` object having the value closest to the value of
    /// the specified `decimal` object following the conversion rules
    /// defined by IEEE-754:
    ///
    /// * If the `decimal` object is a NaN, return a NaN.
    /// * Otherwise if `decimal` is positive or negative infinity, return
    ///   infinity of the same sign.
    /// * Otherwise if `decimal` is positive or negative zero, return zero
    ///   of the same sign.
    /// * Otherwise if `decimal` object has an absolute value that is
    ///   larger than `std::numeric_limits<double>::max()`, raise the
    ///   "overflow" floating-point exception and return infinity of the
    ///   same sign as `decimal`.
    /// * Otherwise if `decimal` has an absolute value that is smaller than
    ///   `std::numeric_limits<double>::min()`, raise the "underflow"
    ///   floating-point exception and return zero of the same sign as
    ///   `decimal`.
    /// * Otherwise if `decimal` has a value that has more significant
    ///   base-10 digits than `std::numeric_limits<double>::digits10`,
    ///   raise the "inexact" floating-point exception, round that value
    ///   according to the *binary* rounding direction setting of the
    ///   floating-point environment, and return the result of that.
    /// * Otherwise if `decimal` has a significand that cannot be exactly
    ///   represented using binary floating-point, raise the "inexact"
    ///   floating-point exception, round that value according to the
    ///   *binary* rounding direction setting of the environment, and
    ///   return the result of that.
    /// * Otherwise use the exact value of the `other` object for the
    ///   initialization if this object.
    static double decimalToDouble   (Decimal32  decimal);
    static double decimalToDouble   (Decimal64  decimal);
    static double decimalToDouble   (Decimal128 decimal);

                        // decimalToFloat functions

    static float decimal32ToFloat (Decimal32  decimal);
    static float decimal64ToFloat (Decimal64  decimal);

    /// @DEPRECATED: Use `deciamalToFloat` instead.
    static float decimal128ToFloat(Decimal128 decimal);

    /// Return a `float` object having the value closest to the value of the
    /// specified `decimal` object following the conversion rules defined
    /// by IEEE-754:
    ///
    /// * If the `decimal` object is a NaN, return a NaN.
    /// * Otherwise if `decimal` is positive or negative infinity, return
    ///   infinity of the same sign.
    /// * Otherwise if `decimal` is positive or negative zero, return zero
    ///   of the same sign.
    /// * Otherwise if `decimal` object has an absolute value that is
    ///   larger than `std::numeric_limits<long double>::max()`, raise the
    ///   "overflow" floating-point exception and return infinity of the
    ///   same sign as `decimal`.
    /// * Otherwise if `decimal` has an absolute value that is smaller than
    ///   `std::numeric_limits<float>::min()`, raise the "underflow"
    ///   floating-point exception and return zero of the same sign as
    ///   `decimal`.
    /// * Otherwise if `decimal` has a value that has more significant
    ///   base-10 digits than `std::numeric_limits<float>::digits10`,
    ///   raise the "inexact" floating-point exception, round that value
    ///   according to the *binary* rounding direction setting of the
    ///   floating-point environment, and return the result of that.
    /// * Otherwise if `decimal` has a significand that cannot be exactly
    ///   represented using binary floating-point, raise the "inexact"
    ///   floating-point exception, round that value according to the
    ///   *binary* rounding direction setting of the environment, and
    ///   return the result of that.
    /// * Otherwise use the exact value of the `other` object for the
    ///   initialization if this object.
    static float decimalToFloat   (Decimal32  decimal);
    static float decimalToFloat   (Decimal64  decimal);
    static float decimalToFloat   (Decimal128 decimal);

                        // decimalFromDouble functions

    static Decimal32  decimal32FromDouble (double binary, int digits = 0);
    static Decimal32  decimal32FromFloat  (float  binary, int digits = 0);
    static Decimal64  decimal64FromDouble (double binary, int digits = 0);
    static Decimal64  decimal64FromFloat  (float  binary, int digits = 0);
    static Decimal128 decimal128FromDouble(double binary, int digits = 0);

    /// Return a decimal floating-point number converted from the specified
    /// `binary`.
    ///
    /// If `binary` is singular (+/-NaN, +/-Inf, or +/-0) or is not within
    /// the representable range of the return type, return a corresponding
    /// decimal singular value.
    ///
    /// Optionally specify `digits` to indicate the number of significant
    /// digits to produce in the returned value.  The `digits` parameter is
    /// treated as follows:
    ///
    /// If `digits` is larger than the number of digits in the destination
    /// type, it will be reduced to that number of digits.
    ///
    /// If `digits` is positive, the result is `binary` rounded to that many
    /// significant digits.
    ///
    /// If `digits` is negative, the decimal value with the fewest
    /// significant digits that converts back to `binary` is returned if
    /// possible, and otherwise the value closest to `binary` is returned.
    /// Note that this provides the most visually appealing result but is
    /// the most expensive to compute.
    ///
    /// If `digits` is not specified or 0, a default value will be used
    /// (possibly depending on the value of `binary`) based on the premise
    /// that `binary` is a converted decimal value of no more significant
    /// digits than is guaranteed to have a uniquely converted binary value
    /// (15 for `double`, 6 for `float` in general, and 7 for `float` in the
    /// range `[ .0009999995 .. 8589972000 ]`).  Note that this is likely to
    /// have the best performance for "business" numbers (i.e., numbers that
    /// originate as decimal values in external market quote feeds).
    ///
    /// Note that the purpose of these functions is to restore a decimal
    /// value that has been converted to a binary floating-point type.  It
    /// is more efficient to use conversion constructors when all that is
    /// needed is the nearest decimal to the `binary` value.
    ///
    /// Note that if `binary` is a `float` value that was converted from an
    /// IBM/Perkin-Elmer/Interdata binary `float` value itself converted
    /// from a decimal value of no more than 6 significant digits,
    /// specifying 6 for `digits` will recover the original decimal value.
    /// Not specifying `digits` may result in a value having a spurious
    /// seventh digit.
    static Decimal128 decimal128FromFloat (float  binary, int digits = 0);

                        // decimalToDPD functions

    static void decimal32ToDPD (unsigned char *buffer,
                                Decimal32      decimal);
    static void decimal64ToDPD (unsigned char *buffer,
                                Decimal64      decimal);
    static void decimal128ToDPD(unsigned char *buffer,
                                Decimal128     decimal);

    /// Populate the specified `buffer` with the Densely Packed Decimal
    /// (DPD) representation of the specified `decimal` value.  The DPD
    /// representations of `Decimal32`, `Decimal64`, and `Decimal128`
    /// require 4, 8, and 16 bytes respectively.  The behavior is undefined
    /// unless `buffer` points to a contiguous sequence of at least
    /// `sizeof(decimal)` bytes.  Note that the DPD representation is
    /// defined in section 3.5 of IEEE 754-2008.
    static void decimalToDPD   (unsigned char *buffer,
                                Decimal32      decimal);
    static void decimalToDPD   (unsigned char *buffer,
                                Decimal64      decimal);
    static void decimalToDPD   (unsigned char *buffer,
                                Decimal128     decimal);

                        // decimalFromDPD functions

    static Decimal32  decimal32FromDPD (const unsigned char *buffer);
    static Decimal64  decimal64FromDPD (const unsigned char *buffer);

    /// Return the native implementation representation of the value of the
    /// same size base-10 floating-point value stored in Densely Packed
    /// Decimal format at the specified `buffer` address.  The behavior is
    /// undefined unless `buffer` points to a memory area at least
    /// `sizeof(decimal)` in size containing a value in DPD format.
    static Decimal128 decimal128FromDPD(const unsigned char *buffer);

    static void decimal32FromDPD (Decimal32           *decimal,
                                  const unsigned char *buffer);
    static void decimal64FromDPD (Decimal64           *decimal,
                                  const unsigned char *buffer);
    static void decimal128FromDPD(Decimal128          *decimal,
                                  const unsigned char *buffer);

    /// Store, into the specified `decimal`, the native implementation
    /// representation of the value of the same size base-10 floating point
    /// value represented in Densely Packed Decimal format, at the specified
    /// `buffer` address.  The behavior is undefined unless `buffer` points
    /// to a memory area at least `sizeof(decimal)` in size containing a
    /// value in DPD format.
    static void decimalFromDPD   (Decimal32           *decimal,
                                  const unsigned char *buffer);
    static void decimalFromDPD   (Decimal64           *decimal,
                                  const unsigned char *buffer);
    static void decimalFromDPD   (Decimal128          *decimal,
                                  const unsigned char *buffer);

                        // decimalToBID functions

    static void decimal32ToBID (unsigned char *buffer,
                                Decimal32      decimal);
    static void decimal64ToBID (unsigned char *buffer,
                                Decimal64      decimal);
    static void decimal128ToBID(unsigned char *buffer,
                                Decimal128     decimal);

    /// Populate the specified `buffer` with the Binary Integer Decimal
    /// (BID) representation of the specified `decimal` value.  The BID
    /// representations of `Decimal32`, `Decimal64`, and `Decimal128`
    /// require 4, 8, and 16 bytes respectively.  The behavior is undefined
    /// unless `buffer` points to a contiguous sequence of at least
    /// `sizeof(decimal)` bytes.  Note that the BID representation is
    /// defined in section 3.5 of IEEE 754-2008.
    static void decimalToBID   (unsigned char *buffer,
                                Decimal32      decimal);
    static void decimalToBID   (unsigned char *buffer,
                                Decimal64      decimal);
    static void decimalToBID   (unsigned char *buffer,
                                Decimal128     decimal);

                         // decimalFromBID functions

    static Decimal32  decimal32FromBID (const unsigned char *buffer);
    static Decimal64  decimal64FromBID (const unsigned char *buffer);

    /// Return the native implementation representation of the value of the
    /// same size base-10 floating-point value stored in Binary Integer
    /// Decimal format at the specified `buffer` address.  The behavior is
    /// undefined unless `buffer` points to a memory area at least
    /// `sizeof(decimal)` in size containing a value in BID format.
    static Decimal128 decimal128FromBID(const unsigned char *buffer);

    static void decimal32FromBID (Decimal32           *decimal,
                                  const unsigned char *buffer);
    static void decimal64FromBID (Decimal64           *decimal,
                                  const unsigned char *buffer);
    static void decimal128FromBID(Decimal128          *decimal,
                                  const unsigned char *buffer);

    /// Store, into the specified `decimal`, the native implementation
    /// representation of the value of the same size base-10 floating point
    /// value represented in Binary Integer Decimal format, at the specified
    /// `buffer` address.  The behavior is undefined unless `buffer` points
    /// to a memory area at least `sizeof(decimal)` in size containing a
    /// value in BID format.
    static void decimalFromBID   (Decimal32           *decimal,
                                  const unsigned char *buffer);
    static void decimalFromBID   (Decimal64           *decimal,
                                  const unsigned char *buffer);
    static void decimalFromBID   (Decimal128          *decimal,
                                  const unsigned char *buffer);

                        // decimalToNetwork functions

    static unsigned char *decimal32ToNetwork (unsigned char *buffer,
                                              Decimal32      decimal);
    static unsigned char *decimal64ToNetwork (unsigned char *buffer,
                                              Decimal64      decimal);
    static unsigned char *decimal128ToNetwork(unsigned char *buffer,
                                              Decimal128     decimal);

    /// Store the specified `decimal`, in network format, into the specified
    /// `buffer` and return the address one past the last byte written into
    /// the `buffer`.  The network format is defined as big endian byte
    /// order and densely packed base-10 significand encoding.  This
    /// corresponds to the way IBM hardware represents these numbers in
    /// memory.  The behavior is undefined unless `buffer` points to a
    /// memory area at least `sizeof(decimal)` in size.  Note that these
    /// functions always return `buffer + sizeof(decimal)` on the supported
    /// 8-bits-byte architectures.
    static unsigned char *decimalToNetwork   (unsigned char *buffer,
                                              Decimal32      decimal);
    static unsigned char *decimalToNetwork   (unsigned char *buffer,
                                              Decimal64      decimal);
    static unsigned char *decimalToNetwork   (unsigned char *buffer,
                                              Decimal128     decimal);

                        // decimalFromNetwork functions

    static const unsigned char *decimal32FromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer);
    static const unsigned char *decimal64FromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer);
    static const unsigned char *decimal128FromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer);

    /// Store into the specified `decimal`, the value of the same size
    /// base-10 floating-point value stored in network format at the
    /// specified `buffer` address and return the address one past the last
    /// byte read from `buffer`.  The network format is defined as big
    /// endian byte order and densely packed base-10 significand encoding.
    /// This corresponds to the way IBM hardware represents these numbers in
    /// memory.  The behavior is undefined unless `buffer` points to a
    /// memory area at least `sizeof(decimal)` bytes.  Note that these
    /// functions always return `buffer + sizeof(decimal)` on the supported
    /// 8-bits-byte architectures.
    static const unsigned char *decimalFromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer);
    static const unsigned char *decimalFromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer);
    static const unsigned char *decimalFromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer);

    /// Store the specified `decimal`, in the *multi-width encoding* format,
    /// into the specified `buffer` and return the number of bytes used by
    /// the encoding.  The behavior is undefined unless `buffer` points to a
    /// memory area with enough room to hold the encode value (which has a
    /// maximum size of 8 bytes).
    static bsls::Types::size_type decimal64ToMultiWidthEncoding(
                                                   unsigned char     *buffer,
                                                   bdldfp::Decimal64  decimal);

    /// If the specified `decimal` can be encoded in 5 or fewer bytes of the
    /// *multi-width encoding* format, then store `decimal` into the
    /// specified `buffer` in that format, and return the number of bytes
    /// written to `buffer`.  Otherwise, return 0.  The behavior is
    /// undefined unless `buffer` points to a memory area having at least 5
    /// bytes.  Note that this function does not supporting encoding values
    /// requiring a full IEEE network encoding, which is supported by the
    /// `decimal64ToMultiWidthEncoding` function.
    static bsls::Types::size_type decimal64ToMultiWidthEncodingRaw(
                                                   unsigned char     *buffer,
                                                   bdldfp::Decimal64  decimal);

    /// Decode a decimal value in the *multi-width encoding* format from the
    /// specified `buffer` having the specified `size`.  Return the decoded
    /// value.  The behavior is undefined unless `buffer` has at least
    /// `size` bytes, `size` is a valid encoding size in the
    /// `multi-width encoding` format, and `size <= 5`.  Note that this
    /// function does not support decoding values requiring a full IEEE
    /// network encoding, which is supported by the
    /// `decimal64FromMultiWidthEncoding` function.
    static Decimal64 decimal64FromMultiWidthEncodingRaw(
                                                const unsigned char    *buffer,
                                                bsls::Types::size_type  size);

    /// Return `true` if the specified `size` is a valid encoding size in
    /// the *multi-width encoding* format, and `false` otherwise.  Note that
    /// valid encoding sizes are 1, 2, 3, 4, 5 and 8 bytes.
    static bool isValidMultiWidthSize(bsls::Types::size_type  size);

    /// Decode a decimal value in the *multi-width encoding* format from the
    /// specified `buffer` having the specified `size`.  Return the decoded
    /// value.  The behavior is undefined unless `buffer` has at least
    /// `size` bytes, and `size` is a valid encoding size in the
    /// `multi-width encoding` format.
    static Decimal64 decimal64FromMultiWidthEncoding(
                                                const unsigned char    *buffer,
                                                bsls::Types::size_type  size);

    /// Decode a decimal value in the *multi-width encoding* format from the
    /// specified `buffer` having the specified `size` and store the result
    /// into the specified `decimal` parameter.  Return 0 on success, and
    /// non-zero value otherwise.  The behavior is undefined unless `buffer`
    /// has at least `size` bytes.  Note that this function returns a
    /// non-zero value and leaves `decimal` unchanged if `size` is not a
    /// valid encoding size of the *multi-width encoding* format.
    static int decimal64FromMultiWidthEncodingIfValid(
                                               Decimal64              *decimal,
                                               const unsigned char    *buffer,
                                               bsls::Types::size_type  size);

    /// Store the specified `decimal`, in the *variable-width encoding*
    /// format, into the specified `buffer` and return the address one past
    /// the last byte written into the `buffer`.  The behavior is undefined
    /// unless `buffer` points to a memory area with enough room to hold the
    /// encoded value (which has a maximum size of 9 bytes).
    static unsigned char *decimal64ToVariableWidthEncoding(
                                                   unsigned char     *buffer,
                                                   bdldfp::Decimal64  decimal);

    /// Store into the specified `decimal`, the value of `Decimal64` value
    /// stored in the *variable-width encoding* format at the specified
    /// `buffer` address.  Return the address one past the last byte read
    /// from `buffer`.  The behavior is undefined unless `buffer` points to
    /// a memory area holding a `Decimal64` value encoded in the
    /// *variable-width encoding* format.
    static const unsigned char *decimal64FromVariableWidthEncoding(
                                                  bdldfp::Decimal64   *decimal,
                                                  const unsigned char *buffer);
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================


// PRIVATE CLASS METHODS

inline
int DecimalConvertUtil::decimal64ToUnpackedSpecial(
                                           bool                *isNegative,
                                           int                 *biasedExponent,
                                           bsls::Types::Uint64 *mantissa,
                                           bdldfp::Decimal64    value)
{
#ifdef BDLDFP_DECIMALPLATFORM_INTELDFP
    bsls::Types::Uint64 bidValue = value.data()->d_raw;
#else
    bsls::Types::Uint64 bidValue = bid_dpd_to_bid64(
                            static_cast<bsls::Types::Uint64>(*(value.data())));
#endif
    // This class method is based on inteldfp 'unpack_BID64' (bid_internal.h),
    // with a non-zero return if 'SPECIAL_ENCODING_MASK64' indicates a special
    // encoding; these are practically non-existent and no need to optimize.

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
        (bidValue & k_SPECIAL_ENCODING_MASK64) == k_SPECIAL_ENCODING_MASK64)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        // punt on special encodings
        return -1;                                                    // RETURN
    }

    *isNegative = (bidValue & 0x8000000000000000ull) ? 1 : 0;

    *biasedExponent = static_cast<int>(
                   (bidValue >> k_EXPONENT_SHIFT_SMALL64) & k_EXPONENT_MASK64);

    *mantissa = bidValue & k_SMALL_COEFF_MASK64;

    return 0;
}

inline
Decimal64 DecimalConvertUtil::decimal64FromUnpackedSpecial(
                                                bool                isNegative,
                                                bsls::Types::Uint64 mantissa,
                                                int                 exponent)
{
#ifdef BDLDFP_DECIMALPLATFORM_INTELDFP
    bdldfp::Decimal64 result;
    result.data()->d_raw = (isNegative ? 0x8000000000000000ull : 0) |
                           (static_cast<BID_UINT64>(exponent + 398)
                            << k_EXPONENT_SHIFT_SMALL64) |
                           mantissa;
    return result;
#else
    if (isNegative) {
        return DecimalImpUtil::makeDecimalRaw64(
                                             -static_cast<long long>(mantissa),
                                             exponent);
    } else {
        return DecimalImpUtil::makeDecimalRaw64(mantissa, exponent);
    }
#endif
}

inline
Decimal64 DecimalConvertUtil::decimal64FromUnpackedSpecial(int mantissa,
                                                           int exponent)
{
#ifdef BDLDFP_DECIMALPLATFORM_INTELDFP
    bdldfp::Decimal64 result;
    result.data()->d_raw = (static_cast<BID_UINT64>(exponent + 398)
                            << k_EXPONENT_SHIFT_SMALL64) |
                            mantissa;
    return result;
#else
    return DecimalUtil::makeDecimalRaw64(mantissa, exponent);
#endif
}

// CLASS METHODS
inline
bsls::Types::size_type DecimalConvertUtil::decimal64ToMultiWidthEncoding(
                                                    unsigned char     *buffer,
                                                    bdldfp::Decimal64  decimal)
{

    bsls::Types::size_type size = decimal64ToMultiWidthEncodingRaw(buffer,
                                                                   decimal);

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(size != 0)) {
        return size;                                                  // RETURN
    }
    else {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        bsls::Types::Uint64 encoded;
        bsl::memcpy(reinterpret_cast<unsigned char *>(&encoded),
                    decimal.data(),
                    sizeof(encoded));

        encoded = BSLS_BYTEORDER_HTONLL(encoded);

        bsl::memcpy(buffer,
                    reinterpret_cast<unsigned char*>(&encoded),
                    8);
        return 8;                                                     // RETURN
    }
}

inline
bsls::Types::size_type DecimalConvertUtil::decimal64ToMultiWidthEncodingRaw(
                                                    unsigned char     *buffer,
                                                    bdldfp::Decimal64  decimal)
{
    bool                isNegative;
    int                 exponent;
    bsls::Types::Uint64 mantissa;

    // 'exponent' is biased --> biased exponent = exponent + 398

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(0 ==
             decimal64ToUnpackedSpecial(&isNegative,
                                        &exponent,
                                        &mantissa,
                                        decimal))) {
        if (!isNegative) {
            if (395 <= exponent && exponent < 399) {
                if (mantissa < (1u << 14)) {
                    unsigned short squished = static_cast<unsigned short>(
                        mantissa | (exponent - 395) << 14);

                    unsigned short squishedN = BSLS_BYTEORDER_HTONS(squished);
                    bsl::memcpy(buffer, &squishedN, 2);
                    return 2;                                         // RETURN
                }
            }
            if (392 <= exponent && exponent < 400) {
                if (mantissa < (1u << 21)) {
                    // On IBM (and Linux to a lesser extent), copying from a
                    // word-aligned source is faster, so we shift an extra the
                    // source by an extra 8 bits.

                    unsigned int squished = static_cast<unsigned int>(
                        (mantissa << 8) | (exponent - 392) << 29);
                    unsigned int squishedN = BSLS_BYTEORDER_HTONL(squished);

                    bsl::memcpy(buffer,
                                reinterpret_cast<unsigned char*>(&squishedN),
                                3);
                    return 3;                                         // RETURN
                }
            }
        }

        if (382 <= exponent && exponent < 414) {
            if (mantissa < (1u << 26)) {
                unsigned int squished = static_cast<unsigned int>(
                                            mantissa | (exponent - 382) << 26);
                if (isNegative) {
                    squished |= 1u << 31;
                }
                unsigned int squishedN = BSLS_BYTEORDER_HTONL(squished);
                bsl::memcpy(buffer, &squishedN, 4);
                return 4;                                             // RETURN
            }
            if (mantissa < (1ull << 34)) {
                bsls::Types::Uint64 squished =
                    static_cast<bsls::Types::Uint64>(
                     (mantissa << 24) |
                     (static_cast<bsls::Types::Uint64>(exponent - 382) << 58));
                if (isNegative) {
                    squished |= 1ull << 63;
                }
                bsls::Types::Uint64 squishedN =
                                               BSLS_BYTEORDER_HTONLL(squished);
                bsl::memcpy(buffer,
                            reinterpret_cast<unsigned char*>(&squishedN),
                            5);
                return 5;                                             // RETURN
            }
        }
    }

    return 0;
}

inline
bool DecimalConvertUtil::isValidMultiWidthSize(bsls::Types::size_type  size)
{
    return (size > 0 && size <= 5) || size == 8;
}

inline
Decimal64 DecimalConvertUtil::decimal64FromMultiWidthEncoding(
                                                const unsigned char    *buffer,
                                                bsls::Types::size_type  size)
{
    BSLS_ASSERT(1 <= size);
    BSLS_ASSERT(size <= 5 || size == 8);

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(size < 6)) {
        return decimal64FromMultiWidthEncodingRaw(buffer, size);      // RETURN
    }
    else {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

        bsls::Types::Uint64 encoded;
        bsl::memcpy(&encoded, buffer, 8);
        encoded = BSLS_BYTEORDER_NTOHLL(encoded);

        DecimalImpUtil::ValueType64 decimal;
        bsl::memcpy(&decimal, &encoded, sizeof(decimal));

        return decimal;                                    // RETURN
    }
}

inline
int DecimalConvertUtil::decimal64FromMultiWidthEncodingIfValid(
                                               Decimal64              *decimal,
                                               const unsigned char    *buffer,
                                               bsls::Types::size_type  size)
{
    int ret(0);

    if (isValidMultiWidthSize(size)) {
        *decimal = decimal64FromMultiWidthEncoding(buffer, size);
    }
    else {
        ret = 1;
    }
    return ret;
}

inline
Decimal64 DecimalConvertUtil::decimal64FromMultiWidthEncodingRaw(
                                                const unsigned char    *buffer,
                                                bsls::Types::size_type  size)
{
    BSLS_ASSERT(1 <= size);
    BSLS_ASSERT(size <= 5);

    switch(size) {
      case 2: {
        int exponent = (buffer[0] >> 6) - 3;
        int mantissa = static_cast<int>(((buffer[0] & 0x3F) << 8) |
                                        static_cast<int>(buffer[1]));

        return decimal64FromUnpackedSpecial(mantissa, exponent);      // RETURN
      } break;
      case 3: {
        int exponent = (buffer[0] >> 5) - 6;
        int mantissa = static_cast<int>(((buffer[0] & 0x1F) << 16) |
                                        static_cast<int>(buffer[1]) << 8 |
                                        static_cast<int>(buffer[2]));
        return decimal64FromUnpackedSpecial(mantissa, exponent);      // RETURN
      } break;
      case 4: {
        bool isNegative = buffer[0] >> 7;
        int  exponent   = ((buffer[0] & 0x7F) >> 2) - 16;
        int  mantissa   = static_cast<int>(((buffer[0] & 0x03)         << 24) |
                                           static_cast<int>(buffer[1]) << 16 |
                                           static_cast<int>(buffer[2]) << 8 |
                                           static_cast<int>(buffer[3]));
        return decimal64FromUnpackedSpecial(isNegative, mantissa, exponent);
                                                                      // RETURN
      } break;
      case 1: {
        int exponent = (buffer[0] >> 7) - 2;
        int mantissa = static_cast<int>(buffer[0] & 0x7F);
        return decimal64FromUnpackedSpecial(mantissa, exponent);      // RETURN
      } break;
#ifdef BSLS_PLATFORM_CMP_IBM
      case 5:
#else
      default:
#endif
      {
        // Xlc optimizes better when 'case 5:' is used instead of 'default:',
        // and vice versa for gcc.

        bool                isNegative = buffer[0] >> 7;
        int                 exponent   = ((buffer[0] & 0x7F) >> 2) - 16;
        bsls::Types::Uint64 mantissa   = static_cast<bsls::Types::Uint64>(
                     static_cast<bsls::Types::Uint64>(buffer[0] & 0x03) << 32 |
                     static_cast<bsls::Types::Uint64>(buffer[1]) << 24 |
                     static_cast<bsls::Types::Uint64>(buffer[2]) << 16 |
                     static_cast<bsls::Types::Uint64>(buffer[3]) << 8 |
                     static_cast<bsls::Types::Uint64>(buffer[4]));
        return decimal64FromUnpackedSpecial(isNegative, mantissa, exponent);
                                                                      // RETURN
      } break;
    }

#ifdef BSLS_PLATFORM_CMP_IBM
    // From here on, the function has undefined behavior.  We will return a
    // default constructed value to suppress compiler warnings.
    return bdldfp::Decimal64();
#endif
}

inline
unsigned char *DecimalConvertUtil::decimal64ToVariableWidthEncoding(
                                                    unsigned char     *buffer,
                                                    bdldfp::Decimal64  decimal)
{
    bool                isNegative;
    int                 exponent;
    bsls::Types::Uint64 mantissa;

    // 'exponent' is biased --> biased exponent = exponent + 398

    if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(0 ==
             decimal64ToUnpackedSpecial(&isNegative,
                                        &exponent,
                                        &mantissa,
                                        decimal))) {
        if (!isNegative) {
            if (396 <= exponent && exponent < 400) {
                if (mantissa < (1u << 13)) {

                    // The predicate disambiguation bit is implicitly 0.

                    unsigned short squished = static_cast<unsigned short>(
                        mantissa | (exponent - 396) << 13);

                    unsigned short squishedN = BSLS_BYTEORDER_HTONS(squished);
                    bsl::memcpy(buffer, &squishedN, 2);
                    return buffer + 2;                                // RETURN
                }
            }

            if (394 <= exponent && exponent < 402) {
                if (mantissa < (1u << 19)) {
                    // On IBM (and Linux to a lesser extent), copying from a
                    // word-aligned source is faster, so we shift the source of
                    // memcpy by an extra 8 bits.

                    unsigned int squished = static_cast<unsigned int>(
                                     (mantissa << 8) | (exponent - 394) << 27);

                    // The predicate bits should be 0b10.

                    squished |= 1u << 31;

                    unsigned int squishedN = BSLS_BYTEORDER_HTONL(squished);

                    bsl::memcpy(buffer,
                                reinterpret_cast<unsigned char*>(&squishedN),
                                3);
                    return buffer + 3;                                // RETURN
                }
            }
        }

        // If the value is negative, with exponent of 15 (biased exponent 413),
        // then the first byte will have a value of FF, which is the state used
        // to indicate that a full 9 byte representation should be used.

        if (382 <= exponent &&
                        (exponent < 413 || (!isNegative && exponent == 413))) {
            if (mantissa < (1u << 24)) {
                unsigned int squished = static_cast<unsigned int>(
                                            mantissa | (exponent - 382) << 24);
                if (isNegative) {
                    squished |= 1u << 29;
                }
                // The predicate bits should be 11.

                squished |= 3u << 30;
                unsigned int squishedN = BSLS_BYTEORDER_HTONL(squished);
                bsl::memcpy(buffer, &squishedN, 4);
                return buffer + 4;                                    // RETURN
            }
        }
    }

    *buffer++ = 0xFF;

    bsls::Types::Uint64 encoded;
    bsl::memcpy(reinterpret_cast<unsigned char *>(&encoded),
                decimal.data(),
                sizeof(encoded));

    encoded = BSLS_BYTEORDER_HTONLL(encoded);

    bsl::memcpy(buffer, reinterpret_cast<unsigned char*>(&encoded), 8);

    return buffer + 8;
}

inline
const unsigned char *DecimalConvertUtil::decimal64FromVariableWidthEncoding(
                                                  bdldfp::Decimal64   *decimal,
                                                  const unsigned char *buffer)
{
    if (!(*buffer & 0x80)) {

        // 2-byte encoding is used.

        int exponent = (buffer[0] >> 5) - 2;
        int mantissa = static_cast<int>(((buffer[0] & 0x1F) << 8) |
                                        static_cast<int>(buffer[1]));

        *decimal = decimal64FromUnpackedSpecial(mantissa, exponent);
        return buffer + 2;                                            // RETURN
    }
    else if ((*buffer & 0xC0) == 0x80) {

        // 3-byte encoding is used.

        unsigned char eByte1 = buffer[0] & 0x3F;

        int exponent = (eByte1 >> 3) - 4;
        int mantissa = static_cast<int>(((eByte1 & 0x07) << 16) |
                                        static_cast<int>(buffer[1] << 8) |
                                        static_cast<int>(buffer[2]));
        *decimal = decimal64FromUnpackedSpecial(mantissa, exponent);
        return buffer + 3;                                            // RETURN
    }
    else if (*buffer == 0xFF) {

        // Full 9-byte encoding is used.
        ++buffer;

        bsls::Types::Uint64 encoded;
        bsl::memcpy(&encoded, buffer, 8);
        encoded = BSLS_BYTEORDER_NTOHLL(encoded);

        bsl::memcpy(decimal,
                    reinterpret_cast<unsigned char *>(&encoded),
                    sizeof(*decimal));

        return buffer + 8;                                            // RETURN
    }
    else  {
        // Here, the condition ((*buffer & 0xC0) == 0xC0) is true, and so the
        // 4-byte encoding is used.

        unsigned char eByte1     = buffer[0] & 0x3F;
        bool          isNegative = eByte1 >> 5;
        int           exponent   = (eByte1 & 0x1F) - 16;
        int           mantissa   =
                           static_cast<int>(static_cast<int>(buffer[1] << 16) |
                                            static_cast<int>(buffer[2] << 8) |
                                            static_cast<int>(buffer[3]));

        *decimal = decimal64FromUnpackedSpecial(isNegative,
                                                mantissa,
                                                exponent);
        return buffer + 4;                                            // RETURN
    }
}

                        // decimalToDouble functions

inline
double DecimalConvertUtil::decimal32ToDouble(Decimal32 decimal)
{
    return Imp::decimalToDouble(decimal);
}

inline
double DecimalConvertUtil::decimal64ToDouble(Decimal64 decimal)
{
    return Imp::decimalToDouble(decimal);
}

inline
double DecimalConvertUtil::decimal128ToDouble(Decimal128 decimal)
{
    return Imp::decimalToDouble(decimal);
}

inline
double DecimalConvertUtil::decimalToDouble(Decimal32 decimal)
{
    return Imp::decimalToDouble(decimal);
}

inline
double DecimalConvertUtil::decimalToDouble(Decimal64 decimal)
{
    return Imp::decimalToDouble(decimal);
}

inline
double DecimalConvertUtil::decimalToDouble(Decimal128 decimal)
{
    return Imp::decimalToDouble(decimal);
}

                        // decimalToFloat functions

inline
float DecimalConvertUtil::decimal32ToFloat(Decimal32 decimal)
{
    return Imp::decimalToFloat(decimal);
}

inline
float DecimalConvertUtil::decimal64ToFloat(Decimal64 decimal)
{
    return Imp::decimalToFloat(decimal);
}

inline
float DecimalConvertUtil::decimal128ToFloat(Decimal128 decimal)
{
    return Imp::decimalToFloat(decimal);
}

inline
float DecimalConvertUtil::decimalToFloat(Decimal32 decimal)
{
    return Imp::decimalToFloat(decimal);
}

inline
float DecimalConvertUtil::decimalToFloat(Decimal64 decimal)
{
    return Imp::decimalToFloat(decimal);
}

inline
float DecimalConvertUtil::decimalToFloat(Decimal128 decimal)
{
    return Imp::decimalToFloat(decimal);
}


                        // decimalToDPD functions

inline
void DecimalConvertUtil::decimal32ToDPD(unsigned char *buffer,
                                        Decimal32      decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

inline
void DecimalConvertUtil::decimal64ToDPD(unsigned char *buffer,
                                        Decimal64      decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

inline
void DecimalConvertUtil::decimal128ToDPD(unsigned char *buffer,
                                         Decimal128     decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToDPD(unsigned char *buffer,
                                      Decimal32      decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToDPD(unsigned char *buffer,
                                      Decimal64      decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToDPD(unsigned char *buffer,
                                      Decimal128     decimal)
{
    Imp::decimalToDPD(buffer, decimal);
}

                        // decimalFromDPD functions

inline
Decimal32
DecimalConvertUtil::decimal32FromDPD(const unsigned char *buffer)
{
    return Imp::decimal32FromDPD(buffer);
}

inline
void
DecimalConvertUtil::decimal32FromDPD(Decimal32           *decimal,
                                     const unsigned char *buffer)
{
    *decimal = Imp::decimal32FromDPD(buffer);
}

inline
Decimal64
DecimalConvertUtil::decimal64FromDPD(const unsigned char *buffer)
{
    return Imp::decimal64FromDPD(buffer);
}

inline
void
DecimalConvertUtil::decimal64FromDPD(Decimal64           *decimal,
                                     const unsigned char *buffer)
{
    *decimal = Imp::decimal64FromDPD(buffer);
}

inline
Decimal128
DecimalConvertUtil::decimal128FromDPD(const unsigned char *buffer)
{
    return Imp::decimal128FromDPD(buffer);
}

inline
void
DecimalConvertUtil::decimal128FromDPD(Decimal128          *decimal,
                                      const unsigned char *buffer)
{
    *decimal = Imp::decimal128FromDPD(buffer);
}

inline
void
DecimalConvertUtil::decimalFromDPD(Decimal32           *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromDPD(decimal, buffer);
}

inline
void
DecimalConvertUtil::decimalFromDPD(Decimal64           *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromDPD(decimal, buffer);
}

inline
void
DecimalConvertUtil::decimalFromDPD(Decimal128          *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromDPD(decimal, buffer);
}


                        // decimalToBID functions

inline
void DecimalConvertUtil::decimal32ToBID(unsigned char *buffer,
                                        Decimal32      decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

inline
void DecimalConvertUtil::decimal64ToBID(unsigned char *buffer,
                                        Decimal64      decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

inline
void DecimalConvertUtil::decimal128ToBID(unsigned char *buffer,
                                         Decimal128     decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToBID(unsigned char *buffer,
                                      Decimal32      decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToBID(unsigned char *buffer,
                                      Decimal64      decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

inline
void DecimalConvertUtil::decimalToBID(unsigned char *buffer,
                                      Decimal128     decimal)
{
    Imp::decimalToBID(buffer, decimal);
}

                        // decimalFromBID functions

inline
Decimal32
DecimalConvertUtil::decimal32FromBID(const unsigned char *buffer)
{
    return Imp::decimal32FromBID(buffer);
}

inline
void
DecimalConvertUtil::decimal32FromBID(Decimal32           *decimal,
                                     const unsigned char *buffer)
{
    *decimal = Imp::decimal32FromBID(buffer);
}

inline
Decimal64
DecimalConvertUtil::decimal64FromBID(const unsigned char *buffer)
{
    return Imp::decimal64FromBID(buffer);
}

inline
void
DecimalConvertUtil::decimal64FromBID(Decimal64           *decimal,
                                     const unsigned char *buffer)
{
    *decimal = Imp::decimal64FromBID(buffer);
}

inline
Decimal128
DecimalConvertUtil::decimal128FromBID(const unsigned char *buffer)
{
    return Imp::decimal128FromBID(buffer);
}

inline
void
DecimalConvertUtil::decimal128FromBID(Decimal128          *decimal,
                                      const unsigned char *buffer)
{
    *decimal = Imp::decimal128FromBID(buffer);
}

inline
void
DecimalConvertUtil::decimalFromBID(Decimal32           *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromBID(decimal, buffer);
}

inline
void
DecimalConvertUtil::decimalFromBID(Decimal64           *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromBID(decimal, buffer);
}

inline
void
DecimalConvertUtil::decimalFromBID(Decimal128          *decimal,
                                   const unsigned char *buffer)
{
    Imp::decimalFromBID(decimal, buffer);
}

}  // close package namespace
}  // close enterprise namespace

#endif

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