[multiprecision] Design question on variable precision types.
Folks, I have a tricky design question that Boost.Multiprecision has been dodging relating to variable precision types - these have just expanded somewhat now that we support complex numbers via mpc as well as mpfr's reals etc. The basic use case is this: We have a set of numbers at very high precision, some calculations are performed on those directly, others we want to temporarily drop precision, get an approximate answer and perhaps later refine it. Generally speaking, performance trumps everything, on a really long running calculation 10-20% speedup can actually make a big difference (in $$'s in some cases if the CPU time is from the cloud). Up till now Boost.Multiprecision has been dodging this issue - it's actually pretty hard to program in a sane and consistent way, so the message has been "don't mix precisions" when using a type like boost::multiprecision::mpfr_float - just set what default you want and get on with it. But we really should do better than that. So... with the help of Danielle Brake I've been trying to nail this down in develop, what we have at present is: * Copy construction or move assignment copies the precision of the source. * Regular assignment retains the precision of the target variable. This is efficient in that assignment doesn't reallocate to a new precision, and provides an easy way to up/down sample precision when required. But it breaks equality after assignment. It does match what MPC/MPFR do in mpfr_set etc though. The fact that move assignment behaves differently from regular assignment bother me too - it's too easy to write code that triggers move semantics without noticing. The trouble is, being able to assign while retaining precision of source is sometimes useful too, so having move-assign behave differently provides a useful back door, but maybe one that's just too cute for it's own good? And then.... we get on to arithmetic. * Currently temporaries are created at current default precision. So it's easy to shoot yourself in the foot by not noticing that temporaries are being created in an expression and not having the default precision set correctly. I actually think this one might be solvable, but it's a huge job involving banning default constructors in all the backends to our number types, and then catching all the cases where they might have been used. * What should happen in "a = expression", where the variable used in "expression" are at different precision to a? The uncontroversial answer might be to adjust the result to the precision of a. But hold on... we have expression templates, and these can use variable a as working space while evaluating the expression... so the change in precision might occur *during* the computation and not after it. * Should we try to ban mixed precision arithmetic altogether? It's a tempting idea, but I can't see how to enforce it in code without also banning mixed precision assignment. Thanks for your consideration, John. --- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus
AMDG On 08/22/2018 12:28 PM, John Maddock via Boost wrote:
I have a tricky design question that Boost.Multiprecision has been dodging relating to variable precision types - these have just expanded somewhat now that we support complex numbers via mpc as well as mpfr's reals etc.
The basic use case is this:
We have a set of numbers at very high precision, some calculations are performed on those directly, others we want to temporarily drop precision, get an approximate answer and perhaps later refine it. Generally speaking, performance trumps everything, on a really long running calculation 10-20% speedup can actually make a big difference (in $$'s in some cases if the CPU time is from the cloud).
Up till now Boost.Multiprecision has been dodging this issue - it's actually pretty hard to program in a sane and consistent way, so the message has been "don't mix precisions" when using a type like boost::multiprecision::mpfr_float - just set what default you want and get on with it. But we really should do better than that.
So... with the help of Danielle Brake I've been trying to nail this down in develop, what we have at present is:
<snip>
How about something like this: - Assignment always preserves the precision of the source. - Mixed precision arithmetic uses the maximum precision of any argument. - There is an explicit function for setting the precision. - There is an way to force assignment to use the precision of the destination, but it must be explicit in some way. I believe these are the correct semantics, without regard for performance. So, to get performance back: - When using expression templates and the precision of the result is set, the precision of intermediate results may be reduced as long as it does not change the final result too much. (The definition of "too much" is left as an exercise for the reader). In Christ, Steven Watanabe
participants (2)
-
John Maddock
-
Steven Watanabe