Boost logo

Boost :

Subject: Re: [boost] Looking for some "real world" extended precision integer arithmetic tests
From: John Maddock (boost.regex_at_[hidden])
Date: 2012-01-25 05:33:38


> I was almost done implementing benchmark with fixed_int and voronoi
> library
> when next problem appeared.
> Let's consider following code snippet:
>
> #include <iostream>
> #include "boost/multiprecision/fixed_int.hpp"
> using namespace boost::multiprecision;
>
> template <typename T>
> void foo(const T& that) {
> std::cout << "Using default foo." << std::endl;
> }
>
> template <>
> void foo<mp_int512_t>(const mp_int512_t& that) {
> std::cout << "Using fixed_int specialization." << std::endl;
> }
>
> int main() {
> mp_int512_t a = 1;
> mp_int512_t b = 2;
> foo(a); // outputs 'Using fixed_int specialization.'
> foo(a * b); // outputs 'Using default foo.'
> return 0;
> }
>
> I understand the reason of such behaviour (results of the expressions are
> stored as some intermediate objects), but in my implementation I need to
> specify template function specialization that converts fixed_int to
> double. Any suggestions on how to fix this?

Nod. It is a problem, and it's highlighted in the docs. BTW does your code
work with mpz_class? If so it should be expression template safe
already.... but leaving that aside, your options are:

1) Overload for the expression template type - but that's an implementation
detail, and may result in a lot of unnecessary overloads.
2) Whenever you call a function template with an expression as argument,
cast the result of the expression to the number type, so for example:

foo(static_cast<number_type>(a+b));

The static_cast should be a no-op if the result of a+b is already that type,
otherwise the expression template gets converted to it's actual number.

3) As above but provide the template argument explicitly:

foo<number_type>(a+b);

4) For public API's it may be convenient to provide some traits classes and
write:

template <class A1, class A2, class A3>
typename result_type<A1, A2, A3>::type foo(const A1& a1, const A2& a2, const
A3& a3)
{
   return foo_imp<typename calculation_type<A1, A2, A3>::type>(a1, a2, a3);
}

Then if the user calls

foo(unsigned, long long, short);

The arguments will get correctly promoted internally and return an
appropriately sized result.

The traits result_type and calculation_type might be the same type of
course, and could probably just be wrappers around boost::common_type from
type_traits (or maybe even *be* that type??).

For what it's worth, Boost.Math does (2) for internal calls, and (4) for
public API's, of course with floats you don't have to worry about the whole
signed/unsigned business which may scupper (4) somewhat.

Apologies for the long answer!

HTH, John.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk