25
Case Study - Fractions Timothy Budd Oregon State University

Case Study - Fractions Timothy Budd Oregon State University

Embed Size (px)

Citation preview

Case Study - Fractions

Timothy Budd

Oregon State University

2

Objectives• To easily create new instances of the rational number

abstraction.• To manipulate rational numbers using arithmetic operations,

yielding new rational number results. • It should be possible to mix rationals and other arithmetic

quantities in the same expression. • It should be possible to assign a rational number value to a

rational number variable. • The modification forms of assignment should be supported.• To compare one rational number to another. • It should be possible to perform input and output operations

with rational numbers.

3

Example of Rational Numbers// probability one is 1 in 8rational p1 (1,8);

// probability two is 2 in 3rational p2 (2, 3);

// probably of both together is their productrational p3 = p1 * p2;

// probably of either independent is their sumrational p4 = p1 + p2;

// what is this probability?cout << "combined probability is " << p3 << endl;cout << "independent probability is " << p4 << endl

4

Definition of Operations on Rational Numbers

O p e r a t i o n C a l c u l a t i o n

d

c

b

a

db

cbda

d

c

b

a

db

cbda

d

c

b

a

db

ca

d

c

b

a

cb

da

b

a

b

a

d

c

b

a )()( cbda

d

c

b

a )()( cbda i f b > 0 , d > 0

5

// class rational// rational number data abstraction

class rational {public:

// constructorsrational () : top(0), bottom(1) { }rational (int t) : top(t), bottom(1) { }rational (int t, int b) : top(t), bottom(b) { normalize(); }rational (const rational & r) : top(r.top), bottom(r.bottom) { }// accesser functionsint numerator () const { return top; }int denominator () const { return bottom; }// assignmentsvoid operator = (const rational & r) {top = r.top; bottom = r.bottom;}void operator += (const rational &);// other operationsoperator double () const { return top / (double) bottom; }const rational & operator++() { top += bottom; return this; }const rational operator++(int);

private:int top; // data areasint bottom;void normalize (); // operation used internally

};

6

Interface and Implementation

• The code associated with a component is separated into two files. – The interface file describes how to use the component.

– The implementation file contains the actual code to perform the actions.

• The class description is divided into two parts.– Public: behaviors and data fields that users of the data

abstraction can access.

– Private: behavior and fields that are accessible only within the component and are off-limits to other users.

7

Constructors

• Every class should define a default constructor.• Use initializes whenever possible.• The declaration that uses an empty parentheses list is

not syntactically incorrect; simply not doing what the programmer expects.

• Every class should define a copy constructor.

8

Member Function

• An accessor function provides access to the internal state of an object.

left.numerator() * right.denominator() + right.numerator() * left.denominator()

• A function defined as part of the behavior of a class is called a member function.

• The const keyword in C++ is not the same as the final keyword in Java.

9

Operators

• In C++, operators can potentially have overloaded meanings.

• Requirement: the definition must not require arguments that match any existing definition.

• Can be achieved if one or both arguments are a new data type, since no existing definition can be using these types.

10

Operators

const rational operator + (const rational & left, const rational & right)

{// return sum of two rational numbersrational result (

left.numerator() * right.denominator() + right.numerator() * left.denominator(),

left.denominator() * right.denominator());return result;

}

• The address operator in the argument list indicates that the left and right values will be passed by reference.

11

Operators

• A list of prototypes for the arithmetic operations.

// prototypes for arithmetic operations, including unary negationconst rational operator + (const rational &, const rational &);const rational operator - (const rational &, const rational &);const rational operator * (const rational &, const rational &);const rational operator / (const rational &, const rational &);const rational operator - (const rational &);bool operator < (const rational &, const rational &);bool operator == (const rational &, const rational &);

12

Unary Negation Operator

const rational operator - (const rational & val)

{// return negation of argument valuereturn rational (- val.numerator(), val.denominator());

}

