Как использовать 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));

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

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

С уважением.


person Aurélien Barbier-Accary    schedule 05.04.2015    source источник


Ответы (1)


«Проблема» (на самом деле это функция) заключается в том, что вы используете внешний интерфейс 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>();
    

    Который работает, принудительно оценивая шаблоны выражений (и, возможно, преобразование с потерями из числа с плавающей запятой -> целое число с использованием 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>;
    

    В этом конкретном случае это мало что меняет, потому что вам все еще нужно выполнять «приведение» типов из целого числа -> число с плавающей запятой -> целое число:

    v += 6 * ceil((sqrt(BF(uMax * uMax - candidate)) - BF(v)) / 6).convert_to<BI>();
    
  3. #P9#
    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);
    
    <цитата> #P10#

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

Жить на 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);
    }
}
person sehe    schedule 05.04.2015
comment
Большое спасибо за такой подробный ответ :-)! Я попробую это в ближайшее время. - person Aurélien Barbier-Accary; 05.04.2015
comment
Это нормально для ошибок компиляции :-) но я не понимаю, почему ceil(BF(18)/6) это 3, а ceil(BF(18)/BF(6)) это 4. Не могли бы вы помочь мне снова? - person Aurélien Barbier-Accary; 05.04.2015
comment
typedef bmp::number<bmp::cpp_dec_float<0>, bmp::et_off> BF; но с typedef bmp::number<bmp::cpp_bin_float<100>, bmp::et_off> BF; все в порядке. Где я могу найти больше объяснений о том, как выбрать лучший тип в зависимости от моих целей? Boost 1.57 doc мне не очень понятно - person Aurélien Barbier-Accary; 06.04.2015
comment
Моя цель - определить что-то однородное между целыми числами и числами с плавающей запятой. В настоящее время я использую namespace bmp = boost::multiprecision; и typedef bmp::number<bmp::cpp_int_backend<>, bmp::et_off> int_bmp; // arbitrary precision integer и typedef bmp::number<bmp::cpp_bin_float<100>, bmp::et_off> float_bmp; // arbitrary precision float number, но я думаю, что тип float_bmp не самый лучший... - person Aurélien Barbier-Accary; 06.04.2015
comment
Просто используйте number<> с et on. Это однородно. Он просто перестает быть таким (потенциально) преобразованием с потерями, потому что на самом деле это делает разные вещи. Вы всегда можете написать .convert_to<> в своем коде, чтобы обойти это. - person sehe; 06.04.2015
comment
Что касается разных результатов (3 против 4), это похоже на ошибку, ИМО: см. ="nofollow noreferrer">эта сравнительная матрица, которую я составил, показывает разные бэкенды, компиляторы и режимы шаблонов выражений. cpp_dec_float — единственный тип, который дает неверные результаты (за исключением случаев, когда ET равно off и ceil(mp_t(18)/6) оценивается, но только на компиляторе GNU c++) - person sehe; 06.04.2015
comment
Большое спасибо, Сехе, очень приятно получать такие полезные ответы! - person Aurélien Barbier-Accary; 06.04.2015
comment
Я зарегистрирую ошибку, если не найду причину сам в ближайшее время. Я все равно не пробовал ветку разработки Boost Multiprecision. - person sehe; 06.04.2015