The input operator for Rational numbers

S

Saeed Amrollahi

Hi

I am going to design and implement a typical library for rational
numbers, something like
Boost.Rational. My class is actually implemented as a template, in a
similar manner to the standard complex class.
I want to handle Whole numbers like rational numbers; As you know,
from point of math view
whole numbers are subset of rational numbers, for example 3 is 3/1.
I like to read both kind of numbers (whole and fraction) in a single
operator>> function. I mean
I want to handle
a
a/b
together. I finally ended with an assumption: Immediately, after
numerator, I should get '/':
template<class T>
std::istream& operator>>(std::istream& is, rational<T>& r)
{
/*
handle the following foramts:
n/d
n
*/
T n, d;
char c = 0; // for '/'
is >> n;
if (is.good()) {
is.get(c);
}
if (c == '/') { // fraction
if (is.good())
is >> d;
}
else if (std::isspace(c) || is.rdstate() & std::ios_base::eofbit)
{ // whole number
d = 1;
}
else {
is.clear(std::ios_base::badbit); // set state
}

if (is) // everything is good
r = rational<T>(n, d);

return is;
}

I know, my code may be inefficient.
Any suggestion for better/more elegant handling different formats and
better efficiency?

Thanks in advance,
-- Saeed Amrollahi
 
J

James Kanze

I am going to design and implement a typical library for
rational numbers, something like Boost.Rational. My class is
actually implemented as a template, in a similar manner to the
standard complex class. I want to handle Whole numbers like
rational numbers; As you know, from point of math view whole
numbers are subset of rational numbers, for example 3 is 3/1.
I like to read both kind of numbers (whole and fraction) in a
single operator>> function. I mean I want to handle
a
a/b
together. I finally ended with an assumption: Immediately,
after numerator, I should get '/':
template<class T>
std::istream& operator>>(std::istream& is, rational<T>& r)
{
/*
handle the following foramts:
n/d
n
*/
T n, d;
char c = 0; // for '/'
is >> n;
if (is.good()) {
is.get(c);
}
if (c == '/') { // fraction
if (is.good())
is >> d;
}
else if (std::isspace(c) || is.rdstate() & std::ios_base::eofbit)
{ // whole number
d = 1;
}
else {
is.clear(std::ios_base::badbit); // set state
}
if (is) // everything is good
r = rational<T>(n, d);
return is;
}
I know, my code may be inefficient.
Any suggestion for better/more elegant handling different
formats and better efficiency?

I'm not too sure about your error handling, and your logic. If
nothing else, you're extracting one character too many when
reading just a whole number. (Think of something like 3+1/3.)
What's wrong with something simple like:

T n;
is >> n;
T d(1); // T must support conversion from int.
if ( is.peek() == '/' ) {
is.get(); // extract '/'
is >> d;
}
if ( is ) {
r = rational<T>( n, d );
}
return is;

This leaves all of the error handling up to >>T, and seems
overall the simplest solution.
 
S

Saeed Amrollahi

I'm not too sure about your error handling, and your logic.  If
nothing else, you're extracting one character too many when
reading just a whole number.  (Think of something like 3+1/3.)
What's wrong with something simple like:

    T n;
    is >> n;
    T d(1);    //  T must support conversion from int.
    if ( is.peek() == '/' ) {
        is.get();       //  extract '/'
        is >> d;
    }
    if ( is ) {
        r = rational<T>( n, d );
    }
    return is;

This leaves all of the error handling up to >>T, and seems
overall the simplest solution.

Hi James
Thank you for your feedback.
The template parameter T is an Integral type.
My main problem is:
Is there any solution to read a fraction and whole number in formatted
way
rather than using unformatted (get function)? I guess there is no way.
So I assumed, if user wants to enter a whole number, (s)he have to
enter in
one line. So, I use isspace() and ios_base::eofbit.
for a fraction my assumption is: if user wants to enter a fraction, (s)
he have to
enter '/' immediately after numerator. Your code somehow reflects
that.
Of course your code is simple and elegant and my code is somehow
sloppy.