13

Comparison Operators

bool operator < (const rational & left, const rational & right)

{// less than comparison of two rational numbersreturn left.numerator() * right.denominator() <

right.numerator() * left.denominator();

}

bool operator == (const rational & left, const rational & right)

{return left.numerator() * right.denominator() ==

right.numerator() * left.denominator();

}

14

Increment and Decrement

• The variable this is a value in Java but is a pointer in C++.

class rational {...const rational & operator ++ () { top += bottom; return *this;}...

};

const rational rational::operator++ (int) {// increment fraction, but return original value, make clonerational clone(*this);top += bottom; // make changereturn clone; // return clone

}

15

Functions

• Functions cannot access the internal (private) structure of any class, unless declared as a friend.

const rational abs (const rational & num) {// return the absolute value of a rational numberint newtop;int newbottom = num.denominator();

// get non-negative numerator partif (num.numerator() < 0)

newtop = - num.numerator();else

newtop = num.numerator();// create and return resultreturn rational(newtop, newbottom);

}

16

Member Function Operators

void rational::operator += (const rational & right)

{// modify by adding right hand sidetop = top * right.denominator() + bottom *

right.numerator();

bottom *= right.denominator();

// normalize the result, ensuring lowest denominator form

normalize();

}

17

void rational::normalize() {// normalize rational by (a) moving sign to numerator :// b) making sure numerator and denominator have no common divisorsint sign = 1; // sign is 1 if non-negative, -1 if negativeif (top < 0) {

sign = -1;top = - top;

}if (bottom < 0) {

sign = - sign;bottom = - bottom;

}

// make sure we are not dividing by zeroif (bottom == 0)throw range_error("fraction with zero numerator");

// find greatest common divisor int d = gcd(top, bottom);

// move sign to top and divide by gcdtop = sign * (top / d);bottom = bottom / d;

}

18

Member Function Operators

try {... // computation involving rationals

} catch (range_error & e) {printf("got exception %s", e.what());

}

19

Conversion Operations

• Constuctors are also used implicitly.

rational x, y;...

x = y * 3;

• Temporary values can also be created directly by the programmer, by invoking the constructor as if it were an ordinary function.

x = y * rational(3, 4);

20

Conversion Operations

• Conversions the other direction, from an object type to another type, are defined using a conversion operator.

• A conversion operator is an operator whose name is a type.

class rational {...

operator double () const { return top / (double) bottom; }};

21

Conversion Operations

• Avoid cast if possible, but if unavoidable use a static or dynamic cast.

rational x(3, 4);cout << "3/4 of pi is " << (3.14 * double(x)) << '\n';

22

Input and Output Streams

• Output is easily accommodated by redefining the left shift operator <<, and providing a new meaning.

ostream & operator << (ostream & out, const rational & value)

{// print representation of rational number on // an output stream

out << value.numerator() << '/' << value.denominator();return out;

}

23

Input and Output Streams

• Predefined precedence is low enough to allow arithmetic expressions to appear in output without using parenthesis, as in:

cout << "a + b * c is " << a + b * c << '\n';

• The left shift operator with its conventional meaning can be used in anoutput statement, by surrounding it with parenthesis:

cout << " a left shift by 3 is " << (a << 3) << '\n';

24

Stream Input• A loop that would read values repeatedly

from the input until end of file could be written as:

while (cin >> intval) {// process intval...

}// reach this point on end of input...

• An easy way to remember, the stream I/O operations is to visualize them as arrows.

• The input operator, >> x, points data into x, whilethe output operator, << x, copies data out of x.

25

istream & operator >> (istream & in, rational & r) {// read a rational number from an input streamint t, b;// read the topin >> t;// if there is a slash, read the next numberchar c;in >> c;if (c == '/')

in >> b; // read bottom partelse {

in.putback(c);b = 1;

}// do the assignmentrational newValue(t, b);r = newValue;// return the streamreturn in;

}