4

Boost::multiprecison を使用してエラーなしでこの単純なコード行を実行する方法を知っていますか?

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

MSVC を使用すると、「sqrt」にエラーが発生し、次の方法で修正できます。

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

次に、「ceil」にエラーがあり、次の方法で修正できます。

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));

すると「ジェネリック相互変換」のエラーが!?!

このように単純なコード行を実現するには、もっと洗練された方法があるはずだと思いませんか? それについて何かアイデアがあれば教えてください。

よろしく。

4

1 に答える 1

6

「問題」(実際には機能です)は、number<>テンプレート式を有効にしてフロントエンドを使用していることです。

これは、コンパイラによってコードが生成される前に、多くの操作を大幅に最適化したり、削除したりすることができることを意味します。

次の 2 つのオプションがあります。

  1. 物事を分解する

    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;
    

    だからあなたは書くことができます

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

    これは、式テンプレートの評価を強制することで機能します (および を使用した float -> integer から潜在的に損失のある変換convert_to<>)。

  2. 一般に、型の非式テンプレート バージョンに切り替えることができます。

    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>;
    

    この特定のケースでは、integer -> float -> integer から型の「強制」を行う必要があるため、あまり変わりません。

    v += 6 * ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    
  3. 単純化することで、代わりにすべての型を float にすると (cpp_dec_float など)、これらの複雑なアーティファクトを取り除くことができます。

    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);
    

    警告プロファイラーを使用して、使用et_offがコードベースでパフォーマンスの問題を引き起こさないことを確認してください

以下は、3 つのアプローチすべてを示すデモ プログラムです。

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);
    }
}
于 2015-04-05T16:40:19.953 に答える