Universidad de Costa Rica
Escuela de Ciencias de
Programación II
CI-1201
Profesor:
Adolfo Di Mare
Tarea Programada IV
Jorge Arias Solera A50543
Carlos Mejías Álvarez A53350
Grupo 1
Lunes 10 de septiembre del 2007
Índice
Portada…………………………………………………………………..1
Índice……………………………………………………………………..2
Introducción……………………………………………………………..3
Objetivos…………………………………………………………………3
Descripción del problema…………………………………………….3
Requerimientos…………………………………………………………3
Abstracción………………………………………………………………3
Especificación de las clases…………………………………………..4
Operaciones y métodos………………………………………………..4
Arquitectura………………………………………………………………5
Código fuente actual…….……………………………………………..5
Introducción.
Implementar
la clase Decimal que cuenta con las operaciones aritméticas necesarias para que
funcione correctamente el programa de prueba
para la clase emplantillada
de números racionales. En el Rep de la
clase Decimal se usa una representación de los
dígitos decimales que permita almacenar
tantos dígitos como sea necesario.
Dirección
de Internet:
http://www.geocities.com/jors17/index.html
Objetivos.
·
Crear la implementación
de la clase Decimal o hugeInt con las operaciones aritméticas básicas como la
suma, resta, multiplicación, división y modulo así como la representación
privada de esta clase.
·
Probar esta implementación
en una clase emplantillada y que funcione correctamente.
Descripción
del problema a resolver.
Desarrollar
la clase decimal con los algoritmos necesarios para resolver
las operaciones aritméticas necesarias.
Requerimientos.
Para poder realizar este trabajo se necesita el proyecto rational_test.vcproj
que contiene los archivos decimal.cpp, decimal.h, ADH_test y ADH_port.
Abstracción.
El programa debe realizar correctamente las operaciones
suma (+), resta (-),
multiplicación (*), división (/) y modulo (%) de números racionales, además de utilizar de manera
correcta.
Especificación
de las clases.
decimal.cpp
Clase
donde se encuentra implementada las
operaciones básicas que se deben
realizar con números racionales.
decimal.h
Esta incluida en el decimal.cpp.
rational_test.cpp
Prueba el funcionamiento correcto de la clase
decimal.cpp.
Operaciones
y métodos.
decimal(
) = Constructor de vector.
decimal(long
num) = Constructor a partir de un valor entero.
decimal(const rational
&o) = Constructor de copia.
~ (decimal) =
Destructor.
void set( long num,) =
Cambia el valor del número decimal.
(long num ) const =
Copia del numerador.
decimal & operador= (const
decimall &o) Copia desde "o".
decimal& operador=(
long) = Asignación desde un “long”
decimal& swap (decimal&
o) = Intercambia los valores de "*this"
y "o".
decimal & operador+=
(const decimal&) = Suma "*this" el
valor de "otro".
decimal& operador-= (const
decimal&) = Resta "*this" el valor de "otro".
decimal & operador*=
(const decimal &) = Multiplica "*this"
por "num".
decimall & operador/=(const
decimall &) = Divide "*this"
por "num". decimall & operador%=(const decimall &) =
Modulo "*this" por "num".
decimal operator- ()
const = Para expresar el valor como negativo"-x".
decimal operador+(const
decimal&,const decimal&) = Suma
decimal operador-(const decimal&,const
decimal&) = Resta
decimal operador*(const decimal&,const
decimal&) = Multiplicacion
decimal operador/(const decimal&,const
decimal&) = División
decimal operador%(const
decimal&,const decimal&) = Modulo
bool operador== (const decimal&,
const decimal&) = Igualdad
bool operador< (const decimal
&, const decimal&) = x < y
bool operador!= (const decimal&, const decimal&) =
Desigualdad
bool operador<= (const
decimal &, const decimal&) = x <= y ?
bool operador>= (const
decimal &, const decimal&) = x >= y
bool operador> (const decimal
&, const decimal&) &) = x > y
bool check_ok
(const decimal&r) Verifica la invariante.
Arquitectura.


