Boost logo

Boost :

From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2001-09-28 09:40:08


----- Original Message -----
From: Eric Ford <eford_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Friday, September 28, 2001 3:25 AM
Subject: [boost] Re: Math Functions: Basic building blocks...

> > I'm not sure if it is a good idea to leave the default
> implementations
> > empty.
> >
> > If I want to use a given UDNT (user defined number type), then the
> first
> > thing I have to do is to specialize all those traits and functions
> for which
> > I can give a meaningful specialization.
> > Now, suppose that my BigFloat doesn't support, say, 'cos()'; but I
> need to
> > use it in some algorithm and I get the compiler error that cos()
> isn't
> > specialized for BigFloat.
> > I have two choices: forget about using BigFloat or write myself (the
> > BigFloat user, not the BigFloat implementer) the specialization.
> > However, I think that chances are that most of the times I will end
> up
> > writting my own specialization, becuase it is unlikely that I can
> forget
> > about BigFloat for this job (or I wouldn't have chosen it in the
> fist place)
>
> I'm not sure I fully understand your example. If your algorithm
> doesn't use cos, then since cos is a template, the compiler will never
> invoke cos<BigFloat> and you won't get an error. If you do write code
> that calls cos<BigFloat> then you will get an error. If you'd like to
> have use it just that once then you could do something liek
> cos<double>(numeric_cast<double>(x)), where x is a BigFloat. If you
> want to call it often, then you'd probably want to specialize cos for
> your BigFloat type. I'd be worried that users would expect
> cos<BigFloat> to provide an accuracey consistant with the precision of
> BigFloat. If the default implementation casts it to a double, then
> users could easily be misled. If they have to either cast or
> specialize themselves, then I'd think it's less likely someone would
> get intro trouble. I suppose we could provide a macro to make
> specializing a "standard function" for a user defined type a one
> liner, provided they were willing for it to be implemented in terms of
> casting their type to a double, doing the calculation in double, and
> casting the answer back to their type.
>
I think the question is this: What would a user expect from X
boost::math::cos( X const& ) if he didn't provide any specialization?
Can it expect it to take advantage of all the precision of X? I wouldn't,
because that's not the way C++ works.

However, a user might have just *forgoten* to specialize cos, even when a
specific implementation exist in the UDNT package, so the asserting default
would trap his fault here.

So I agree, an asserting default is safer, and we can provide the macro you
suggested to easliy introduce a specialization using double.

> > Second:
> > ~~~~~~
> >
> > I think that the functions should take "T const&" instead of "T
> const".
> > For a built-in type, pass by value is OK, but this is targeted to
> arbitrary
> > types, not just built ins.
>
> What about using call_traits<T>::param_type?
>
I've tried this in generic_math.h, but it trashes Borland (up to) 5.5.1.

> > Third:
> > ~~~~
> >
> > In:
> >
> > template<> TYPE FUNC< TYPE >(const TYPE x) \
> > { return boost::numeric_cast<TYPE>(::std::STDFUNC(x)); };
> >
> > The numeric_cast<> here is unnecesary.
> > STDFUNC is passed to the macro such that it has the signature TYPE
> > (*f)(TYPE);
> > If it didn't, then another conversion would be required from TYPE to
> the
> > function argument.
>
> I had in mind allowing for implicit use of more accurate type to
> perform a calculation. (e.g. if pow didn't have a float
> implementation, then it could convert the float to a double, perform
> the calculation in double, and cast back to float). In retrospect,
> there's probably no instance where this is actually necessary. So the
> numeric_cast can probably be removed.
>
Notice that if TYPE has bigger range than the std function argument -instead
of smaller, as you considered-, then the cast is required in the function
call, not in the return.
But, unfortunately, for this we need to know the argument type of STDFUNC;
and this would defeat any chance of proper overloading.
Having the cast only on the return type will work in cases were the std
function takes a bigger-range number than the one passed (as in the example
you gave), but will perform an implicit conversion were the type passed has
bigger range.
Therefore, I wouldn't use overloading. I think the best choice is to use std
functions with explicit signature (thus knowing exactly the type used) in
this way:

  return numeric_cast<TYPE>(::std::STDFUNC( numeric_cast<STDTYPE>(X) ) ) ;

Notice that with a properly optimized numeric_cast<>, this has no runtime
overhead in the case were TYPE==STDTYPE.

Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com


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