问题
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 * ceil((sqrt(uMax * uMax - candidate) - v) / 6);
Using MSVC there is an error for "sqrt" and it's possible to fix it with:
v += 6 * ceil((sqrt(static_cast<boost::multiprecision::cpp_int>(uMax * uMax - candidate)) - v) / 6);
Then there is an error for "ceil" and it's possible to fix it with:
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));
Then there is an error of "generic interconvertion" !?!
I think there should be a more elegant way to realize a so simple line of code, isn't it? Let me know if you have some ideas about it please.
Regards.
回答1:
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:
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<>
).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>();
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);
}
}
来源:https://stackoverflow.com/questions/29457795/how-to-use-sqrt-and-ceil-with-boostmultiprecision