Boost logo

Boost :

From: Moore, Paul (paul.moore_at_[hidden])
Date: 2001-01-23 10:58:06


I think I'm close to the point where I can release an updated version of
rational.hpp. Most of the issues are now sorted.

Only one big outstanding issue remains, which is that without Koenig lookup,
the implementation of abs() is irretrievably broken. In the case of
rational<IntType> where IntType is a user-defined type in its own namespace,
with an abs() function defined in that namespace, I can't see any way for
abs(rational<IntType>) to locate abs(IntType) in the absence of Koenig
lookup. After all, that's the problem Koenig lookup was designed to solve...

With a fully implemented Koenig lookup, the following should work:

template <typename IntType> rational<IntType> abs(rational<IntType> r)
{
    using std::abs; // [1] For built-in types...
    return rational<IntType>(abs(r.numerator()), r.denominator());
}

I dislike the need for the incantation at [1], though. First, because it
feels clumsy. Effectively, it is attempting to add a few more names into the
pot for when the compiler tries to find an abs() function - specifically for
the case where IntType is a builtin. But Koenig lookup is effectively a "do
the right thing" type of rule - which doesn't work without help for builtin
types?!?? As I say, it just "feels clumsy".

More importantly, the impression I get is that too many compilers are broken
with respect to the using statement at [1]. Either they don't put abs() into
std::, or (gcc) they ignore using statements at function scope. There's also
the question of whether std::abs() is available for all built-in types. The
standard only mandates std::abs(int) and std::abs(long) - while I can't see
anyone wanting rational<char> or rational<short>, I certainly can see a case
for rational<long long> or rational<__int64> or whatever. And I could
imagine compilers "forgetting" std::abs(long) and just leaving std::labs for
that case (I know MSVC does this, but as it doesn't have Koenig lookup, the
point is moot.)

Overall, I feel that worrying about all of these issues for a relatively
uncommon, non-performance-critical function such as abs() just isn't worth
it.

I'm pretty sure that what I'll do is to implement rational abs() as

template <typename IntType> rational<IntType> abs(rational<IntType> r)
{
    IntType num = r.numerator();
    if (num < IntType(0))
        num = -num;
    return rational<IntType>(num, r.denominator());
}

I apologise in advance to implementers of unlimited-precision integer types
who take care to write a highly tuned abs() function which does a simple bit
operation.

[[Actually, I will probably work a bit harder at this to minimise copying of
IntTypes. That's probably a more important optimisation than worrying about
abs vs compare/negate]].

Paul.

PS There's probably scope here for a boost/numeric_ops.hpp header, which
takes a traits-like approach to standard numeric operations like
sign-testing. The default implementation would be comparison against zero,
but implementers of numeric types could specialise to provide optimised
versions. This is somewhat related to the Number/Algebraic concepts thread,
but taking a less theoretical and more practical approach.


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