Código fuente.
//
decimal.cpp (c) 2005
[email protected]
/**
\file decimal.cpp
\brief Implementaciones para la clase \c
"decimal"
\author Adolfo Di Mare
<[email protected]>
\date
2005
- Why English names??? ==>
http://www.di-mare.com/adolfo/binder/c01.htm#sc04
*/
#include "decimal.h"
#include <cstdlib>
#include <cctype> // isdigit()
/**
Verifica la invariante de la clase \c decimal.
\par <em>Rep</em> Modelo de la
clase:
\code
+---+
| 3 | <== m_num == numerador del número racional
+---+
|134| <== m_den == denominador del número racional
+---+
\endcode
-
http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
\remark
Libera al programador de implementar el
método \c Ok()
- http://www.di-mare.com/adolfo/binder/c04.htm#sc11
*/
bool check_ok( const decimal& r ) {
if (&r
== 0) {
/// -
Invariante: ningún objeto puede estar almacenado en la posición nula.
return false;
}else {
return
true;
}
} // check_ok()
/**
Verifica la invariante de la clase \c decimal.
\remark
Esta implementación nos se le mete al
<em>Rep</em>
(casi siempre no es posible implementar una
función como ésta).
-
http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
\remark
Libera al programador de implementar el
método \c Ok()
-
http://www.di-mare.com/adolfo/binder/c04.htm#sc11
*/
bool
check_ok_no_Rep( const decimal& r ) {
if (&r
== 0) {
/// -
Invariante: ningún objeto puede estar almacenado en la posición nula.
return false;
}else{
return
true;
}
} // check_ok_no_Rep()
/**
Calcula el Máximo Común Divisor de los números \c "x" y \c
"y".
- <code> mcd(x,y) >= 1
</code> siempre.
- MCD <==> GCD: <em> Greatest
Common Divisor </em>.
\pre
<code> (y != 0) </code>
\remark
Se usa el algoritmo de Euclides para hacer
el cálculo.
\par Ejemplo:
\code
2*3*5 == mcd( 2*2*2*2 * 3*3 * 5*5, 2*3*5 )
30 == mcd( -3600, -30 )
\endcode
*/
/**
Simplifica el numerador y el denomidador.
- Transforma el número decimal de manera
que el numerador y el
denominador sean primos relativos,
asegurando además que el
denominador es siempre positivo.
- Si <code>(m_num==0) ==>
(m_den==1)</code>.
- Simplifica la fracción para que \c m_num
y \c m_den sean números
primos relativos ie,
<code>mcd(m_num,m_den) == 1</code>.
- Asegura que \c m_den sea un número
positivo.
- Restaura la invariante de la clase \c
decimal.
*/
///
Le suma a \c "*this" el valor de \c "otro".
inline decimal&
decimal::operator += (const
decimal& otro) {
// m_num
= m_num * otro.m_den + m_den * otro.m_num;
// m_den *= otro.m_den;
//Simplify();
return *this;
} // operator +=
///
Le resta a \c "*this" el valor de \c "otro".
inline decimal&
decimal::operator -= (const
decimal& otro) {
/*long oldm_den =
m_den;
long oldm_num = m_num;
long d
= otro.m_den;
long n
= otro.m_num;
m_den *= d;
m_num = oldm_num * d - oldm_den * n;
//Simplify();
return *this;*/
} // operator -=
/**
Graba el valor de \c "r" en el flujo \c "COUT".
- Graba el valor en el formato [num/den].
- En particular, este es el operador que se
invoca
cuando se usa, por ejemplo, este tipo de
instrucción:
\code
cout << r << q;
\endcode
*/
inline
ostream& operator<< (ostream
&COUT, const decimal& r) {
/*
if ( r.m_den == 1 ) { // no hay parte
fraccional
return COUT << "["
<< r.m_num << "]" ;
} else {
return COUT << "["
<< r.m_num << "/" << r.m_den << "]"
;
}
*/
} // operator
<<
/**
Lee del flujo de texto \c "CIN" el valor de \c "r".
\pre
El número decimal debe haber sido escrito
usando
el formato "[r/den]", aunque es
permisible usar
algunos blancos.
- Se termina de leer el valor sólo cuando
encuentra \c "]".
- <code> [ -+-+-+-+- 4 / -- -+ --
32 ] </code> se lee como
<code> [1/8] </code>
*/
inline
istream& operator >> (istream
&CIN, decimal& r) {
char
ch; // valor
leido, letra por letra, de "CIN"
bool
es_positivo = true; // manejo de los
signos + y -
// se brinca todo
hasta el primer dígito
do {
CIN >> ch;
if (ch
== '-') {
// cambia de signo
es_positivo = !es_positivo;
}
} while
(!isdigit(ch));
// se traga el
numerador
r.m_num = 0;
while
(isdigit(ch)) { // convierte a decimal: izq -->
der
r.m_num = 10 * r.m_num + (ch-'0');
CIN >> ch;
}
// se brinca los
blancos después del numerador
while
(isspace(ch)) {
CIN >> ch;
}
if (ch ==']') { // es un número
entero
// r.m_den = 1;
}
else {
do
{ // se brinca
todo hasta el denominador
CIN >> ch;
if
(ch == '-') {
es_positivo = !es_positivo;
}
} while
(!isdigit(ch));
// se traga
el denominador
//r.m_den =
0;
while
(isdigit(ch)) {
//r.m_den
= 10 * r.m_den + (ch-'0');
CIN >> ch;
}
// El
programa se duerme si en el flujo de entrada
// NO aparece
el caracter delimitador final "]",
// pues la
lectura termina hasta encontrar el "]".
while
(ch != ']') {
CIN >> ch;
}
} // corrección: Andrés Arias <[email protected]>
// le cambia el
signo, si hace falta
if (!
es_positivo) {
r.m_num = -r.m_num;
}
//r.Simplify();
return CIN;
/*
no detecta errores...
[1/0] lo lee y no se queja
[ !#!#!$#@! 3/ aaaa 4 jajaja ] lo lee como 3/4
... pero no se supone que el usuario cometa
errores...
*/
} // operator
>>
///
\c "x+y".
///
- Calcula y retorna la suma \c "x+y&quuot;.
inline decimal operator + (const
decimal &x, const decimal &y) {
decimal res;
res = x;
res += y;
return
res;
} // operator + ()
///
\c "x-y".
///
- Calcula y retorna la resta \c "x-y&qquot;.
inline decimal operator - (const
decimal &x, const decimal &y) {
//to do
} // operator - ()
///
\c "x*y".
///
- Calcula y retorna la multiplicación \c &qquot;x*y".
inline decimal operator * (const
decimal &x, const decimal &y) {
//to do
} // operator * ()
///
\c "x/y".
///
- Calcula y retorna la división \c "x//y".
///
\pre <code> y != 0 </code>
inline decimal operator / (const
decimal &x, const decimal &y) {
//to do
} // operator / ()
// EOF: decimal.cpp
//
decimal.h (c) 2005 [email protected]
/**
\file decimal.h
\brief Declara el tipo \c
"decimal".
- La clase \c decimal implementa las
operaciones aritméticas
principales para números decimales.
-
<code> [1/3] == [2/6] == ... [9/27] == ... </code>
- <code> [1/3] * [2/6] / [3/9] - [9/27] </code>
- Permite usar racionales en cualquier
sitio en donde se puedan
usar valores numéricos.
\author Adolfo Di Mare <[email protected]>
\date
2005
*/
#ifndef decimal_h
#define decimal_h ///< Evita la inclusión múltiple
#define INCLUDE_iostream //
ADH_port.h ==> #include <iostream>
#include "ADH_port.h"
/** La clase \c decimal implementa las
operaciones aritméticas
principales para números decimales.
- <code> [1/3] == [2/6] == ...
[9/27] == ... </code>
- <code> [1/3] * [2/6] / [3/9] - [9/27] </code>
*/
class decimal {
private:
long m_num;
///< Numerador
public:
// constructores
decimal() : m_num(0) { } ///< Constructor
de vector
decimal(long
num) : m_num(num) { } ///< Constructor a partir de
un valor entero
decimal(const
decimal& o) { m_num = o.m_num; }
/// Constructor de copia
~decimal() { } ///<
Destructor
void set(long num=0); // Le cambia el valor a \c "*this"
long num() const { return m_num;
} ///<
Copia del numerador
// void num(long n) { m_num=n; Simplify();
} // FEO
// void den(long d) { m_den= ( d!=0 ? d : m_den)
; Simplify(); } // FEO
decimal& operator = (const
decimal&); //
Asignación (copia)
decimal& operator = (long);
decimal& swap ( decimal& );
decimal& operator
+= (const decimal&);
decimal& operator
-= (const ddecimal&);
decimal& operator
*= (const decimal&);
decimal& operator
/= (const decimal&);
decimal operator - () const; //
menos unario
friend
decimal operator + (const
decimal&, const decimal&);
friend
decimal operator - (const
decimal&, const decimal&);
friend
decimal operator * (const
decimal&, const decimal&);
friend
decimal operator / (const
decimal&, const decimal&);
friend bool operator == (const decimal&, const
decimal&);
friend bool operator
< (const
decimal&, const decimal&);
friend bool operator != (const decimal&, const
decimal&);
friend bool operator <= (const decimal&, const
decimal&);
friend bool operator >= (const decimal&, const
decimal&);
friend bool operator
> (const
decimal&, const decimal&);
//friend
ostream& operator << (ostream &, const decimal& );
friend
istream& operator >> (istream
&, decimal& );
//decimal&
fromString (const char* nStr);
friend double real (const decimal& ); // Conversión a
real
friend long integer(const decimal& ); // Conversión a
long
friend bool check_ok( const
decimal& r ); // Ok()
// excluidos porque producen ambigüedad con
operadores aritméticos
// operator double () { return double(m_num) /
double(m_den); }
// operator long () { return m_num
/ m_den ; }
}; // decimal
///
Sinónimo de \c mcd(x,y) <code> [ inline ] </code>
//inline
long gcd(long x, long y) { return mcd(x,y); }
///
Cambia el valor del número decimal a \c "n/d"
inline void decimal::set(long
n) {
m_num = n;
//m_den = d;
//Simplify();
}
/**
Copia desde \c "o".
- El valor anterior de \c "*this"
se pierde.
\par Complejidad:
O( \c 1 )
\returns *this
\see
http://www.di-mare.com/adolfo/binder/c04.htm#sc05
*/
inline
decimal& decimal::operator = (const decimal& o) {
m_num = o.m_num;
//m_den =
o.m_den;
// sobra invocar a "Simplify()" pues
"o" ya está simplificado
return *this;
} // operator =
/**
Intercambia los valores de \c "*this" y \c "o".
\par Complejidad:
O( \c 1 )
\returns *this
\see
http://www.di-mare.com/adolfo/binder/c04.htm#sc08
*/
inline
decimal& decimal::swap ( decimal& o ) {
#if 1
decimal tmp = o;
o = *this;
*this =
tmp;
#else
// Esto NO funciona para objetos, métodos
virtuales, etc.
char tmp[ sizeof( *this ) ];
memcpy( tmp, o,
sizeof( *this ) ); // tmp = o;
memcpy( o, *this,
sizeof( *this ) ); // o = *this;
memcpy( *this, tmp, sizeof( *this ) ); // *this = tmp;
#endif
return *this;
}
///
Asignación desde un \c "long".
inline
decimal& decimal::operator = (long entero) {
m_num = entero;
//m_den = 1;
return *this;
} // operator =
///
Multiplica \c "*this" por \c "num".
inline
decimal& decimal::operator *= (const decimal& num) {
m_num *= num.m_num;
//m_den *=
num.m_den;
//Simplify();
return *this;
} // operator *=
/** Divide \c "*this" por el valor de
\c "num".
\pre
- (num != 0)
*/
inline
decimal& decimal::operator /= (const decimal& num) {
m_num *= num.m_num;
//m_den *=
num.m_num;
//Simplify();
return *this;
} // operator /=
///
\c "-x".
///
- Menos unario
///
- Calcula y retorna el valor \c "-x&quuot;
inline decimal
decimal::operator - () const
{
decimal tmp = (*this); // tmp.decimal(
*this );
tmp.m_num = - tmp.m_num;
return tmp;
} // operator -
///
¿ x == y ?
inline bool operator == (const decimal &x, const
decimal &y) {
return
(x.m_num == y.m_num);
/* Nota:
Como los números racionales siempre están
simplificados, no puede
ocurrir que [1/1] está almacenado como
[3/3] y en consecuencia
basta comparar los valores campo por campo
para determinar si se
da o no la igualdad.
*/
} // operator ==
///
¿ x < y ?
inline bool operator < (const decimal &x, const
decimal &y) {
return
(x.m_num * y) < (x * y.m_num);
/* Nota:
Una desigualdad de fracciones se preserva
siempre que se
multiplique a ambos lados por un número
positivo. Por eso:
[a/b] < [c/d]
<==>
[(b*d)*a/b] < [(b*d)*c/d] <==>
[d*a] < [b*c]
[a/b] > [c/d] <==> [(b*d)*a/b]
> [(b*d)*c/d] <==> [d*a] > [b*c]
Debido a que el denominador siempre es un
número positivo, el
trabajo de comparar 2 racionales se puede
lograr haciendo 2
multiplicaciones de números enteros, en
lugar de convertirlos
a punto flotante para hacer la división,
que es hasta un orden
de magnitud más lento.
*/
// return double(x.m_num) / double(x.m_den) <
double(y.m_num) / double(y.m_den);
} // operator <
///
¿ x > y ?
inline bool operator > (const decimal &x, const
decimal &y) {
return (y
< x);
} // operator >
///
¿ x != y ?
inline bool operator != (const decimal& x, const
decimal& y) {
return !(x
== y);
} // operator !=
///
¿ x <= y ?
inline bool operator <= (const decimal& x, const
decimal& y) {
return !(y
< x);
} // operator <=
///
¿ x >= y ?
inline bool operator >= (const decimal& x, const
decimal& y) {
return !(x
< y);
} // operator >=
///
Convertidor a punto flotante.
inline double real(const
decimal& num) {
return double (num.m_num);
} // real()
///
Convertidor a punto fijo.
inline long integer(const
decimal& num) {
return long
(num.m_num);
} // integer()
#if 0
/// Convertidor a punto fijo
inline decimal::operator long() {
return long (m_num / m_den);
} //
decimal::operator long
#endif
bool
check_ok_externo( const decimal& r );
#endif // decimal_h
// EOF: decimal.h
//
test_rational.cpp (C) 2006 [email protected]
/**
\file test_rational.cpp
\brief Programa de prueba para la clase \c
rational
\author Adolfo Di Mare
<[email protected]>
\date
2006
*/
#include "ADH_test.h"
#include "decimal.h"
///
Ambiente de prueba para la clase complicada para probar \c rational.
///
- No es necesario definir el método \c tearrDown() pues para este
/// caso específico \c TestFixture::tearDown()
funciona bien.
class
decimal_TestFixture : public TestCase {
protected:
decimal m_half, m_quarter, m_one;
public:
virtual void setUp(); ///<
Establece el ambiente de prueba
}; // rational_TestFixture
void
decimal_TestFixture::setUp() {
m_half.set();
m_quarter.set();
m_one.set(1);
}
class
decimal_Test_Add : public decimal_TestFixture {
public:
bool run();
}; // rational_TestFixture
///
Función principal del la prueba
///
- Requiere que recién haya sido ejecutado \\c setUp()
bool
decimal_Test_Add::run() {
/*assertTrue(
m_half+m_quarter == rational(30,40) );
assertTrue( rational(30,40) ==
m_quarter+m_half );
assertTrue( m_one+m_quarter ==
rational(125,100) );
assertTrue( rational(125,100) ==
m_quarter+m_one );
assertTrue(
rational(1,3) == rational(33,100)
);
// Esto está aquí sólo para destrozar los
valores de *this
m_half = m_quarter = m_one = 0;*/
return
nError() == 0;
}
///
Clase para probar la resta de racionales.
class
decimal_Test_Substract : public
decimal_TestFixture {
public:
bool run();
}; // rational_TestFixture
///
Función principal del la prueba
///
- Requiere que recién haya sido ejecutado \\c setUp()
bool
decimal_Test_Substract::run() {
/*assertTrue( m_half-m_quarter == rational(10,40) );
assertTrue(
rational(10,40) ==
-m_quarter+m_half );
assertTrue(
m_one-m_quarter ==
rational(75,100) );
assertTrue(
rational(-75,100) == m_quarter-m_one
);
assertEquals( rational(75,100) , m_quarter-m_one );
assertTrue( rational(75,100) == m_one+m_quarter );
// Esto está aquí sólo para destrozar los
valores de *this
m_half = m_quarter = m_one = 0;*/
return
nError() == 0;
}
///
Programa principal desde donse se invocan todas las pruebas
int main() {
decimal_Test_Add decimal_Test_Add_Instance;
cout << endl << endl << "1) Prueba directa con
\"TestFixture\"" << endl;
decimal_Test_Add_Instance.setUp();
decimal_Test_Add_Instance.run();
Report(cout, decimal_Test_Add_Instance);
if
(decimal_Test_Add_Instance.nError() != 0 ) {
cout << "El
error" << endl << decimal_Test_Add_Instance.toString();
}
decimal_Test_Substract decimal_Test_Substract_Instance;
cout << endl << endl << "2) Prueba directa con
\"TestFixture\"" << endl;
decimal_Test_Substract_Instance.setUp();
decimal_Test_Substract_Instance.run();
Report(cout, decimal_Test_Substract_Instance);
if
(decimal_Test_Substract_Instance.nError() != 0 ) {
cout << "El
error" << endl <<
decimal_Test_Substract_Instance.toString();
}
return 0;
}
// EOF: test_rational.cpp