<html>
<head><title>Unbounded Decimal</title></head>
<body>
<center>
<h2>An Unbounded Decimal Floating-Point Type</h2>
</center>
<table border=0>
<tr>
  <td><b>Doc No:</b></td>
  <td>P2159R0</td>
</tr>
<tr>
  <td><b>Date:</b></td>
  <td>2020-04-23</td>
</tr>
<tr>
  <td><b>Author:</b>&nbsp;</td>
  <td>Bill Seymour</td>
</tr>
<tr>
  <td><b>Reply to:</b>&nbsp;</td>
  <td>stdbill<tt>.</tt>h<sup>@</sup>pobox<tt>.</tt>com</td>
</tr>
<tr>
  <td><b>Audience:</b></td>
  <td>LEWG-I, SG6 (Numerics)</td>
</tr>
</table>

<hr size=3>

<p>Is there any interest in an unbounded decimal floating-point type for the numbers TS proposed in P1889?

<p>(The author intends to implement an open-source version for his own use in any event.
Should he write the standardese?)

<p>This paper uses from <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1889r1.pdf">P1889R1</a>
<tt>enum</tt>&nbsp;<tt>class</tt>&nbsp;<tt>rounding</tt> (&sect;3.3),
<tt>integer</tt> (&sect;9.3), and
<tt>decimal128</tt> (IEEE 754 decimal floating-point, future &sect;8.5?).

<p>The &ldquo;scale&rdquo; is the number of digits to the right of the decimal point.
The rounding mode defaults to <tt>tie_to_even</tt> but can be changed at any time.

<p>Note the arguments in
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2010r0.pdf">P2010</a>
for <tt>std::format</tt>, <tt>std::to_chars</tt> and <tt>std::from_chars</tt> which we might want to add.
(P2010&rsquo;s arguments against the I/O operators probably don&rsquo;t apply since those operators
could be written to use the <tt>numpunct</tt> facet from the stream&rsquo;s <tt>imbue</tt>d <tt>locale</tt>,
and they could be optional in freestanding environments.)

<p><hr size=3>
<h3>The basic idea:</h3>

<p>(<tt>std::</tt> qualifiers omitted for simplicity)

<p><pre>
class decimal
{
public:
  //
  // As a practical matter, we&rsquo;ll need to limit the number of fractional digits
  // to something finite.
  //
    static constexpr int max_scale = <i>implementation-defined</i>;

  //
  // Constructors, destructor:
  //
    constexpr decimal() noexcept;

    decimal(long double); // NB: implicit
    decimal(decimal128);

    decimal(const integer&amp; <i>unscaled_value</i>, int <i>scale</i>);
    decimal(integer&amp;&amp; <i>unscaled_value</i>, int <i>scale</i>) noexcept;

    // as if in "C" locale:
    template&lt;class Ch, class Tr, class A&gt;
      explicit decimal(const basic_string&lt;Ch,Tr,A&gt;&amp;, int <i>radix</i> = 10);

    ~decimal() noexcept;

  //
  // Copyable, noexcept-moveable, noexcept-swappable:
  //
    decimal(const decimal&amp;);
    decimal(decimal&amp;&amp;) noexcept;

    decimal&amp; operator=(const decimal&amp;);
    decimal&amp; operator=(decimal&amp;&amp;) noexcept;

    void swap(decimal&amp;) noexcept;

  //
  // Capacity in units of decimal digits:
  //
    size_t size() const noexcept;
    size_t capacity() const noexcept;
    void reserve(size_t <i>digits</i>);
    void shrink_to_fit();

  //
  // Conversions:
  //
    explicit operator bool() const noexcept;
    explicit operator integer() const;
    explicit operator long double() const noexcept;
    explicit operator decimal128() const noexcept;

    // as if in "C" locale:
    string to_string(int <i>scale</i> = -1, // &lt; 0 means exact
                     int <i>radix</i> = 10) const;

  //
  // Observers:
  //
    const integer&amp; unscaled_value() const noexcept;
    int scale() const noexcept;
    rounding rounding_mode() const noexcept;

    bool is_zero() const noexcept;
    bool is_neg() const noexcept;

    decimal integer_part() const;    // rounding::all_to_zero
    decimal fractional_part() const; // *this - integer_part()
    decimal modf(decimal*) const;    // after modf(double,double*)

  //
  // Modifiers:
  //
    void rescale(int <i>new_scale</i>) noexcept; // shifts decimal point in O(1) time
    void reset_rounding(rounding = rounding::tie_to_even) noexcept;

  //
  // Comparisons:
  //
    int compare(const decimal&amp;) const noexcept;

  //
  // Member operators:
  //
    decimal&amp; operator++();
    decimal&amp; operator--();

    decimal  operator++(int);
    decimal  operator--(int);

    decimal&amp; operator+=(const decimal&amp;);
    decimal&amp; operator-=(const decimal&amp;);
    decimal&amp; operator*=(const decimal&amp;);
    decimal&amp; operator/=(const decimal&amp;);

  //
  // Other arithmetic operations:
  //
    decimal&amp; abs() noexcept;
    decimal&amp; negate() noexcept;
    decimal&amp; sqrt();
    decimal&amp; pow(const decimal&amp; <i>exponent</i>);
    decimal&amp; remainder(const decimal&amp; <i>divisor</i>);
};
void swap(decimal&amp;, decimal&amp;) noexcept;

//
// Non-member operators:
//
decimal operator+(const decimal&amp;);
decimal operator+(decimal&amp;&amp;) noexcept;

decimal operator-(const decimal&amp;);
decimal operator-(decimal&amp;&amp;) noexcept;

decimal operator+(const decimal&amp;, const decimal&amp;);
decimal operator-(const decimal&amp;, const decimal&amp;);
decimal operator*(const decimal&amp;, const decimal&amp;);
decimal operator/(const decimal&amp;, const decimal&amp;);

//
// Other non-member arithmetic operations:
//
decimal abs(const decimal&amp;);
decimal abs(decimal&amp;&amp;) noexcept;

decimal sqrt(const decimal&amp;);

decimal pow(const decimal&amp; <i>base</i>, const decimal&amp; <i>exponent</i>);

decimal remainder(const decimal&amp; <i>dividend</i>, const decimal&amp; <i>divisor</i>);

//
// I/O:
//
template&lt;class Ch, class Tr&gt;
  basic_ostream&lt;Ch,Tr&gt;&amp; operator&lt;&lt;(basic_ostream&lt;Ch,Tr&gt;&amp;, const decimal&amp;);

template&lt;class Ch, class Tr&gt;
  basic_istream&lt;Ch,Tr&gt;&amp; operator&gt;&gt;(basic_istream&lt;Ch,Tr&gt;&amp;, decimal&amp;);
</pre>
<hr size=3>
All corrections and suggestions will be welcome.  All flames will be amusing.<br>Mail to:&nbsp;&nbsp;stdbill<tt>.</tt>h<sup>@</sup>pobox<tt>.</tt>com
</body>
</html>
