How to use sqrt and ceil with Boost::multiprecision?

前端 未结 1 698
一向
一向 2021-01-20 01:36

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 * c         


        
相关标签:
1条回答
  • 2021-01-20 01:49

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题