<html>
<head>
  <title>NTTP are incomplete without float, double, and long double! </title>
  <meta Author="Jorg Brown" content="proposal">
</head>

<body bgcolor="#FFFFFF" text="#000000">

<style>
del { background-color:LightPink; }
ins { background-color:LightGreen; }
</style>

<font size=-1>
Jorg Brown &lt;jorg.brown@gmail.com&gt;
<br>
2019-07-19
<br>
Document P1714R1
<code>$Id: proposal.html,v 1.50 2019/07/19 02:12:37 jorg Exp $</code>
</font>

<h1>P1714: NTTP are incomplete without float, double, and long double!
 (Revision 1)</h1>

<h2>Revision history</h2>

<ul>
  <li>2019-06-18: R0: Proposal to allow float, double, and long double template parameters.</li>
  <li>2019-07-19: R1: Proposal to allow float, double, and long double template parameters.
    <ul>
      <li>Rather than modify paragraph 7 of 13.1 [temp.param], it is deleted entirely.</li>
      <li>Support for floating point is specifically specified in paragraph 4 instead.</li>
      <li>Section 13.5 Type equivalence [temp.type] is updated for floating-point since operator== will not work.</li>
      <li>__cpp_nontype_template_paramater_class is suggested as the feature test macro to use.</li>
      <li>Minor commentary updates and typo fixes.</li>
    </ul>
  </li>
</ul>

<h2>Introduction.</h2>

<p>
For decades, template parameters could be either types or constants of certain trivial types: integers, enums, pointers, etc.  Notably absent from this list were floating-point values.  Recently, the adoption of P0732 has allowed constants of class type to be used as template parameters.  Furthermore, P0476 allows us to perform constexpr bit-casting of floating-point values.  And in the decades since floating-point types were banned from use as template parameters, compile-time computation of floating-point values has advanced dramatically.
</p>

<p>
This paper, P1714. proposes to include floating-point values into the list of acceptable template parameters.
</p>

<h2>I. Motivation and Scope.</h2>

<p>
Consider the pow() function.  The most general implementation uses log and exp:

<blockquote>
<pre>
double pow(double base, double exponent) {
  return exp(log(base) * exponent);
}
</pre>
</blockquote>

This has several disadvantages, one being that if exponent is an integer, the exactness available through multiplication isn't achieved due to round-off error in exp and log.  So we end up in the unfortunate situation that raising an integer to an integral power sometimes produces a result that is very close to, but not equal to, an integer.  Similarly, often a number is raised to the power 1/2 in an attempt to obtain a square root, esp. by programmers from other languages who are unaware that the standard library offers an extremely accurate square-root instruction.
</p>

<p>
But suppose we could specify the exponent:

<blockquote>
<pre>
template&lt;double exponent>
double pow(double base);
</pre>
</blockquote>

The default implementation of such a function could use the log/exp solution, while the code could be specialized for common integer powers and binary fractions (1/2, 1/4) to produce far more accurate results - and to produce them faster.  There's just one problem: the floating-point exponent is not allowed as a template parameter.
</p>

<p>
With the new facilities of C++20, we can work around this problem: (Working demo at <a href="https://godbolt.org/z/FaeQc4">Compiler Explorer</a>)

<blockquote>
<pre>
template&lt;typename T> struct AsTemplateArg {
  std::array&lt;char, sizeof(T)> buffer = {};
  constexpr AsTemplateArg(const std::array&lt;char, sizeof(T)> buf) : buffer(buf) {}
  constexpr AsTemplateArg(T t) : AsTemplateArg(std::bit_cast&lt;std::array&lt;char, sizeof(T)> >(t)) {}
  constexpr operator T() const { return std::bit_cast&lt;T>(this->buffer); }
};

template&lt;AsTemplateArg&lt;double> exponent>
double pow(double base) {
  return exp(log(base) * double{exponent});
}

template&lt;>
double pow&lt;AsTemplateArg&lt;double>{1.0}>(double base) {
  return base;
}
</pre> 
</blockquote>

<p>
But why?  Let's just let the compiler do what it can do very easily, rather than force the use of a bunch of bit-cast boilerplate.
</p>

<h2>Impact on the Standard</h2>
<p>
Portions of the standard which currently prohibit use of floating-point constants as template parameters shall be removed.
</p>

<h2>Proposed Wording</h2>

<p>Note: All changes are relative to the 2019-06-13 working draft of C++20.</p>

