Boost C++ Libraries: Ticket #13503: Boost.Multiprecision generates incorrect code https://svn.boost.org/trac10/ticket/13503 <p> Comparing two identical functions, one using <code>boost::multiprecision::uint128_t</code> and the other using GCC/Clang builtin <code>unsigned __int128</code>: </p> <pre class="wiki">auto foo(std::uint64_t lhs, std::uint64_t rhs) -&gt; std::uint64_t { boost::multiprecision::uint128_t a(lhs); a *= rhs; return static_cast&lt;std::uint64_t&gt;(a &gt;&gt; 64) + static_cast&lt;std::uint64_t&gt;(a); } auto bar(std::uint64_t lhs, std::uint64_t rhs) -&gt; std::uint64_t { unsingned __int128 a(lhs); a *= rhs; return static_cast&lt;std::uint64_t&gt;(a &gt;&gt; 64) + static_cast&lt;std::uint64_t&gt;(a); } </pre><p> Comparing the outputs of each function, they return different results for <code>(-1ULL, -1ULL)</code>, <code>(-1ULL, 2)</code>, <code>(-1ULL, 3)</code>, and presumably others. (<code>foo</code> returns <code>18446744073709551613</code>, <code>0</code>, and <code>1</code>, respectively). </p> <p> Comparing the results to Python's big int type (the builtin <code>int</code>), it seems that the <code>foo</code> results are not correct and the <code>bar</code> results are (this can also be confirmed by hand). This is also confirmed by looking at the disassembly for the each function: </p> <pre class="wiki">0000000000000000 &lt;foo(unsigned long, unsigned long)&gt;: 0: 48 89 f0 mov %rsi,%rax 3: 48 f7 e7 mul %rdi 6: 48 83 f8 ff cmp $0xffffffffffffffff,%rax a: 48 89 d1 mov %rdx,%rcx d: 48 83 d9 00 sbb $0x0,%rcx 11: 48 c7 c1 ff ff ff ff mov $0xffffffffffffffff,%rcx 18: 48 0f 43 c1 cmovae %rcx,%rax 1c: 48 01 d0 add %rdx,%rax 1f: c3 retq 0000000000000020 &lt;bar(unsigned long, unsigned long)&gt;: 20: 48 89 f0 mov %rsi,%rax 23: 48 f7 e7 mul %rdi 26: 48 01 d0 add %rdx,%rax 29: c3 retq </pre><p> These results were consistent when compiling with clang++ (5.0.0) and g++ (6.3.1) and when compiling with optimization enabled and disabled. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/13503 Trac 1.4.3 John Maddock Fri, 30 Mar 2018 16:25:28 GMT <link>https://svn.boost.org/trac10/ticket/13503#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/13503#comment:1</guid> <description> <p> This was deliberate, but on reflection is a mistake. </p> <p> The issue is in the narrowing conversion from 128 to 64 bit integer - you are relying on this being truncation, but there's nothing in the standard to specify or guarantee that - instead it's implementation defined behaviour and is permitted to vary by compiler and/or platform. Boost.Multiprecision's default interconversion functions convert a value that would overflow the destination type to the maximum value that type can hold. This part is the mistake for unsigned integers (and only for unsigned integers) which should probably follow the common practice of truncation. </p> </description> <category>Ticket</category> </item> <item> <author>Sam Lunt <samuel.j.lunt@…></author> <pubDate>Sat, 31 Mar 2018 21:57:48 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/13503#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/13503#comment:2</guid> <description> <p> Looking at the standard, it seems that conversion to signed type is implementation defined, but for conversion to unsigned, truncation is mandated. </p> <p> This is from section 4.7.2 and 4.7.3 [conv.integral]: </p> <ol start="2"><li>If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2<sup>n</sup> where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). — end note] </li></ol><ol start="3"><li>If the destination type is signed, the value is unchanged if it can be represented in the destination type; otherwise, the value is implementation-defined. </li></ol><p> Is it possible to change the behavior to match the standard? I can image there could be some concern about a behavior change, but I'd wonder how many people are depending on this behavior and how many expect truncation but get saturation. </p> </description> <category>Ticket</category> </item> <item> <author>Данил Ильиных <woodroof@…></author> <pubDate>Sun, 06 May 2018 05:47:24 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/13503#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/13503#comment:3</guid> <description> <p> Discovered the same bug :( Had to change code to static_cast&lt;uint64_t&gt;(v &amp; 0xfffffffffffffffflu). </p> </description> <category>Ticket</category> </item> <item> <dc:creator>anonymous</dc:creator> <pubDate>Thu, 17 May 2018 07:39:00 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/13503#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/13503#comment:4</guid> <description> <p> Fixed in develop for all conversions of (signed or unsigned) integers to unsigned. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Tue, 22 May 2018 17:01:54 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/13503#comment:5 https://svn.boost.org/trac10/ticket/13503#comment:5 <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> </ul> Ticket