Please throw light.
-- Saeed Amrollahi
 
J

James Kanze

On Nov 16, 12:01 pm, James Kanze <[email protected]> wrote:

[...]
Thank you for your feedback.
The template parameter T is an Integral type.

I more or less guessed that:). Although you might want to ask
yourself what will happen if someone instantiates your template
on double, or even std::string.
My main problem is:
Is there any solution to read a fraction and whole number in
formatted way rather than using unformatted (get function)?

I'm not sure what you really mean by "formatted" way? You want
to look at a single character, without extracting it;
istream::peek is the function which does that.

There is one weakness in my quicky solution above: it doesn't
allow white space before the '/', but allows it after. This
could easily be fixed by verifying that the character after the
'/' is not white space (getting it, again, using peek()), or by
skipping white space before checking for the '/'.
I guess there is no way. So I assumed, if user wants to enter
a whole number, (s)he have to enter in one line.

Not with the above. The above will read a number. If the
character immediately following the number is a '/', it skips
this, and reads a second number. Otherwise, it stops with the
number, removing no more characters.
So, I use isspace() and ios_base::eofbit.

That was the point I didn't fully understand in the original
code. Is the fact that you only read the numerator (and use 1
for the denominator) determined by the fact that the first
number is immediately followed by a '/', or by the fact that it
was followed by white space or the end of file. In other words,
what was the desired behavior when reading something like:
43,x
Should the extractor extract the 43, return 43/1, and leave the
input positionned on the ',', or should it set failbit to
indicate a format error on input. (My version does the first.)
for a fraction my assumption is: if user wants to enter a
fraction, (s) he have to enter '/' immediately after
numerator. Your code somehow reflects that.

My code does that exactly. If the first character after the
number in the stream is a '/', it skips it, and attempts to read
a denominator. Otherwise, it uses the default value 1 as
denominator.
 
S

Saeed Amrollahi

On Nov 16, 12:01 pm, James Kanze <[email protected]> wrote:

    [...]




Thank you for your feedback.
The template parameter T is an Integral type.

I more or less guessed that:).  Although you might want to ask
yourself what will happen if someone instantiates your template
on double, or even std::string.
My main problem is:
Is there any solution to read a fraction and whole number in
formatted way rather than using unformatted (get function)?

I'm not sure what you really mean by "formatted" way?  You want
to look at a single character, without extracting it;
istream::peek is the function which does that.

There is one weakness in my quicky solution above: it doesn't
allow white space before the '/', but allows it after.  This
could easily be fixed by verifying that the character after the
'/' is not white space (getting it, again, using peek()), or by
skipping white space before checking for the '/'.
I guess there is no way.  So I assumed, if user wants to enter
a whole number, (s)he have to enter in one line.

Not with the above.  The above will read a number.  If the
character immediately following the number is a '/', it skips
this, and reads a second number.  Otherwise, it stops with the
number, removing no more characters.
So, I use isspace() and ios_base::eofbit.

That was the point I didn't fully understand in the original
code.  Is the fact that you only read the numerator (and use 1
for the denominator) determined by the fact that the first
number is immediately followed by a '/', or by the fact that it
was followed by white space or the end of file.  In other words,
what was the desired behavior when reading something like:
    43,x
Should the extractor extract the 43, return 43/1, and leave the
input positionned on the ',', or should it set failbit to
indicate a format error on input.  (My version does the first.)
for a fraction my assumption is: if user wants to enter a
fraction, (s) he have to enter '/' immediately after
numerator. Your code somehow reflects that.

My code does that exactly.  If the first character after the
number in the stream is a '/', it skips it, and attempts to read
a denominator.  Otherwise, it uses the default value 1 as
denominator.

Thanks for your feedback
-- Saeed Amrollahi
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top