<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta name="generator" content="pandoc" />
  <title>P0811R1: Well-behaved interpolation for numbers and pointers</title>
  <style type="text/css">
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
  margin: 0; padding: 0; vertical-align: baseline; border: none; }
table.sourceCode { width: 100%; }
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code > span.kw { color: #007020; font-weight: bold; }
code > span.dt { color: #902000; }
code > span.dv { color: #40a070; }
code > span.bn { color: #40a070; }
code > span.fl { color: #40a070; }
code > span.ch { color: #4070a0; }
code > span.st { color: #4070a0; }
code > span.co { color: #60a0b0; font-style: italic; }
code > span.ot { color: #007020; }
code > span.al { color: #ff0000; font-weight: bold; }
code > span.fu { color: #06287e; }
code > span.er { color: #ff0000; font-weight: bold; }
  </style>
  <style type="text/css">
  s { background: #ff8888; }
  u { background: #88ff88; }
  </style>
</head>
<body>
<div id="header">
<h1 class="title">P0811R1: Well-behaved interpolation for numbers and pointers</h1>
</div>
<p><em>Audience</em>: LEWG, SG6; WG14<br />S. Davis Herring &lt;<script type="text/javascript">
<!--
h='&#108;&#x61;&#110;&#108;&#46;&#x67;&#x6f;&#118;';a='&#64;';n='&#104;&#x65;&#114;&#114;&#x69;&#110;&#x67;';e=n+a+h;
document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'">'+'<code>'+e+'</code>'+'<\/'+'a'+'>');
// -->
</script><noscript>&#104;&#x65;&#114;&#114;&#x69;&#110;&#x67;&#32;&#x61;&#116;&#32;&#108;&#x61;&#110;&#108;&#32;&#100;&#x6f;&#116;&#32;&#x67;&#x6f;&#118;</noscript>&gt;<br />Los Alamos National Laboratory<br />February 9, 2018</p>
<h1 id="introduction">Introduction</h1>
<p>The simple problem of computing a value between two other values is surprisingly subtle in general. This paper proposes library functions to compute the midpoint between two integer, floating-point, or pointer values, as well as a more general routine that interpolates (or extrapolates) between two floating-point values.</p>
<p>These utilities are a pure library extension. With the exception of the pointer versions, they would be valuable and straightforward to implement in C (perhaps via type-generic macros); it would be appropriate to consult with WG14 about including them in the C standard library.</p>
<h1 id="midpoint">Midpoint</h1>
<h2 id="integers">Integers</h2>
<p>Computing the (integer) midpoint of two integer values via</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">(a+b)/<span class="dv">2</span></code></pre>
<p>can cause overflow for signed or unsigned integers as well as for floating-point values. <a href="https://research.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html">Java’s binary search implementation</a> had this integer overflow bug for nearly a decade, and Mozilla had <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=917841">the same issue</a> in its JavaScript implementation.</p>
<p>The standard alternative</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">a+(b-a)/<span class="dv">2</span></code></pre>
<p>works for unsigned integers (even if <code>b&lt;a</code>). On a typical two’s complement implementation where conversion from unsigned to signed preserves bit patterns, the library can provide the simple implementation</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">Integer midpoint(Integer a, Integer b) {
  <span class="kw">using</span> U = make_unsigned_t&lt;Integer&gt;;
  <span class="kw">return</span> Integer(U(a)+(U(b)-U(a))/<span class="dv">2</span>);
}</code></pre>
<p>that works for signed or unsigned <code>Integer</code>. Note that when <code>b==a+1</code> or <code>b==a-1</code> (without overflow), the result is <em>a</em> because of truncating division. This can be exploited to round half-integers up or down by supplying <code>a&gt;=b</code> or <code>a&lt;=b</code> respectively. (The simple <code>(a+b)/2</code> always truncates half-integers towards 0, yielding <code>min(a,b)</code> when they differ by 1.)</p>
<h2 id="pointers">Pointers</h2>
<p>When a binary search over an array is implemented using pointers, the array size must not exceed <code>PTRDIFF_MAX</code> to avoid undefined behavior ([expr.add]/5). The library can also provide a function template</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp"><span class="kw">template</span>&lt;<span class="kw">class</span> T&gt;
T* midpoint(T *a, T *b);</code></pre>
<p>which is straightforward on common architectures but, it seems, <strong>cannot be implemented</strong> portably and efficiently in C++. As with integers, when the midpoint lies between two pointer values the one closer to <code>a</code> is chosen; for the usual case of <code>a&lt;b</code>, this is compatible with the usual half-open ranges by selecting <code>a</code> when <code>[a,b)</code> is <code>[a,a+1)</code>.</p>
<h2 id="floating-point-types">Floating-point types</h2>
<p>Each of the midpoint formulas above can cause overflow for floating-point types; the latter can also produce results that are not correctly rounded (by rounding in the subtraction and the addition). A third choice</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">a/<span class="dv">2</span>+b/<span class="dv">2</span></code></pre>
<p>prevents overflow but is not correctly rounded for subnormal inputs (due to rounding each separately). The library can easily provide overloads of <code>midpoint</code> for floating-point types that is correctly rounded for all inputs by switching between these strategies:</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">Float midpoint(Float a, Float b)
{<span class="kw">return</span> isnormal(a) &amp;&amp; isnormal(b) ? a/<span class="dv">2</span>+b/<span class="dv">2</span> : (a+b)/<span class="dv">2</span>;}</code></pre>
<h2 id="naming">Naming</h2>
<p>The name <code>mean</code> is superior for the floating-point case, but for the other types (and the common application to binary search) the name <code>midpoint</code> used above is more appropriate. It would be possible to use both names (despite the possible confusion with the use of <code>mean</code> in [rand.dist]), but as it aims to replace the simple expression <code>a+(b-a)/2</code> regardless of type, a single name seems best.</p>
<h1 id="linear-interpolation">Linear interpolation</h1>
<p>Both <a href="https://stackoverflow.com/questions/4353525/floating-point-linear-interpolation">obvious approaches</a> used in published implementations of floating-point linear interpolation have <a href="https://math.stackexchange.com/questions/907327/accurate-floating-point-linear-interpolation">issues</a>:</p>
<ol style="list-style-type: decimal">
<li><code>a+t*(b-a)</code> does not in general reproduce <em>b</em> when <code>t==1</code>, and can overflow if <em>a</em> and <em>b</em> have the largest exponent and opposite signs.</li>
<li><code>t*b+(1-t)*a</code> is not monotonic in general (unless <code>a*b&lt;=0</code>).</li>
</ol>
<p>Lacking an obvious, efficient means of obtaining a correctly rounded overall result, the goal is instead to guarantee the basic properties of</p>
<ol style="list-style-type: decimal">
<li><em>exactness</em>: <code>linear(a,b,0)==a &amp;&amp; linear(a,b,1)==b</code></li>
<li><em>monotonicity</em>: <code>(linear(a,b,t2)-linear(a,b,t1)) * (t2-t1) * (b-a) &gt;= 0</code></li>
<li><em>boundedness</em>: <code>t&lt;0 || t&gt;1 || isfinite(linear(a,b,t))</code></li>
<li><em>consistency</em>: <code>linear(a,a,t)==a</code></li>
</ol>
<p>given that each argument <code>isfinite</code> (for monotonicity, <em>t1</em> and <em>t2</em> may also be infinite if <code>a!=b</code> and <code>t1!=t2</code>). These properties are useful in proofs of correctness of algorithms based on <code>linear</code>. When interpolating, consistency follows from the other properties, but it and monotonicity apply even when extrapolating.</p>
<p>To demonstrate the feasibility of satisfying these properties, a simple implementation that provides all of them is given here:</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">Float linear(Float a, Float b, Float t) {
  <span class="co">// Exact, monotonic, bounded, and (for a=b=0) consistent:</span>
  <span class="kw">if</span>(a*b &lt;= <span class="dv">0</span>) <span class="kw">return</span> t*b + (<span class="dv">1</span>-t)*a;

  <span class="kw">if</span>(t==<span class="dv">1</span>) <span class="kw">return</span> b;                        <span class="co">// exact</span>
  <span class="co">// Exact at t=0, monotonic except near t=1, bounded, and consistent:</span>
  <span class="dt">const</span> Float x = a + t*(b-a);
  <span class="kw">return</span> t&gt;<span class="dv">1</span> == b&gt;a ? max(b,x) : min(b,x);  <span class="co">// monotonic near t=1</span>
}</code></pre>
<p>Putting <code>b</code> first in the <code>min</code>/<code>max</code> prefers it to another equal value (i.e., <code>-0.</code> vs. <code>+0.</code>), which avoids returning <code>-0.</code> for <code>t==1</code> but <code>+0.</code> for other nearby values of <em>t</em>.</p>
<p>Whether it uses this implementation or not, the library can provide a function satisfying these mathematical properties.</p>
<h2 id="naming-1">Naming</h2>
<p>The common (if abstruse) name <code>lerp</code> is avoided because it might suggest a restriction to <em>t</em> on [0,1].</p>
<h1 id="wording">Wording</h1>
<h2 id="midpoint-1">Midpoint</h2>
<p>Add to the end of the synopsis in [numeric.ops.overview]:</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp">  <span class="co">// 29.8.14, least common multiple</span>
  <span class="kw">template</span>&lt;<span class="kw">class</span> M, <span class="kw">class</span> N&gt;
    <span class="kw">constexpr</span> common_type_t&lt;M,N&gt; lcm(M m, N n);</code></pre>
<p><u><code>  </code><code>A midpoint(A a, A b);  // A arithmetic</code><br> <code>  </code><code>template&lt;class T&gt;</code><br> <code>    </code><code>T* midpoint(T* a, T* b);</code></u></p>
<p><code>}</code></p>
<p>Add a new subsection after [numeric.ops.lcm]:</p>
<h3 id="midpoint-2">29.8.15 Midpoint</h3>
<pre class="sourceCode cpp"><code class="sourceCode cpp">A midpoint(A a, A b);</code></pre>
<ol style="list-style-type: decimal">
<li><em>Returns:</em> Half the sum of <code>a</code> and <code>b</code>. No overflow occurs. If <code>A</code> is an integer type and the sum is odd, the result is rounded towards <code>a</code>. If <code>A</code> is a floating-point type, at most one inexact operation occurs.</li>
<li><em>Remarks:</em> An overload exists for each of <code>char</code> and all arithmetic types except <code>bool</code> being <code>A</code>.</li>
</ol>
<pre class="sourceCode cpp"><code class="sourceCode cpp"><span class="kw">template</span>&lt;<span class="kw">class</span> T&gt;
  T* midpoint(T* a, T* b);</code></pre>
<ol style="list-style-type: decimal">
<li><em>Requires:</em> <code>a</code> and <code>b</code> point to, respectively, elements <code>x[</code><em>i</em><code>]</code> and <code>x[</code><em>j</em><code>]</code> of the same array object <code>x</code> [ <em>Footnote:</em> An object that is not an array element is considered to belong to a single-element array for this purpose; see [expr.unary.op]. A pointer past the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n] for this purpose; see [basic.compound]. — <em>end footnote</em> ].</li>
<li><em>Returns:</em> A pointer to <code>x[</code><em>i</em>+(<em>j</em>-<em>i</em>)/2<code>]</code>, where the result of the division is truncated towards zero.</li>
</ol>
<h2 id="linear-interpolation-1">Linear interpolation</h2>
<p>Add to the synopsis in [cmath.syn]:</p>
<pre class="sourceCode cpp"><code class="sourceCode cpp"><span class="dt">long</span> <span class="dt">double</span> fmal(<span class="dt">long</span> <span class="dt">double</span> x, <span class="dt">long</span> <span class="dt">double</span> y, <span class="dt">long</span> <span class="dt">double</span> z);</code></pre>
<p><u><code>// 29.9.4, linear interpolation</code></u><br> <u><code>F linear(F a, F b, F t);   // F floating-point</code></u></p>
<pre class="sourceCode cpp"><code class="sourceCode cpp"><span class="co">// 29.9.4, classification / comparison functions</span></code></pre>
<p>Add a new subsection after [c.math.hypot3]:</p>
<h3 id="linear-interpolation-2">29.9.4 Linear interpolation</h3>
<pre class="sourceCode cpp"><code class="sourceCode cpp">F linear(F a, F b, F t);</code></pre>
<ol style="list-style-type: decimal">
<li><em>Returns:</em> <em>a</em>+<em>t</em>(<em>b</em>-<em>a</em>). If each argument <code>isfinite</code>, the result satisfies these conditions:
<ol style="list-style-type: decimal">
<li><code>t&lt;0 || t&gt;1 || isfinite(linear(a,b,t))</code></li>
<li><code>linear(a,b,0)==a &amp;&amp; linear(a,b,1)==b</code></li>
<li><code>linear(a,a,t)==a</code></li>
<li><code>(linear(a,b,t2)-linear(a,b,t1)) * (t2-t1) * (b-a) &gt;= 0</code></li>
</ol>
The last condition applies additionally if <code>!isnan(t1) &amp;&amp; !isnan(t2) &amp;&amp; t1!=t2 &amp;&amp; a!=b</code>.</li>
<li><em>Remarks:</em> An overload exists for each floating-point type being <code>F</code>.</li>
</ol>
</body>
</html>
