Doc No:   SC22/WG21/N1776
                                                            J16/05-0036
                                                  Date:     2005-03-04
                                                  Project:  JTC1.22.32
                                                  Reply to: Robert Klarer
                                                            IBM Canada, Ltd.
                                                            klarer@ca.ibm.com

Decimal Types for C++

0 About this document

[Editor's note: this document is being proposed (by me) as a basis for continuing work. None of the design choices implied in this work is final. Better ideas, as well as comments and critiques will be gratefully received.]

This document is known to be incomplet, inkorrect, and badly formatted.

1 Introduction

Most of today's general purpose computing architectures provide binary floating-point arithmetic in hardware. Binary float-point is an efficient representation that minimizes memory use, and is simpler to implement that floating-point arithmetic using other bases. It has therefore become the norm for scientific computations, with almost all implementations following the IEEE-754 standard for binary floating-point arithmetic.

However, human computation and communication of numeric values almost always uses decimal arithmetic, and decimal notations. Laboratory notes, scientific papers, legal documents, business reports and financial statements all record numeric values in decimal form. When numeric data are given to a program or are displayed to a user, binary to-and-from decimal conversion is required. There are inherent rounding errors involved in such conversions; decimal fractions cannot, in general, be represented exactly by floating-point values. These errors often cause usability and efficiency problems, depending on the application.

These problems are minor when the application domain accepts, or requires results to have, associated error estimates (as is the case with scientific applications). However, in business and financial applications, computations are either required to be exact (with no rounding errors) unless explicitly rounded, or be supported by detailed analyses that are auditable to be correct. Such applications therefore have to take special care in handling any rounding errors introduced by the computations.

The most efficient way to avoid conversion error is to use decimal arithmetic. Recognizing this, the IEEE-754R Standard for Floating-Point Arithmetic specifies decimal floating-point encodings and arithmetic. This technical report specifies extensions to the International Standard for the C++ programming language to permit the use of decimal arithmetic in a manner consistent with the IEEE-754R standard.

1.1 Arithmetic model

The proposal of this Technical Report is based on a model of decimal arithmetic which is a formalization of the decimal system of numeration (Algorism) as further defined and constrained by the relevant standards, IEEE-854, ANSI X3-274, and IEEE-754R.

There are three components to the model:

The model defines these components in the abstract. It neither defines the way in which operations are expressed (which might vary depending on the computer language or other interface being used), nor does it define the concrete representation (specific layout in storage, or in a processor's register, for example) of numbers or context.

From the perspective of the C language, numbers are represented by data types, operations are defined within expressions, and context is the floating environment specified in fenv.h. This Technical Report specifies how the C language implements these components.

Note: A description of the arithmetic model can be found in http://www2.hursley.ibm.com/decimal/decarith.html.

1.2 Encodings

In the C++ International Standard, the representation of a floating-point number is specified in an abstract form where the constituent components of the representation are defined (sign, exponent, significand) but the internals of these components are not. In particular, the exponent range, significand size and the base (or radix), are implementation defined. This allows flexibility for an implementation to take advantage of its underlying hardware architecture. Furthermore, certain behaviors of operations are also implementation defined, for example in the area of handling of special numbers and in exceptions.

The reason for this approach is historical. At the time when C++ (or more importantly, its predecessor, C) was standardized, there were already various hardware implementations of floating-point arithmetic in common use. Specifying the exact details of a representation would make most of the existing implementations at the time not conforming.

C99 provides a binding to IEEE-754 by specifying an annex F and adopting that standard by reference. An implementation not conforming to IEEE-754 can choose to do so by not defining the macro __STDC_IEC_559__. This means not all implementations need to support IEEE-754, and the floating-point arithmetic need not be binary.

This Technical Report specifies decimal floating-point arithmetic according to the IEEE-754R standard, with the constituent components of the representation defined. This is more stringent than the approach taken for the floating point types in the C++ standard. Since it is expected that all decimal floating-point hardware implementations will conform to the IEEE-754R standard, binding to this standard directly benefits both implementors and programmers.

1.3 References

The following standard contain provisions which, through reference in this text, constitute provisions of this Technical Report. For dated references, subsequent amendment to, or revisions of, any of these publications do not apply. However, parties to agreements based on this Technical Report are encouraged to investigate the possibility of applying the most recent editions of the normative documents indicated below. For undated references, the latest edition of the normative document referred applies. Members of the IEC and ISO maintain registers of current valid International Standards.

1.3.1 ISO/IEC 14882:2003, Information technology -- Programming languages, their environments and system software interfaces -- Programming Language C++.

1.3.2 ISO/IEC TR 19768:2005, Information technology -- Programming languages, their environments and system software interfaces -- Technical Report on C++ Library Extensions.

1.3.3 ANSI/IEEE 754R - IEEE Standard for Floating-Point Arithmetic. The Institute of Electrical and Electronic Engineers, Inc..

1.3.4 ANSI/IEEE 854-1997 - IEEE Standard for Radix-Independent Floating-Point Arithmetic. The Institute of Electrical and Electronic Engineers, Inc., New York, 1987.

2 Conventions

This technical report is non-normative; the extensions that it describes may be considered for inclusion in a future revision of the International Standard for C++, but they are not currently required for conformance to that standard. Furthermore, it is conceivable that a future revision of the International Standard will include facilities that are similar and not identical to the extensions described in this report,

Although this report describes extensions to the C++ standard library, vendors may choose to implement these extensions in the C++ language translator itself. This practice is permitted so long as all well-formed programs are accepted by the implementation, and the semantics of those programs are the same as they would be had the extensions taken the form of a library. [Note: The same practice is permitted with respect to the implementation of the C++ standard library. --end note]

2.1 Relation to C++ Standard Library Introduction

Unless otherwise specified, the whole of the ISO C++ Standard Library introduction [lib.library] is included into this Technical Report by reference.

This Technical Report introduces the following elements to supplement those described in [lib.structure.specifications]:

2.2 Relation to "Technical Report on C++ Library Extensions"

To be written.

2.3 Categories of extensions

This technical report describes N categories of library extensions:

  1. New library components (types and functions) that are declared entirely in new headers, such as the type dfp::decimal32 in the <dec32> header.
  2. New library components declared as additions to existing standard headers, such as the functions added to the headers <cmath> and <math.h> in clause 3.7.
  3. New library components declared as additions to TR1 headers, such as the template is_decimal_fp added to the header <type_traits> in clause 3.10.
  4. Additions to standard library components, such as the specializations of std::numeric_limits in clauses 3.2.14, 3.3.14, and 3.3.15.

New headers are distinguished from extensions to existing headers by the title of the synopsis clause. In the first case the title is of the form "Header <foo> synopsis," and the synopsis includes all namespace scope declarations contained in the header. In the second case the title is of the form "Additions to header <foo> synopsis" and the synopsis includes only the extensions, i.e. those namespace scope declarations that are not present in the C++ standard or TR1.

2.4 Namespaces and headers

Since the extensions described in this technical report are not part of the C++ standard library, they should not be declared directly within namespace std. Unless otherwise specified, all components described in this technical report are declared in namespace dfp.

Unless otherwise specified, references to other entities described in this technical report are assumed to be qualified with dfp, and references to entities described in the C++ standard library are assumed to be qualified with std::.

Even when an extension is specified as additions to standard headers (the second and third categories in section 2.3), vendors should not simply add declarations to standard headers in a way that would be visible to users by default [Note: That would fail to be standard conforming, because the new names, even within a namespace, could conflict with user macros. --end note] Users should be required to take explicit action to have access to library extensions.

It is recommended either that additional declarations in standard headers be protected with a macro that is not defined by default, or else that all extended headers, including both new headers and parallel versions of standard headers with nonstandard declarations, be placed in a separate directory that is not part of the default search path.

3 Decimal floating-point types

This Technical Report introduces three decimal floating-point types, named decimal32, decimal64, and decimal128. The set of values of type decimal32 is a subset of the set of values of type decimal64; the set of values of the type decimal64 is a subset of the set of values of the type decimal128. Support for decimal128 is optional.

3.1 Decimal type encodings

The three decimal encoding formats defined in IEEE-754R correspond to the three decimal floating types as follows:

The finite numbers are defined by a sign, an exponent, (which is a power of ten), and a decimal integer coefficient. The value of a finite number is given by (1)sign x coefficient x 10exponent. Refer to IEEE-754R for details of the format.

These formats are characterized by the length of the coefficient, and the maximum and minimum exponent. The table below shows these characteristics by format:

Format decimal32 decimal64 decimal128
Coefficient length in digits 7 16 34
Maximum Exponent (Emax) 96 384 6144
Minimum Exponent (Emin) -95 -383 -6143

3.2 32-bit Decimal type

3.2.1 Header <dec32> synopsis

      #include <iosfwd>

      namespace dfp {
        class decimal32;

        // 3.2.10 unary arithmetic operators:
        decimal32 operator+(const decimal32 & lhs);
        decimal32 operator-(const decimal32 & lhs);

        // 3.2.11 binary arithmetic operators:
        template <class LHS>
        /* implementation-defined */ operator+(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator+(const decimal32 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator-(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator-(const decimal32 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator*(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator*(const decimal32 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator/(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator/(const decimal32 & lhs,
                                               const RHS & rhs);

        // 3.2.12 comparison operators:
        template <class LHS>
        /* implementation-defined */ operator==(const LHS & lhs,
                                                const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator==(const decimal32 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator!=(const LHS & lhs,
                                                const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator!=(const decimal32 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<(const decimal32 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<=(const LHS & lhs,
                                                const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<=(const decimal32 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>(const LHS & lhs,
                                               const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>(const decimal32 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>=(const LHS & lhs,
                                                const decimal32 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>=(const decimal32 & lhs,
                                                const RHS & rhs);

        // 3.2.13 input/output:
        template <class charT, class traits>
          std::basic_istream<charT, traits> &
            operator>>(std::basic_istream<charT, traits> & is, const decimal32 & d);

        template <class charT, class traits>
          std::basic_ostream<charT, traits> &
            operator<<(std::basic_ostream<charT, traits> & is, const decimal32 & d);
      }

3.2.2 Class decimal32

      namespace dfp {
        class decimal32 {
          public:
         
            // 3.2.3 construct/copy/destroy:
            decimal32();
            decimal32(const decimal32 & d32);
            decimal32 & operator=(const decimal32 & d32);
            ~decimal32();

            // 3.2.4 conversion from floating-point type:
            explicit decimal32(const decimal64 & d64);
            explicit decimal32(const decimal128 & d128);
            explicit decimal32(float r);
            explicit decimal32(double r);
            explicit decimal32(long double r);

            // 3.2.5 conversion from integral type:
            decimal32(int z);
            decimal32(unsigned int z);
            decimal32(long z);
            decimal32(unsigned long z);
            decimal32(long long z);
            decimal32(unsigned long long z);

            // 3.2.6 initialization from coefficient and exponent:
            decimal32(long long coeff, int exponent);
            decimal32(unsigned long long coeff, int exponent);

            // 3.2.7 conversion to generic floating-point type:
            long double to_long_double() const;

            // 3.2.8 conversion to integral type:
            operator long long() const;

            // 3.2.9 compound assignment:
            decimal32 & operator+=(const decimal32 & rhs);
            template <class T>
            /* implementation-defined */ operator+=(const T & rhs);

            decimal32 & operator-=(const decimal32 & rhs);
            template <class T>
            /* implementation-defined */ operator-=(const T & rhs);

            decimal32 & operator*=(const decimal32 & rhs);
            template <class T>
            /* implementation-defined */ operator*=(const T & rhs);

            decimal32 & operator/=(const decimal32 & rhs);
            template <class T>
            /* implementation-defined */ operator/=(const T & rhs);
        };
      }

3.2.3 Construct/copy/destroy

decimal32();

Effects: Constructs an object of type decimal32 with the value 0;

decimal32(const decimal32 & d32);
decimal32 & operator=(const decimal32 & d32);

Effects: Copies an object of type decimal32.

~decimal32();

Effects: Destroys an object of type decimal32.

3.2.4 Conversion from floating-point type

explicit decimal32(const decimal64 & d64);

Effects: Constructs an object of type decimal32 by converting from type decimal64. Conversion is performed as in IEEE-754R.

explicit decimal32(const decimal128 & d128);

Effects: Constructs an object of type decimal32 by converting from type decimal128. Conversion is performed as in IEEE-754R.

explicit decimal32(float r);

Effects: Constructs an object of type decimal32 by converting from type float. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal32(double r);

Effects: Constructs an object of type decimal32 by converting from type double. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal32(long double r);

Effects: Constructs an object of type decimal32 by converting from type long double. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

3.2.5 Conversion from integral type

decimal32(int z);
decimal32(unsigned int z);
decimal32(long z);
decimal32(unsigned long z);
decimal32(long long z);
decimal32(unsigned long long z);

Effects: Constructs an object of type decimal32 by converting from the type of z. Conversion is performed as in IEEE-754R.

3.2.6 Initialization from coefficient and exponent

decimal32(long long coeff, int exponent);
decimal32(unsigned long long coeff, int exponent);

Effects: If coeff x 10exponent is representable within the range of decimal32, constructs an object of type decimal32 with that value. Otherwise, initializes the object with DEC_NAN [3.7.1].

[Editor's note: is it better to throw a std::range_error exception in the error case?]

3.2.7 Conversion to floating-point type

long double to_long_double() const;

Returns: If std::numeric_limits<long double>::is_iec559 == true, returns the result of the conversion of *this to long double, performed as in IEEE-754R. Otherwise, the returned value is implementation-defined.

[Editor's note: this notation is ugly, but C++ does not currently allow a user-defined conversion operator to be explicit.]

3.2.8 Conversion to integral type

operator long long() const;

Returns: Returns the result of the conversion of *this to long long, performed as in IEEE-754R.

3.2.9 Compound assignment

decimal32 & operator+=(const decimal32 & rhs);

Effects: Computes the sum of rhs and *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator+=(const T & rhs);

Constraints: T is one of the integral types, decimal64, or decimal128.
Returns: operator+=(decimal32(rhs))

decimal32 & operator-=(const decimal32 & rhs);

Effects: Subtracts rhs from *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator-=(const T & rhs);

Constraints: T is one of the integral types, decimal64, or decimal128.
Returns: operator-=(decimal32(rhs))

decimal32 & operator*=(const decimal32 & rhs);

Effects:Multiplies *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator*=(const T & rhs);

Constraints: T is one of the integral types, decimal64, or decimal128.
Returns: operator*=(decimal32(rhs))

decimal32 & operator/=(const decimal32 & rhs);

Effects:Divides *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator/=(const T & rhs);

Constraints: T is one of the integral types, decimal64, or decimal128.
Returns: operator/=(decimal32(rhs))

3.2.10 Unary arithmetic operators

decimal32 operator+(const decimal32 & lhs);

Returns: Adds lhs to 0, as in IEEE-754R, and returns the result.

decimal32 operator-(const decimal32 & lhs);

Returns: Subtracts lhs from 0, as in IEEE-754R, and returns the result.

3.2.11 Binary arithmetic operators

template <class LHS>
/* implementation-defined */ operator+(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator+(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator-(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Subtracts rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator-(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Subtracts rhs from lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator*(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator*(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator/(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator/(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

3.2.12 Comparison operators

template <class LHS>
/* implementation-defined */ operator==(const LHS & lhs,
                                        const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator==(const decimal32 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator!=(const LHS & lhs,
                                        const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator!=(const decimal32 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator<(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator<=(const LHS & lhs,
                                        const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<=(const decimal32 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>(const LHS & lhs,
                                       const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>(const decimal32 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>=(const LHS & lhs,
                                        const decimal32 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>=(const decimal32 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

3.2.13 Input/output

To be written.

3.2.14 Addition to header <limits>

The standard template std::numeric_limits shall be specialized for the decimal32 type.

[Example:

      namespace std {
        template<> class numeric_limits<dfp::decimal32> {
        public:
          static const bool is_specialized = true;

          static dfp::decimal32 min() throw() { return DEC32_MIN; }
          static dfp::decimal32 max() throw() { return DEC32_MAX; }

          static const int digits   = 7;
          static const int digits10 = digits; 

          static const bool is_signed  = true;
          static const bool is_integer = false;
          static const bool is_exact   = false;

          static const int radix = 10;
          static dfp::decimal32 epsilon()     throw() { return DEC32_EPSILON; }
          static dfp::decimal32 round_error() throw() { return ...; }

          static const int min_exponent   = -95;
          static const int min_exponent10 = min_exponent;
          static const int max_exponent   = 96;
          static const int max_exponent10 = max_exponent; 

          static const bool has_infinity             = true;
          static const bool has_quiet_NaN            = true;
          static const bool has_signaling_NaN        = true;
          static const float_denorm_style has_denorm = denorm_present;
          static const bool has_denorm_loss          = true;

          static dfp::decimal32 infinity()      throw() { return ...; }
          static dfp::decimal32 quiet_NaN()     throw() { return ...; }
          static dfp::decimal32 signaling_NaN() throw() { return ...; }
          static dfp::decimal32 denorm_min()    throw() { return DEC32_DEN; }

          static const bool is_iec559       = false;
          static const bool is_bounded      = true;
          static const bool is_modulo       = false;
          static const bool traps           = true;
          static const bool tinyness_before = true;

          static const float_round_style round_style = round_indeterminate;
        };
      }

--end example]

3.3 64-bit Decimal type

3.3.1 Header <dec64> synopsis

      #include <iosfwd>

      namespace dfp {
        class decimal64;

        // 3.3.10 unary arithmetic operators:
        decimal64 operator+(const decimal64 & lhs);
        decimal64 operator-(const decimal64 & lhs);

        // 3.3.11 binary arithmetic operators:
        template <class LHS>
        /* implementation-defined */ operator+(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator+(const decimal64 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator-(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator-(const decimal64 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator*(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator*(const decimal64 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator/(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator/(const decimal64 & lhs,
                                               const RHS & rhs);

        // 3.3.12 comparison operators:
        template <class LHS>
        /* implementation-defined */ operator==(const LHS & lhs,
                                                const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator==(const decimal64 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator!=(const LHS & lhs,
                                                const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator!=(const decimal64 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<(const decimal64 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<=(const LHS & lhs,
                                                const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<=(const decimal64 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>(const LHS & lhs,
                                               const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>(const decimal64 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>=(const LHS & lhs,
                                                const decimal64 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>=(const decimal64 & lhs,
                                                const RHS & rhs);

        // 3.3.13 input/output:
        template <class charT, class traits>
          std::basic_istream<charT, traits> &
            operator>>(std::basic_istream<charT, traits> & is, const decimal64 & d);

        template <class charT, class traits>
          std::basic_ostream<charT, traits> &
            operator<<(std::basic_ostream<charT, traits> & is, const decimal64 & d);
      }

3.3.2 Class decimal64

      namespace dfp {
        class decimal64 {
          public:

            // 3.2.3 construct/copy/destroy:
            decimal64();
            decimal64(const decimal64 & d64);
            decimal64 & operator=(const decimal64 & d64);
            ~decimal64();

            // 3.3.4 conversion from floating-point type:
            decimal64(const decimal32 & d32);
            explicit decimal64(const decimal128 & d128);
            explicit decimal64(float r);
            explicit decimal64(double r);
            explicit decimal64(long double r);

            // 3.3.5 conversion from integral type:
            decimal64(int z);
            decimal64(unsigned int z);
            decimal64(long z);
            decimal64(unsigned long z);
            decimal64(long long z);
            decimal64(unsigned long long z);

            // 3.3.6 initialization from coefficient and exponent:
            decimal64(long long coeff, int exponent);
            decimal64(unsigned long long coeff, int exponent);

            // 3.3.7 conversion to generic floating-point type:
            long double to_long_double() const;

            // 3.3.8 conversion to integral type:
            operator long long() const;

            // 3.3.9 compound assignment:
            decimal64 & operator+=(const decimal64 & rhs);
            template <class T>
              /* implementation-defined */ operator+=(T rhs);

            decimal64 & operator-=(const decimal64 & rhs);
            template <class T>
              /* implementation-defined */ operator-=(T rhs);

            decimal64 & operator*=(const decimal64 & rhs);
            template <class T>
              /* implementation-defined */ operator*=(T rhs);

            decimal64 & operator/=(const decimal64 & rhs);
            template <class T>
              /* implementation-defined */ operator/=(T rhs);
        };
      }

3.3.3 Construct/copy/destroy

decimal64();

Effects: Constructs an object of type decimal64 with the value 0;

decimal64(const decimal64 & d64);
decimal64 & operator=(const decimal64 & d64);

Effects: Copies an object of type decimal64.

~decimal64();

Effects: Destroys an object of type decimal64.

3.3.4 Conversion from floating-point

decimal64(const decimal32 & d32);

Effects: Constructs an object of type decimal64 by converting from type decimal32. Conversion is performed as in IEEE-754R.

explicit decimal64(const decimal128 & d128);

Effects: Constructs an object of type decimal64 by converting from type decimal128. Conversion is performed as in IEEE-754R.

explicit decimal64(float r);

Effects: Constructs an object of type decimal64 by converting from type float. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal64(double r);

Effects: Constructs an object of type decimal64 by converting from type double. If std::numeric_limits<double>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal64(long double r);

Effects: Constructs an object of type decimal64 by converting from type long double. If std::numeric_limits<long double>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

3.3.5 Conversion from integral type

decimal64(int z);
decimal64(unsigned int z);
decimal64(long z);
decimal64(unsigned long z);
decimal64(long long z);
decimal64(unsigned long long z);

Effects: Constructs an object of type decimal64 by converting from the type of z. Conversion is performed as in IEEE-754R.

3.3.6 Initialization from coefficient and exponent

decimal64(long long coeff, int exponent);
decimal64(unsigned long long coeff, int exponent);

Effects: If coeff x 10exponent is representable within the range of decimal32, constructs an object of type decimal64 with that value. Otherwise, initializes the object with DEC_NAN [3.7.1].

[Editor's note: is it better to throw a std::range_error exception in the error case?]

3.3.7 Conversion to generic floating-point

long double to_long_double() const;

Returns: if std::numeric_limits<long double>::is_iec559 == true, returns the result of conversion of *this to long double, performed as in IEEE-754R. Otherwise, the returned value is implementation-defined.

[Editor's note: this notation is ugly, but C++ does not currently allow a user-defined conversion operator to be explicit.]

3.3.8 Conversion to integral type

operator long long() const;

Returns: Returns the result of the conversion of *this to long long, performed as in IEEE-754R.

3.3.9 Compound assignment

decimal64 & operator+=(const decimal64 & rhs);

Effects: Computes the sum of rhs and *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator+=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal128.
Returns: operator+=(decimal64(rhs))

decimal64 & operator-=(const decimal64 & rhs);

Effects: Subtracts rhs from *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator-=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal128.
Returns: operator-=(decimal64(rhs))

decimal64 & operator*=(const decimal64 & rhs);

Effects:Multiplies *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator*=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal128.
Returns: operator*=(decimal64(rhs))

decimal64 & operator/=(const decimal64 & rhs);

Effects:Divides *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator/=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal128.
Returns: operator/=(decimal64(rhs))

3.3.10 Unary arithmetic operators

decimal64 operator+(const decimal64 & lhs);

Returns: Adds lhs to 0, as in IEEE-754R, and returns the result.

decimal64 operator-(const decimal64 & lhs);

Returns: Subtracts lhs from 0, as in IEEE-754R, and returns the result.

3.3.11 Binary arithmetic operators

template <class LHS>
/* implementation-defined */ operator+(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator+(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator-(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Subtracts rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator-(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Subtracts rhs from lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator*(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator*(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator/(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator/(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

3.2.12 Comparison operators

template <class LHS>
/* implementation-defined */ operator==(const LHS & lhs,
                                        const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator==(const decimal64 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator!=(const LHS & lhs,
                                        const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator!=(const decimal64 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator<(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator<=(const LHS & lhs,
                                        const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<=(const decimal64 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>(const LHS & lhs,
                                       const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>(const decimal64 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>=(const LHS & lhs,
                                        const decimal64 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>=(const decimal64 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

3.3.13 Input/output

To be written.

3.3.6 Addition to header <limits>

The standard template std::numeric_limits shall be specialized for the decimal64 type.

[Example:

      namespace std {
        template<> class numeric_limits<dfp::decimal64> {
        public:
          static const bool is_specialized = true;

          static dfp::decimal64 min() throw() { return DEC64_MIN; }
          static dfp::decimal64 max() throw() { return DEC64_MAX; }

          static const int digits      = 16;
          static const int digits10    = digits; 

          static const bool is_signed  = true;
          static const bool is_integer = false;
          static const bool is_exact   = false;

          static const int radix = 10;
          static dfp::decimal64 epsilon()     throw() { return DEC64_EPSILON; }
          static dfp::decimal64 round_error() throw() { return ...; }

          static const int min_exponent   = -383;
          static const int min_exponent10 = min_exponent;
          static const int max_exponent   = 384;
          static const int max_exponent10 = max_exponent; 

          static const bool has_infinity             = true;
          static const bool has_quiet_NaN            = true;
          static const bool has_signaling_NaN        = true;
          static const float_denorm_style has_denorm = denorm_present;
          static const bool has_denorm_loss          = true;

          static dfp::decimal64 infinity()      throw() { return ...; }
          static dfp::decimal64 quiet_NaN()     throw() { return ...; }
          static dfp::decimal64 signaling_NaN() throw() { return ...; }
          static dfp::decimal64 denorm_min()    throw() { return DEC64_DEN; }

          static const bool is_iec559       = false;
          static const bool is_bounded      = true;
          static const bool is_modulo       = false;
          static const bool traps           = true;
          static const bool tinyness_before = true;

          static const float_round_style round_style = round_indeterminate;
        };
      }

--end example]

3.4 128-bit Decimal type

3.4.1 Header <dec128> synopsis

      #include <iosfwd>

      namespace dfp {
        class decimal128;

        // 3.4.10 unary arithmetic operators:
        decimal128 operator+(const decimal128 & lhs);
        decimal128 operator-(const decimal128 & lhs);

        // 3.4.11 binary arithmetic operators:
        template <class LHS>
        /* implementation-defined */ operator+(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator+(const decimal128 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator-(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator-(const decimal128 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator*(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator*(const decimal128 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator/(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator/(const decimal128 & lhs,
                                               const RHS & rhs);

        // 3.4.12 comparison operators:
        template <class LHS>
        /* implementation-defined */ operator==(const LHS & lhs,
                                                const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator==(const decimal128 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator!=(const LHS & lhs,
                                                const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator!=(const decimal128 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<(const decimal128 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator<=(const LHS & lhs,
                                                const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator<=(const decimal128 & lhs,
                                                const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>(const LHS & lhs,
                                               const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>(const decimal128 & lhs,
                                               const RHS & rhs);

        template <class LHS>
        /* implementation-defined */ operator>=(const LHS & lhs,
                                                const decimal128 & rhs);

        template <class RHS>
        /* implementation-defined */ operator>=(const decimal128 & lhs,
                                                const RHS & rhs);

        // 3.4.12 input/output
        template <class charT, class traits>
          std::basic_istream<charT, traits> &
            operator>>(std::basic_istream<charT, traits> & is, const decimal128 & d);

        template <class charT, class traits>
          std::basic_ostream<charT, traits> &
            operator<<(std::basic_ostream<charT, traits> & is, const decimal128 & d);
      }

3.4.2 Class decimal128

      namespace dfp {
        class decimal128 {
          public:

            // 3.4.3 construct/copy/destroy:
            decimal128();
            decimal128(const decimal128 & d128);
            decimal128 & operator=(const decimal128 & d128);
            ~decimal128();

            // 3.4.5 conversion from floating-point type: 
            decimal128(const decimal32 & d32);
            decimal128(const decimal64 & d64);
            explicit decimal128(float r);
            explicit decimal128(double r);
            explicit decimal128(long double r);

            // 3.4.5 conversion from integral type: 
            decimal128(int z);
            decimal128(unsigned int z);
            decimal128(long z);
            decimal128(unsigned long z);
            decimal128(long long z);
            decimal128(unsigned long long z);

            // 3.4.6 conversion from generic floating-point type: 
            decimal128(long long coeff, int exponent);
            decimal128(unsigned long long coeff, int exponent);

            // 3.4.7 conversion to generic floating-point type: 
            long double to_long_double() const;

            // 3.4.8 conversion to integral type: 
            operator long long() const;

            // 3.4.9 compound assignment: 
            decimal128 & operator+=(const decimal128 & rhs);
            template <class T>
              /* implementation-defined */ operator+=(T rhs);

            decimal128 & operator-=(const decimal128 & rhs);
            template <class T>
              /* implementation-defined */ operator-=(T rhs);

            decimal128 & operator*=(const decimal128 & rhs);
            template <class T>
              /* implementation-defined */ operator*=(T rhs);

            decimal128 & operator/=(const decimal128 & rhs);
            template <class T>
              /* implementation-defined */ operator/=(T rhs);
        };
      }

3.4.3 Construct/copy/destroy

decimal128();

Effects: Constructs an object of type decimal128 with the value 0;

decimal128(const decimal128 & d128);
decimal128 & operator=(const decimal128 & d128);

Effects: Copies an object of type decimal128.

~decimal128();

Effects: Destroys an object of type decimal128.

3.4.4 Conversion from floating-point type

decimal128(const decimal32 & d32);

Effects: Constructs an object of type decimal32 by converting from type decimal32. Conversion is performed as in IEEE-754R.

decimal128(const decimal64 & d64);

Effects: Constructs an object of type decimal128 by converting from type decimal64. Conversion is performed as in IEEE-754R.

explicit decimal128(float r);

Effects: Constructs an object of type decimal128 by converting from type float. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal128(double r);

Effects: Constructs an object of type decimal128 by converting from type double. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

explicit decimal128(long double r);

Effects: Constructs an object of type decimal128 by converting from type long double. If std::numeric_limits<float>::is_iec559 == true then the conversion is performed as in IEEE-754R. Otherwise, the result of the conversion is implementation-defined.

3.4.5 Conversion from integral type

decimal128(int z);
decimal128(unsigned int z);
decimal128(long z);
decimal128(unsigned long z);
decimal128(long long z);
decimal128(unsigned long long z);

Effects: Constructs an object of type decimal128 by converting from the type of z. Conversion is performed as in IEEE-754R.

3.4.6 Initialization from coefficient and exponent

decimal128(long long coeff, int exponent);
decimal128(unsigned long long coeff, int exponent);

Effects: If coeff x 10exponent is representable within the range of decimal128, constructs an object of type decimal128 with that value. Otherwise, initializes the object with DEC_NAN [3.7.1].

[Editor's note: is it better to throw a std::range_error exception in the error case?]

3.4.7 Conversion to floating-point type

long double to_long_double() const;

Returns: If std::numeric_limits<long double>::is_iec559 == true, returns the result of the conversion of *this to long double, performed as in IEEE-754R. Otherwise, the returned value is implementation-defined.

[Editor's note: this notation is ugly, but C++ does not currently allow a user-defined conversion operator to be explicit.]

3.4.8 Conversion to integral type

operator long long() const;

Returns: Returns the result of the conversion of *this to long long, performed as in IEEE-754R.

3.4.9 Compound assignment

decimal128 & operator+=(const decimal128 & rhs);

Effects: Computes the sum of rhs and *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator+=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal64.
Returns: operator+=(decimal128(rhs))

decimal128 & operator-=(const decimal128 & rhs);

Effects: Subtracts rhs from *this, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator-=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal64.
Returns: operator-=(decimal128(rhs))

decimal128 & operator*=(const decimal128 & rhs);

Effects:Multiplies *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator*=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal64.
Returns: operator*=(decimal128(rhs))

decimal128 & operator/=(const decimal128 & rhs);

Effects:Divides *this by rhs, as in IEEE-754R, and assigns the result to *this.
Returns: *this

template <class T>
/* implementation-defined */ operator/=(const T & rhs);

Constraints: T is one of the integral types, decimal32, or decimal64.
Returns: operator/=(decimal128(rhs))

3.4.10 Unary arithmetic operators

decimal128 operator+(const decimal128 & lhs);

Returns: Adds lhs to 0, as in IEEE-754R, and returns the result.

decimal128 operator-(const decimal128 & lhs);

Returns: Subtracts lhs from 0, as in IEEE-754R, and returns the result.

3.4.11 Binary arithmetic operators

template <class LHS>
/* implementation-defined */ operator+(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator+(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Adds rhs to lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator-(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Subtracts rhs to lhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator-(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Subtracts rhs from lhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator*(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator*(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Multiplies lhs by rhs, as in IEEE-754R, and returns the result.

template <class LHS>
/* implementation-defined */ operator/(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

template <class RHS>
/* implementation-defined */ operator/(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: Divides lhs by rhs, as in IEEE-754R, and returns the result.

3.2.12 Comparison operators

template <class LHS>
/* implementation-defined */ operator==(const LHS & lhs,
                                        const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator==(const decimal128 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator!=(const LHS & lhs,
                                        const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator!=(const decimal128 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is not exactly equal to rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator<(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than rhs according to IEEE-754R, false otherwise.


template <class LHS>
/* implementation-defined */ operator<=(const LHS & lhs,
                                        const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator<=(const decimal128 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is less than or equal torhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>(const LHS & lhs,
                                       const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>(const decimal128 & lhs,
                                       const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than rhs according to IEEE-754R, false otherwise.

template <class LHS>
/* implementation-defined */ operator>=(const LHS & lhs,
                                        const decimal128 & rhs);

Constraints: LHS is one of the integral types or one of the decimal floating-point types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

template <class RHS>
/* implementation-defined */ operator>=(const decimal128 & lhs,
                                        const RHS & rhs);

Constraints: RHS is one of the integral types.
Returns: true if lhs is greater than or equal to rhs according to IEEE-754R, false otherwise.

3.4.13 Input/output

To be written.

3.4.14 Addition to header <limits>

The standard template std::numeric_limits shall be specialized for the decimal128 type.

[Example:

      namespace std {
        template<> class numeric_limits<dfp::decimal128> {
        public:
          static const bool is_specialized = true;

          static dfp::decimal128 min() throw() { return DEC128_MIN; }
          static dfp::decimal128 max() throw() { return DEC128_MIN; }

          static const int digits   = 384;
          static const int digits10 = digits; 

          static const bool is_signed  = true;
          static const bool is_integer = false;
          static const bool is_exact   = false;

          static const int radix = 10;
          static dfp::decimal128 epsilon()     throw() { return DEC128_EPSILON; }
          static dfp::decimal128 round_error() throw() { return ...; }

          static const int min_exponent   = -6143;
          static const int min_exponent10 = min_exponent;
          static const int max_exponent   = 6144;
          static const int max_exponent10 = max_exponent; 

          static const bool has_infinity             = true;
          static const bool has_quiet_NaN            = true;
          static const bool has_signaling_NaN        = true;
          static const float_denorm_style has_denorm = denorm_present;
          static const bool has_denorm_loss          = true;

          static dfp::decimal128 infinity()      throw() { return ...; }
          static dfp::decimal128 quiet_NaN()     throw() { return ...; }
          static dfp::decimal128 signaling_NaN() throw() { return ...; }
          static dfp::decimal128 denorm_min()    throw() { return DEC128_DEN; }

          static const bool is_iec559       = false;
          static const bool is_bounded      = true;
          static const bool is_modulo       = false;
          static const bool traps           = true;
          static const bool tinyness_before = true;

          static const float_round_style round_style = round_indeterminate;
        };
      }

--end example]

3.5 Headers <cdecfloat> and <decfloat.h>

The standard C++ headers <cfloat> and <float.h> define characteristics of the floating-point types float, double, and long double. Their contents remain unchanged by this Technical Report.

Headers <cdecfloat> and <decfloat.h> define characteristics of the decimal floating-point types decimal32, decimal64, and decimal128. As well, these headers define the convenience typedefs _Decimal32, _Decimal64, and _Decimal128, for compatibilty with the C programming language.

The contents of the headers <cdecfloat> and <decfloat.h> are the same. <decfloat.h> is provided for compatibility with C.

3.5.1 Header <cdecfloat> synopsis

      #include <dec32>
      #include <dec64>
      #include <dec128>

      // C-compatibility convenience typedefs:
      typedef dfp::decimal32  _Decimal32;
      typedef dfp::decimal64  _Decimal64;
      typedef dfp::decimal128 _Decimal128;

      // number of digits in the coefficient:
      #define DEC32_MANT_DIG  7
      #define DEC64_MANT_DIG 16
      #define DEC64_MANT_DIG 34

      // minimum exponent:
      #define DEC32_MIN_EXP    -95
      #define DEC64_MIN_EXP   -383
      #define DEC128_MIN_EXP -6143

      // maximum exponent:
      #define DEC32_MIN_EXP    96
      #define DEC64_MIN_EXP   384
      #define DEC128_MIN_EXP 6144

      // 3.5.3 maximum finite value:
      #define DEC32_MAX  /* implementation-defined */
      #define DEC64_MAX  /* implementation-defined */
      #define DEC128_MAX /* implementation-defined */

      // 3.5.4 epsilon:
      #define DEC32_EPSILON  /* implementation-defined */
      #define DEC64_EPSILON  /* implementation-defined */
      #define DEC128_EPSILON /* implementation-defined */

      // 3.5.5 minimum positive normal value:
      #define DEC32_MIN  /* implementation-defined */
      #define DEC64_MIN  /* implementation-defined */
      #define DEC128_MIN /* implementation-defined */

      // 3.5.6 minimum positive subnormal value:
      #define DEC32_DEN  /* implementation-defined */
      #define DEC64_DEN  /* implementation-defined */
      #define DEC128_DEN /* implementation-defined */
      

3.5.2 Header <decfloat.h> synopsis

      #include <cdecfloat>

      /* no further contents */

3.5.3 Maximum finite value

#define DEC32_MAX /* implementation-defined */

Expansion: an lvalue of type decimal32 equal to the maximum finite number that can be represented by an object of type decimal32; exactly equal to 9.999999 x 1096 (there are six 9's after the decimal point)

#define DEC64_MAX /* implementation-defined */

Expansion: an lvalue of type decimal64 equal to the maximum finite number that can be represented by an object of type decimal64; exactly equal to 9.999999999999999 x 10384 (there are fifteen 9's after the decimal point)

#define DEC128_MAX /* implementation-defined */

Expansion: an lvalue of type decimal128 equal to the maximum finite number that can be represented by an object of type decimal128; exactly equal to 9.999999999999999999999999999999999 x 106144 (there are thirty-three 9's after the decimal point)

3.5.4 Epsilon

#define DEC32_EPSILON /* implementation-defined */

Expansion: an lvalue of type decimal32 equal to the difference between 1 and the least value greater than 1 that can be represented by an object of type decimal32; exactly equal to 1 x 10-6

#define DEC64_EPSILON /* implementation-defined */

Expansion: an lvalue of type decimal64 equal to the difference between 1 and the least value greater than 1 that can be represented by an object of type decimal32; exactly equal to 1 x 10-15

#define DEC128_EPSILON /* implementation-defined */

Expansion: an lvalue of type decimal128 equal to the difference between 1 and the least value greater than 1 that can be represented by an object of type decimal128; exactly equal to 1 x 10-33

3.5.5 Minimum positive normal value

#define DEC32_MIN /* implementation-defined */

Expansion: an lvalue of type decimal32 equal to the minimum positive normal number that can be represented by an object of type decimal32; exactly equal to 1 x 10-95

#define DEC64_MIN /* implementation-defined */

Expansion: an lvalue of type decimal64 equal to the minimum positive normal number that can be represented by an object of type decimal64; exactly equal to 1 x 10-383

#define DEC128_MIN /* implementation-defined */

Expansion: an lvalue of type decimal128 equal to the minimum positive normal number that can be represented by an object of type decimal128; exactly equal to 1 x 10-6143

3.5.6 Minimum positive subnormal value

#define DEC32_DEN /* implementation-defined */

Expansion: an lvalue of type decimal32 equal to the minimum positive finite number that can be represented by an object of type decimal32; exactly equal to 1 x 10-101

#define DEC64_DEN /* implementation-defined */

Expansion: an lvalue of type decimal64 equal to the minimum positive finite number that can be represented by an object of type decimal64; exactly equal to 1 x 10-398

#define DEC128_DEN /* implementation-defined */

Expansion: an lvalue of type decimal128 equal to the minimum positive finite number that can be represented by an object of type decimal128; exactly equal to 1 x 10-6176

3.6 Additions to <cfenv> and <fenv.h>

The header <cfenv> is described in TR1 8.6. The header <fenv.h> is described in TR1 8.7.

3.6.1 Rounding modes

The following macros are added to <cfenv> and -- transitively -- <fenv.h>:

      FE_DEC_DOWNWARD
      FE_DEC_TONEAREST
      FE_DEC_TONEARESTFROMZERO
      FE_DEC_TOWARD_ZERO
      FE_DEC_UPWARD

These macros are used by the fegetround and fesetround functions for getting and setting the rounding mode to be used in decimal floating-point operations.

3.6.2 Exception modes

The following macros are added to <cfenv> and -- transitively -- <fenv.h>:

      FE_DEC_DIVBYZERO
      FE_DEC_INEXACT
      FE_DEC_INVALID
      FE_DEC_OVERFLOW
      FE_DEC_UNDERFLOW

These macros are used by the feclearexcept, fegetexceptflag, feraiseexcept, fesetexceptflag, and fesetexcept functions.

3.7 Additions to <cmath> and <math.h>

The elementary mathematical functions declared in the standard C++ header <cmath> are overloaded by this Technical Report to support the decimal floating-point types. The macros HUGE_VAL_D32, HUGE_VAL_D64, HUGE_VAL_D128, DEC_INFINITY, and DEC_NAN are defined for use with these functions. With the exception of sqrt, fmax, and fmin, the accuracy of the result of a call to one of these functions is implementation-defined. The implementation may state that the accuracy is unknown. The classification macros FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, and FP_ZERO and the comparison macros isgreater, isgreaterequal, isless, islessequal, islessgreater, and isunordered are also extended to handle the decimal floating-point types.

3.7.1 Additions to header <cmath> synopsis


      // 3.7.2 macros:
      #define HUGE_VAL_D32  /* implementation-defined */
      #define HUGE_VAL_D64  /* implementation-defined */
      #define HUGE_VAL_D128 /* implementation-defined */
      #define DEC_INFINITY  /* implementation-defined */
      #define DEC_NAN       /* implementation-defined */
     
      namespace dfp {
        // 3.7.3 samequantum functions:
        bool samequantum     (decimal32 x,  decimal32 y);
        bool samequantumd32  (decimal32 x,  decimal32 y);

        bool samequantum     (decimal64 x,  decimal64 y);
        bool samequantumd64  (decimal64 x,  decimal64 y);

        bool samequantum     (decimal128 x, decimal128 y);
        bool samequantumd128 (decimal128 x, decimal128 y);

        // 3.7.4 quantize functions:
        decimal32  quantize     (decimal32 x,  decimal32 y);
        decimal32  quantized32  (decimal32 x,  decimal32 y);

        decimal64  quantize     (decimal64 x,  decimal64 y);
        decimal64  quantized64  (decimal64 x,  decimal64 y);

        decimal128 quantize     (decimal128 x, decimal128 y);
        decimal128 quantized128 (decimal128 x, decimal128 y);
      } 

3.7.2 <cmath> macros

#define HUGE_VAL_D32 /* implementation-defined */

Expansion: a positive lvalue of type decimal32.

#define HUGE_VAL_D64 /* implementation-defined */

Expansion: a positive lvalue of type decimal64, not necessarily representable as a decimal32.

#define HUGE_VAL_128 /* implementation-defined */

Expansion: a positive lvalue of type decimal128, not necessarily representable as a decimal64.

#define DEC_INFINITY /* implementation-defined */

Expansion: an lvalue of type decimal32 representing infinity.

#define DEC_NAN /* implementation-defined */

Expansion: an lvalue of type decimal32 representing quiet NaN.

3.7.3 samequantum functions

bool samequantum     (decimal32 x, decimal32 y);

Effects: behaves as in IEEE-754R.

bool samequantumd32  (decimal32 x, decimal32 y);

Returns: samequantum(x, y)

bool samequantum     (decimal64 x, decimal64 y);

Effects: behaves as in IEEE-754R.

bool samequantumd64  (decimal64 x, decimal64 y);

Returns: samequantum(x, y)

bool samequantum     (decimal128 x, decimal128 y);

Effects: behaves as in IEEE-754R.

bool samequantumd128 (decimal128 x, decimal128 y);

Returns: samequantum(x, y)

3.7.4 quantize functions

bool quantize     (decimal32 x, decimal32 y);

Effects: behaves as in IEEE-754R.

bool quantized32  (decimal32 x, decimal32 y);

Returns: quantize(x, y)

bool quantize     (decimal64 x, decimal64 y);

Effects: behaves as in IEEE-754R.

bool quantized64  (decimal64 x, decimal64 y);

Returns: quantize(x, y)

bool quantize     (decimal128 x, decimal128 y);

Effects: behaves as in IEEE-754R.

bool quantized128 (decimal128 x, decimal128 y);

Returns: quantize(x, y)

3.7.5 Changes to <math.h>

Each name placed into the namespace dfp by <cmath> is placed into both the namespace dfp and the global namespace by <math.h>. Macros added to <cmath> by this Technical Report are transitively added to <math.h>.

3.8 Additions to <cstdlib> and <stdlib.h>

3.8.1 Additions to header <cstdlib> synopsis

      namespace dfp {
        // 3.8.2 strtod functions
        decimal32  strtod32  (const char * nptr, char ** endptr);
        decimal64  strtod64  (const char * nptr, char ** endptr);
        decimal128 strtod128 (const char * nptr, char ** endptr);
      }

3.8.2 strtod functions

To be written.

3.8.3 Changes to <stdlib.h>

Each name placed into the namespace dfp by <cstdlib> is placed into both the namespace dfp and the global namespace by <stdlib.h>.

3.9 Additions to <wchar.h> and <cwchar>

3.9.1 Additions to <cwchar> synopsis

      namespace dfp {
        // 3.9.2 wcstod functions
        decimal32  wcstod32  (const char * nptr, char ** endptr);
        decimal64  wcstod64  (const char * nptr, char ** endptr);
        decimal128 wcstod128 (const char * nptr, char ** endptr);
      }

3.9.2 wcstod functions

To be written.

3.9.3 Changes to <wchar.h>

Each name placed into the namespace dfp by <cwchar> is placed into both the namespace dfp and the global namespace by <wchar.h>.

3.10 Type traits

3.10.1 Addition to header <type_traits> synopsis

      namespace dfp {
        // 3.10.2 is_decimal_fp type_trait:
        template <class T> struct is_decimal_fp; 
      }

3.10.2 is_decimal_fp type_trait

is_decimal_fp is a UnaryTypeTrait [TR 4.1] and satisfies all of the requirements of that category [TR 4.4].

Template Condition Comments
template <class T>
struct is_decimal_fp;
T is one of decimal32, decimal64, or decimal128

[Editor's note: if the decimal floating-point types are implemented as builtin types, this will have implications to several of the TR1 type_traits. What should is_class<decimal64> return? How about is_arithmetic<decimal64>?]

4 Notes on C compatibility

One of the goals of the design of the decimal floating-point types that are the subject of this Technical Report is to minimize incompatibility with the C decimal floating types; however, differences between the C and C++ languages make some incompatibilty inevitable. Differences between the C and C++ decimal types -- and techniques for overcoming them -- are described in this section.

4.1 Use of <decfloat.h>

To aid portability to C++, it is recommended that C programmers #include the header file <decfloat.h> in those translation units that make use of the decimal floating types. This ensures that the equivalent C++ floating-point types will be available, should the program source be ported to C++.

4.2 Literals

To be written.

4.3 Conversions

To be written.