6

Do you know how to do this simple line of code without error using Boost::multiprecison ?

boost::multiprecision::cpp_int v, uMax, candidate;
//...
v += 6 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);

Using MSVC there is an error for "sqrt" and it's possible to fix it with:

v += 6 * ceil((sqrt(static_cast<boost::multiprecision::cpp_int>(uMax * uMax - candidate)) - v) / 6);

Then there is an error for "ceil" and it's possible to fix it with:

namespace bmp = boost::multiprecision;
typedef bmp::number<bmp::cpp_dec_float<0>> float_bmp;
v += 6 * ceil(static_cast<float_bmp>((sqrt(static_cast<bmp::cpp_int>(uMax * uMax - candidate)) - v) / 6));

Then there is an error of "generic interconvertion" !?!

I think there should be a more elegant way to realize a so simple line of code, isn't it? Let me know if you have some ideas about it please.

Regards.

1 Answers1

6

The "problem" (it's actually a feature) is that you are using the number<> frontend with template expressions enabled.

This means that many operations can be greatly optimized or even eliminated before code is generated by the compiler.

You have two options:

  1. break things down

    using BF = boost::multiprecision::cpp_bin_float_100;
    using BI = boost::multiprecision::cpp_int;
    BI v = 1, uMax = 9, candidate = 1;
    
    //v += 6 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);
    BF tmp1(uMax * uMax - candidate);
    BF tmp2(sqrt(tmp1) - BF(v));
    BF tmp3(ceil(tmp2 / 6));
    BI tmp4(tmp3.convert_to<BI>());
    std::cout << tmp1 << " " << tmp2 << " " << tmp3 << " " << tmp4 << "\n";
    
    v = v + 6*tmp4;
    

    So you could write

    v += 6*ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    

    Which works by forcing evaluation of expression templates (and potentially lossy conversion from float -> integer using convert_to<>).

  2. In general you could switch to non-expression-template versions of the types:

    using BF = mp::number<mp::cpp_bin_float_100::backend_type, mp::et_off>;
    using BI = mp::number<mp::cpp_int::backend_type, mp::et_off>;
    

    In this particular case it doesn't change much because you still have to do type "coercions" from integer -> float -> integer:

    v += 6 * ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    
  3. By simplifying, if you make all types float instead (e.g. cpp_dec_float) you can get rid of these complicating artefacts:

    using BF = mp::number<mp::cpp_dec_float_100::backend_type, mp::et_off>;
    BF v = 1, uMax = 9, candidate = 1;
    
    v += 6 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);
    

    CAVEAT Use your profiler to see that using et_off doesn't cause a performance problem on your code-base

Here's a demo program showing all three approaches:

Live On Coliru

#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/number.hpp>

int main() {
    namespace mp = boost::multiprecision;
    //v += 6 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);
    {
        using BF = mp::cpp_bin_float_100;
        using BI = mp::cpp_int;
        BI v = 1, uMax = 9, candidate = 1;

#ifdef DEBUG
        BF tmp1(uMax * uMax - candidate);
        BF tmp2(sqrt(BF(uMax * uMax - candidate)) - BF(v));
        BF tmp3(ceil(tmp2 / 6));
        BI tmp4(tmp3.convert_to<BI>());
        std::cout << tmp1 << " " << tmp2 << " " << tmp3 << " " << tmp4 << "\n";
#endif

        v += 6*ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    }

    {
        using BF = mp::number<mp::cpp_bin_float_100::backend_type, mp::et_off>;
        using BI = mp::number<mp::cpp_int::backend_type, mp::et_off>;
        BI v = 1, uMax = 9, candidate = 1;

        v += 6 * ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    }

    {
        using BF = mp::number<mp::cpp_dec_float_100::backend_type, mp::et_off>;
        BF v = 1, uMax = 9, candidate = 1;

        v += 6 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);
    }
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • It's ok for the compilation errors :-) but I don't understand why `ceil(BF(18)/6)` is `3` and `ceil(BF(18)/BF(6))` is `4`. Could you help me again? – Aurélien Barbier-Accary Apr 05 '15 at 20:40
  • `typedef bmp::number, bmp::et_off> BF;` but it's ok with `typedef bmp::number, bmp::et_off> BF;`. Where could I find more explanations about how to choose the best type depending on my goals? [Boost 1.57 doc](http://www.boost.org/doc/libs/1_57_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/cpp_dec_float.html) is not very clear for me – Aurélien Barbier-Accary Apr 05 '15 at 21:02
  • My goal is to define something homogenous between integers and floats. Currently I'm using `namespace bmp = boost::multiprecision;` and `typedef bmp::number, bmp::et_off> int_bmp; // arbitrary precision integer` and `typedef bmp::number, bmp::et_off> float_bmp; // arbitrary precision float number` but I think that the `float_bmp` type is not the best one... – Aurélien Barbier-Accary Apr 05 '15 at 21:24
  • Just use `number<>` with et on. It's homogeneous. It just stops being so on (potentially) lossy conversions, because that's actually doing different things. You can always write `.convert_to<>` in your code to get around this. – sehe Apr 05 '15 at 22:03
  • Regarding the different outcomes (3 vs 4) this appears to be a bug, IMO: see **[this comparison matrix](https://docs.google.com/spreadsheets/d/1f9yNLV1Vn5QaoKIF1GF1FRDUSdJgGWXNJwLieDiu87Q/edit?usp=sharing)** I made which shows different backends, compilers and expression-template modes. `cpp_dec_float` is the only type that gets wrong results (except when ET of _off_ and `ceil(mp_t(18)/6)` is evaluated, but **only** on GNU c++ compiler) – sehe Apr 05 '15 at 22:04
  • Thank you very much Sehe, it's a pleasure to have so helpful replies! – Aurélien Barbier-Accary Apr 05 '15 at 22:15
  • I'll file the bug unless I find the cause myself soon. I haven't tried development branch of Boost Multiprecision anyways – sehe Apr 05 '15 at 22:15