 Boost :

From: Moore, Paul (paul.moore_at_[hidden])
Date: 2000-11-22 04:16:57

From: John (EBo) David [mailto:ebo_at_[hidden]]
> When playing with the rationals some more I ran across another bug.
> When initializing a rational from a floating point number, the value
> is auto-casted to IntType and produces erroneous results. For
> example:
>
> int main ()
> {
> rational <int> tr;
>
> td = 1.5; tr = td;
> std::cout << "rational " << tr;
> std::cout << " cast " << boost::rational_cast<double>(tr);
> std::cout << " original double " << td << std::endl;
> }
>
> sets tr to be "1/1" and not "3/2"
>
> Is it reasonable to provide a rational constructor and/or assignment
> with a floating point argument?

It certainly is, in principle. The hard problem is how to convert a floating
point value to a rational, in general. As far as I understand it, this is a
non-trivial issue, especially if you want a "reasonable" answer. For
example, given that 0.1 is not exactly representable in base 2, how confused
would you be if

rational<int> r = 0.1;
cout << r;

returned "1236517/12365171" (or something)???

I believe that there are good algorithms for converting (inexact) floating
point numbers to "reasonable" rationals. But I don't know what they are, nor
am I competent to judge their effectiveness. If anyone could contribute some
code for this, I'd be willing to integrate it.

By the way, regardless of all this, I do agree that the existing behaviour
is surprising almost to the point of being describable as a bug... But I
don't see a good fix, short of the one I described above.

> Somewhat related to this is the operator>> definition. Isn't it
> reasonable to assume that the user defined input should not have to be
> redefined to use the "nnn/ddd" syntax, but to behave as expected when
> accepting floating point input?

Same issue, to an extent. However, by parsing the floating point manually,
it would be possible to read exact floats and store them as exact rationals.
But I do not, personally, see that as entirely appropriate.

The key point here (familiar to Scheme programmers...) is the distinction
between "exact" and "inexact" numbers. Integers and rationals are "exact",
whereas floats are "inexact". Conversions from exact to inexact can
potentially lose information (this is not obvious to C programmers, where
all of the exact representations convert without loss to the inexact types -
but 64-bit platforms start to see this, where very large integers can't be
represented exactly as floats, or even doubles). Conversions from inexact to
exact can either be impossible (without truncation) or can give
non-intuitive results - basically, the issue is that exact->inexact->exact
is by no means certain of returning the original value.

This is all very subtle stuff (as are the mathematical properties of machine
floating point in general). It would be nice to get it right, but it's
verging on being outside the design limits of the rational class (which is
intended as a "simple" rational number solution).

It's also related to the subtleties (hinted at in the documentation) of the
accuracy limits of rational<T> when T is a limited-precision integer type...

As I say, code accepted gratefully, but it's a bit beyond my competence to
get it right by myself.
Paul.