<p>Modify 13.1 Template parameters [temp.param] as follows:</p>

<p>Modify paragraph 4 to add floating-point:</p>

<blockquote>
<p>A non-type template-parameter shall have one of the following (optionally cv-qualified) types:</p>

<p>-- a literal type that has strong structural equality ([class.compare.default]),</p>

<p><ins>-- a floating-point type.</ins></p>

<p>-- an lvalue reference type,</p>

<p>-- a type that contains a placeholder type ([dcl.spec.auto]), or</p>

<p>-- a placeholder for a deduced class type ([dcl.type.class.deduct]).</p>
</blockquote>

<p>Delete paragraph 7 (which begins with <del>"A non-type template-parameter shall not be declared to..."</del>]</p>

<p>Modify 13.5 Type equivalence [temp.type] paragraph 1 as follows:</p>

<blockquote>
<p>Two <i>template-ids</i> refer to the same class, function, or variable if<ins>, after converting each <i>template-argument</i> to match the kind and type of its corresponding <i>template-parameter,</i> if needed,</ins></p>

<p>-- their <i>template-names,</i> <i>operator-function-ids,</i> or <i>literal-operator-ids</i> refer to the same template and</p>

<p>-- their corresponding type <del><i>template-arguments</i></del><ins>template arguments</ins> are the same type and</p>

<p>-- their corresponding non-type <del><i>template-arguments</i></del><ins>template arguments</ins> of pointer-to-member type refer to the same class member or are both the null member pointer value and</p>

<p>-- their corresponding non-type <del><i>template-arguments</i></del><ins>template arguments</ins> of reference type refer to the same object or function and</p>

<p><ins>-- their corresponding non-type template arguments of floating-point type have identical value representations and</ins></p>

<p>-- their remaining corresponding non-type <del><i>template-arguments</i></del><ins>template arguments</ins> have the same type and <del>value after conversion to the type of the <i>template-parameter,</i> where they are considered to have the same value if they</del> compare equal with the == operator ([expr.eq]), and</p>

<p>-- their corresponding template <i>template-arguments</i> refer to the same template.</p>
</blockquote>

<p>I propose to update the existing feature test macro, __cpp_nontype_template_parameter_class, for this feature.</p>

<h2>Alternatives Considered</h2>

<p>
Originally the suggested design was to decompose a floating-point type into sign, exponent, and mantissa, and then use the existing P0732 wording to allow that triplet of values to represent the floating-point constant in question.  This was changed because:
</p>

<p>
1) It's more work for the compiler.  All compilers must already know how to represent floating-point values in bit form for their target architecture, in case the user declares a global floating-point value with an initial value.  And that bytewise representation satisfies P0732's requirements for template parameter, no decomposition needed.
</p>

<p>
2) Such a decomposition does not distinguish between positive zero and negative zero, which would prohibit the implementation of a function such as pow, which distinguishes between positive and negative zero.  Even printf is defined to treat +0.0 and -0.0 differently.
</p>

<p>
3) Such a decomposition proves difficult for INF and NaN values, especially since <a href="http://wg21.link/P0533">P0533</a> has not yet been adopted.
</p>

<h2>Downsides</h2>

<p>
Using bit-level equality rather than the type's underlying operator== means that if you specialize a template that uses float/double parameters, using 0.0 as your specialization, then your specialization will not impact code that passes -0.0 as a parameter.  (But note that the difference between 0.0 and -0.0 is observable at runtime.  For example, 1/0.0 produces +INFINITY, while 1/-0.0 produces -INFINITY)
</p>

<p>
There is a related impact with NaNs; attempting to specialize such a template using a value of NaN or -NaN will only specialize those two NaNs, rather than the full range of NaNs that exist.  Nevertheless, this is not expected to be an issue; expressions which produce infinities and nans are not allowed at compile-time.  Also, if a user wants to have different template behavior for NaNs, it's a simple matter of adding:
</p>

<blockquote>
<pre>
if constexpr(!(float_param == float_param)) {
  // Handle NaN
}
</pre> 
</blockquote>

<h2>References</h2>

<ul>
  <li>JF Bastien, <a href="http://wg21.link/p0476">Bit-casting object representations</a> </li>
  <li>Jeff Snyder, <a href="http://wg21.link/p0732">Class Types in Non-Type Template Parameters</a> </li>
</ul>

</body>

</html>
