Как использовать sqrt и ceil с Boost :: multiprecision?

Знаете ли вы, как сделать эту простую строку кода без ошибок, используя 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));

Тогда возникает ошибка «общего взаимопревращения»!?!

Я думаю, что должен быть более элегантный способ реализовать такую ​​простую строку кода, не так ли?
Дайте мне знать, если у вас есть идеи по этому поводу, пожалуйста.

С уважением.

3

Решение

«Проблема» (это на самом деле особенность) вы используете number<> интерфейс с включенными шаблонными выражениями.

Это означает, что многие операции могут быть значительно оптимизированы или даже исключены до того, как компилятор сгенерирует код.

У вас есть два варианта:

  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. Упрощая, если вы вместо этого сделаете все типы плавающими (например, 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 не вызывает проблем с производительностью в вашей базе кода

Вот демонстрационная программа, показывающая все три подхода:

Жить на Колиру

#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);
}
}
4

Другие решения

использование boost::multiprecision::sqrt а также boost::multiprecision::ceil,

0

По вопросам рекламы [email protected]