Boost logo

Boost :

From: Moore, Paul (paul.moore_at_[hidden])
Date: 2000-10-19 03:42:35


From: David Abrahams [mailto:abrahams_at_[hidden]]
> Thanks. I guess all I've proven is that you
> can't find this stuff out at a glance. Maybe it
> still warrants an update?

Looks like it. As you can tell, documentation is not my strong point...

> > This gives rise to a question - is there any
> > real-world need to access the numerator or
> > denominator of a rational number, in general?
>
> Yeah! What if I want to print the number?
> I think without access to the rep it is
> essentially a write-only medium. Kind of like a
> "data motel" (see below).

I could argue that I've provided operator<<() for that. But I take your
point. (See below).

> Also, suppose I want to convert the number to a
> double? Is there a way to do that?

rational_cast<double>(rat)

This is my least-favourite design decision. I'd like to be able to override
the "real" cast operators, to get this behaviour. I don't want implicit
conversions. I don't want a member function (doesn't fit with the "just
another number type" feel). I don't want a "simple" global function like
to_double (too likely to cause name clashes). This is a compromise which
gives cast-like syntax. It's cute, but if it is a commonly-used idiom, we're
going to end up with masses of similarly-named type-specific cast operators.

But it is documented, if you look hard enough :-)

> I think that when we get to the stage of wanting
> a rational number that isn't neccessarily
> normalized, we'll want to parametrize the type
> to allow either behavior. So long as we're not
> (yet) overgeneralizing this class into oblivion,
> I would prefer that we make maximal explicit
> guarantees.

The original thought process was that we may save some time by avoiding
normalisation steps during intermediate calculations. I can't imagine ever
wanting (externally) a denormalised fraction, but I can certainly imagine
the internal representation being in that state occasionally, for efficiency
reasons.

That only implies that numerator() and denominator() should be guaranteed to
normalise, if needed. But that makes them (potentially) non-const, or
requires the whole internal representation to be mutable...

Just to clarify the internal representation issue a bit more (it's mentioned
briefly in the documentation :-), the problem which arises is with the
granularity of the representation. Assume we have a 32-bit int, and we are
looking at rational<int>. Then the smallest representable positive rational
is 1/0x7FFFFFFF. Hence, "rational-eps" is 1/0x7FFFFFFF. But, at the top end
of the range, "rational-max" is 0x7FFFFFFF/1, and the next lower
representable rational is 1 less (0x7FFFFFFE/1). This varying-precision
dependent upon magnitude behaviour is normal with floating point
representations, but it is well known that floatingh point arithmetic is
"hard" to get right, and I would argue that that is *not* what the average
user would expect of a rational class (after all, rationals are "exact
fractions", aren't they?).

There is no way to build a truly exact rational from a limited-range integer
(one of the reasons I'd like to see a bigint class in boost is that
rational<bigint> is the natural rational number representation), but there
are representations which minimise the surprise. Integer part plus fraction
less than 1, is the one I thought of first. But it complicates the
implementation significantly, so I held off implementing things like that. I
do believe that it may be worth the change in the long run, though.

With int+num/den representation, there is a problem with the numerator() and
denominator() methods, in that they may not be representable within the
underlying integer type - consider 0x7FFFFFFF+1/0x7FFFFFFF. That is the
basic reason why I am reluctant to "bless" these methods. It fixes the
internal representation in ways I am not sure are appropriate.

But as and when Boost gains an efficient implementation of unlimited
precision integers, it becomes reasonable to simply include a "representable
values" section in the documentation, which discusses the issues, and points
out that for exact fractions (which is what people usually want), the best
option is rational<bigint>.

> > I may look at rewording this section of the
> > documentation. Any suggestions would be
> > gratefully received.
>
> A simple table would probably help.

I'll see what I can do.

Thanks for raising the issues. Hopefully, the above explains the trade-offs
a bit better.

Paul.


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