Universidad de Costa Rica

 

Escuela de Ciencias de la Computación e Informática

 

 

Programación II

CI-1201

 

Profesor:

 

Adolfo Di Mare

 

Tarea Programada IV

 

 

Jorge Arias Solera A50543

[email protected]

 

Carlos Mejías Álvarez A53350

[email protected]

 

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".

 

       Friends.

            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.               

Organigrama

 

 

         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

Hosted by www.Geocities.ws

1