Boost C++ Libraries: Ticket #12327: cpp_rational::convert_to<double>() does not return the nearest number
https://svn.boost.org/trac10/ticket/12327
<p>
Hello,
</p>
<p>
I tested with the following program to see if boost::multiprecision::cpp_rational::convert_to<double>() returns the nearest double number to the exact value. I expected it to print only "true"s, but it actually prints some "false"s too. Is it a bug or (un)documented behavior?
</p>
<div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf"><boost/multiprecision/cpp_int.hpp></span><span class="cp"></span>
<span class="k">typedef</span> <span class="n">boost</span><span class="o">::</span><span class="n">multiprecision</span><span class="o">::</span><span class="n">cpp_rational</span> <span class="n">NT</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">test_convert_to_double</span><span class="p">(</span><span class="kt">double</span> <span class="n">d1</span><span class="p">,</span> <span class="kt">double</span> <span class="n">d2</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">NT</span> <span class="n">r1</span><span class="p">(</span><span class="n">d1</span><span class="p">);</span>
<span class="n">NT</span> <span class="n">r2</span><span class="p">(</span><span class="n">d2</span><span class="p">);</span>
<span class="n">NT</span> <span class="n">two</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
<span class="n">NT</span> <span class="n">three</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="n">NT</span> <span class="n">r112</span> <span class="o">=</span> <span class="p">(</span><span class="n">two</span> <span class="o">*</span> <span class="n">r1</span> <span class="o">+</span> <span class="n">r2</span><span class="p">)</span> <span class="o">/</span> <span class="n">three</span><span class="p">;</span>
<span class="n">NT</span> <span class="n">r12</span> <span class="o">=</span> <span class="p">(</span><span class="n">r1</span> <span class="o">+</span> <span class="n">r2</span><span class="p">)</span> <span class="o">/</span> <span class="n">two</span><span class="p">;</span>
<span class="n">NT</span> <span class="n">r122</span> <span class="o">=</span> <span class="p">(</span><span class="n">r1</span> <span class="o">+</span> <span class="n">two</span> <span class="o">*</span> <span class="n">r2</span><span class="p">)</span> <span class="o">/</span> <span class="n">three</span><span class="p">;</span>
<span class="kt">double</span> <span class="n">rd1</span> <span class="o">=</span> <span class="n">r1</span><span class="p">.</span><span class="n">convert_to</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">();</span>
<span class="kt">double</span> <span class="n">rd112</span> <span class="o">=</span> <span class="n">r112</span><span class="p">.</span><span class="n">convert_to</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">();</span>
<span class="kt">double</span> <span class="n">rd12</span> <span class="o">=</span> <span class="n">r12</span><span class="p">.</span><span class="n">convert_to</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">();</span>
<span class="kt">double</span> <span class="n">rd122</span> <span class="o">=</span> <span class="n">r122</span><span class="p">.</span><span class="n">convert_to</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">();</span>
<span class="kt">double</span> <span class="n">rd2</span> <span class="o">=</span> <span class="n">r2</span><span class="p">.</span><span class="n">convert_to</span><span class="o"><</span><span class="kt">double</span><span class="o">></span><span class="p">();</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">boolalpha</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="n">rd1</span> <span class="o">==</span> <span class="n">d1</span><span class="p">)</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="n">rd112</span> <span class="o">==</span> <span class="n">d1</span><span class="p">)</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="n">rd12</span> <span class="o">==</span> <span class="n">d2</span><span class="p">)</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="n">rd122</span> <span class="o">==</span> <span class="n">d2</span><span class="p">)</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="n">rd2</span> <span class="o">==</span> <span class="n">d2</span><span class="p">)</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">volatile</span> <span class="kt">double</span> <span class="n">a</span> <span class="o">=</span> <span class="mf">0.099999999999999992</span><span class="p">;</span>
<span class="k">volatile</span> <span class="kt">double</span> <span class="n">b</span> <span class="o">=</span> <span class="mf">0.10000000000000001</span><span class="p">;</span>
<span class="k">volatile</span> <span class="kt">double</span> <span class="n">c</span> <span class="o">=</span> <span class="mf">0.10000000000000002</span><span class="p">;</span>
<span class="c1">// prints true, false, true, true, true</span>
<span class="n">test_convert_to_double</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
<span class="c1">// prints true, true, false, false, true</span>
<span class="n">test_convert_to_double</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div></div><p>
I compiled this program with MSVC 14.0 and ICL 16.0.3, both for x64 Debug build. They give the same results.
</p>
en-us
Boost C++ Libraries
/htdocs/site/boost.png
https://svn.boost.org/trac10/ticket/12327
Trac 1.4.3
-
John Maddock
Thu, 14 Jul 2016 17:00:01 GMT
<link>https://svn.boost.org/trac10/ticket/12327#comment:1 </link>
<guid isPermaLink="false">https://svn.boost.org/trac10/ticket/12327#comment:1</guid>
<description>
<p>
I believe it's an error in your test program, taking the first example:
</p>
<pre class="wiki">0.099999999999999992 = 7205759403792793/72057594037927936
0.10000000000000001 = 3602879701896397/36028797018963968
</pre><p>
Note that these fractions are exact (in the binary, not decimal sense).
</p>
<p>
Then <a class="changeset" href="https://svn.boost.org/trac10/changeset/112" title="Fix link to test site.
">r112</a> becomes 5404319552844595/54043195528445952 which is 0.0999999999999999962992565845828115319212277730305989583... in decimal, which rounds up to 0.10000000000000001 and not down to 0.099999999999999992 due to the "6" after the string of 9's.
</p>
</description>
<category>Ticket</category>
</item>
<item>
<author>komakisen@…</author>
<pubDate>Fri, 15 Jul 2016 04:46:21 GMT</pubDate>
<title/>
<link>https://svn.boost.org/trac10/ticket/12327#comment:2 </link>
<guid isPermaLink="false">https://svn.boost.org/trac10/ticket/12327#comment:2</guid>
<description>
<p>
Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/12327#comment:1" title="Comment 1">johnmaddock</a>:
</p>
<blockquote class="citation">
<p>
Then <a class="changeset" href="https://svn.boost.org/trac10/changeset/112" title="Fix link to test site.
">r112</a> becomes 5404319552844595/54043195528445952 which is 0.0999999999999999962992565845828115319212277730305989583... in decimal, which rounds up to 0.10000000000000001 and not down to 0.099999999999999992 due to the "6" after the string of 9's.
</p>
</blockquote>
<p>
Thank you for investigating my program, but I still don't understand this point.
</p>
<p>
To begin with, 0.099999999999999992 is actually 0.09999999999999999167332... and 0.10000000000000001 is actually 0.1000000000000000055511... (they differ by 1ulp)
</p>
<p>
So 0.09999999999999999629925... is less than their midpoint 0.09999999999999999861222...
</p>
<p>
Also, <code>double</code> is a binary floating-point number. Thus I believe round-off calculation on a decimal digit "6" explains nothing here.
</p>
<p>
If I run the following program, I get 0.099999999999999992.
</p>
<div class="wiki-code"><div class="code"><pre><span class="cp">#include</span> <span class="cpf"><iostream></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><iomanip></span><span class="cp"></span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">double</span> <span class="n">d</span> <span class="o">=</span> <span class="mf">0.09999999999999999629926</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">setprecision</span><span class="p">(</span><span class="mi">17</span><span class="p">)</span> <span class="o"><<</span> <span class="n">d</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div></div><p>
So isn't 0.099999999999999992 the expected result? Or am I confusing myself?
</p>
</description>
<category>Ticket</category>
</item>
<item>
<dc:creator>John Maddock</dc:creator>
<pubDate>Mon, 25 Jul 2016 18:17:56 GMT</pubDate>
<title>status, milestone changed; resolution set
https://svn.boost.org/trac10/ticket/12327#comment:3
https://svn.boost.org/trac10/ticket/12327#comment:3
<ul>
<li><strong>status</strong>
<span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span>
</li>
<li><strong>resolution</strong>
→ <span class="trac-field-new">fixed</span>
</li>
<li><strong>milestone</strong>
<span class="trac-field-old">To Be Determined</span> → <span class="trac-field-new">Boost 1.62.0</span>
</li>
</ul>
<p>
You're correct, I was the one confused!
</p>
<p>
Fixed in <a class="ext-link" href="https://github.com/boostorg/multiprecision/commit/7ebd9dfd9b0bc463642b065036a3cd6947a74431"><span class="icon"></span>https://github.com/boostorg/multiprecision/commit/7ebd9dfd9b0bc463642b065036a3cd6947a74431</a>.
</p>
<p>
However, your second test case still fails testing rd12, in this case I *think* the bug is yours: <a class="changeset" href="https://svn.boost.org/trac10/changeset/12" title="More things getting included from common.
">r12</a> is
</p>
<p>
<code>14411518807585589/144115188075855872</code>
</p>
<blockquote>
<p>
which in binary is exactly the 54 bit quantity:
</p>
</blockquote>
<pre class="wiki">0.000 1100110011 0011001100 1100110011 0011001100 1100110011 010 1
rounded here ^
</pre><p>
And in this case no rounding up occurs since we have a tie and round to even.
</p>
<p>
Please do correct me and re-open if I'm wrong! :)
</p>
Ticket
-
komakisen@…
Tue, 26 Jul 2016 14:41:34 GMT
<link>https://svn.boost.org/trac10/ticket/12327#comment:4 </link>
<guid isPermaLink="false">https://svn.boost.org/trac10/ticket/12327#comment:4</guid>
<description>
<p>
Great work! Thank you very much! :)
</p>
<blockquote class="citation">
<p>
And in this case no rounding up occurs since we have a tie and round to even.
</p>
</blockquote>
<p>
Yes, you're right! Sorry for messing up.
</p>
</description>
<category>Ticket</category>
</item>
</channel>
</rss>