模板和划分

Templates and division

我在 C++ 中使用模板化 class,我计划确保与双精度和 mpfr 浮点数的兼容性。程序中发生的唯一除法是除以 2。除以 2 的双精度和 mpfr 浮点数的行为应该不同,因为在 mpfr 中,我可以直接访问指数。

问题:您有什么建议可以产生最高效的编译代码?

我会检查 number<mpfr_floatXXX> 是否尚未检测到优化。

Boost's mpfr wrapper does not seem useful because it doesn't seem to use the mpfr_div_2ui command and would, instead, divide by the mpfr float with a value of 2. I expect this to be slower than directly changing the exponent.

这种期望是没有根据的。只需检查:

#include <boost/multiprecision/mpfr.hpp>

int main() {
    using namespace boost::multiprecision;

    mpfr_float_50 n ("787878787878");
    n /= 2;
}

编译成

mov rax, QWORD PTR fs:40
mov QWORD PTR [rsp+232], rax
xor eax, eax
lea rdi, [rsp+16]
call    mpfr_init2
cmp QWORD PTR [rsp+40], 0
xor ecx, ecx
mov edx, 10
lea rdi, [rsp+16]
call    mpfr_set_str
test    eax, eax
cmp QWORD PTR [rsp+40], 0
lea rsi, [rsp+16]
xor ecx, ecx
mov edx, 2
mov rdi, rsi
call    mpfr_div_ui

所以,它并没有你想象的那么糟糕。

实施

这是我的非通用实现:

mp::mpfr_float_50 div_2ui(mp::mpfr_float_50 const& f, unsigned i) {
    mp::mpfr_float_50 r;
    ::mpfr_div_2ui(
            r.backend().data(), 
            f.backend().data(),
            i,
            MPFR_RNDN);

    return r;
}

通用实现如下所示:

template <typename T, typename Enable = void> struct is_mpfr : boost::mpl::false_ {};

template <unsigned digits10, mp::mpfr_allocation_type AllocationType, mp::expression_template_option ET>
struct is_mpfr<
        mp::number<mp::mpfr_float_backend<digits10, AllocationType>, ET >
    > : boost::mpl::true_ 
{};

template <typename T>
T div_2ui_impl(T f, unsigned i, boost::mpl::false_) {
    while (i--)
        f /= 2;
    return f;
}

template <typename Mpfr>
Mpfr div_2ui_impl(Mpfr f, unsigned i, boost::mpl::true_) {
    std::cout << "-- optimized --";
    Mpfr r;
    ::mpfr_div_2ui(r.backend().data(), f.backend().data(), i, MPFR_RNDN);
    return r;
}

template <typename T>
T div_2ui(T const &f, unsigned i) {
    return div_2ui_impl(f, i, is_mpfr<T> { });
}

现场演示

Live On Coliru

template <typename T>
void test() {
    T n("787878787878");
    n = arith::div_2ui(n, 1);
    std::cout << __FUNCTION__ << ": " << n << "\n";
}

int main() {
    std::cout << std::fixed;

    test<mp::mpfr_float_50>();
    test<mp::mpfr_float_100>();
    test<mp::cpp_int>();
    test<mp::cpp_dec_float_100>();
    test<mp::number<mp::gmp_int> >();
    test<mp::mpf_float_1000>();
}

版画

-- optimized --test: 393939393939.000000
-- optimized --test: 393939393939.000000
test: 393939393939
test: 393939393939.000000
test: 393939393939
test: 393939393939.000000