C# - Trabalhando com números racionais (Structs)


Neste artigo irei mostrar como podemos tratar números racionais criando uma classe e alguns métodos para realizar as operações básicas com números racionais.

Denominamos número racional o quociente de dois números inteiros (onde divisor tem que ser diferente de zero), ou seja, todo número que pode ser colocado na forma fracionária, em que o numerador e denominador são números inteiros.(o numerador aparece na parte superior da fração e do denominador na parte inferior.)

Representação :   O conjunto dos números racionais é representando pela letra Q => Q = {m/n : m e n em Z, n diferente de zero}

O uso dos números racionais tem algumas vantagens sobre o uso de valores decimais.

A principal vantagem é a precisão. Com valores decimais, os erros de arredondamento podem ocorrer em pequenas quantidades, devido à maneira pela qual os valores são armazenados.

Isto pode ser visto no código do exemplo abaixo. Embora o valor inicial é dividido e depois multiplicado pelo mesmo número, o resultado não é o mesmo que o valor original. Se os números racionais fossem usados este problema de arredondamento não teriam sido introduzidos.

Neste artigo vamos desenvolver uma estrutura(Struct) para tratar e manipular números racionais. A estrutura NumeroRacional poderia facilmente ser criada como uma classe. No entanto, neste caso uma estrutura é preferível visto que os números racionais exibem o comportamento de tipos de valor, em vez de tipos de referência.

Vamos portanto mostrar neste artigo como criar uma estrutura e definir seu construtor , propriedades e métodos e ainda rever alguns conceitos básicos dos números racionais.

Vamos começar com uma revisão de conceitos sobre estruturas.

O que são estruturas ?

Uma estrutura na linguagem C# é simplesmente uma composição de tipos de dados composta de elementos de outros tipos. Uma estrutura pode conter campos, métodos, constantes , construtores, propriedades, indexadores, operadores e mesmo outro tipos de estruturas.

Uma estrutura é considerado um Tipo por Valor e instâncias ou objetos de uma estrutura são criadas na memória STACK.

A seguir temos um exemplo de definição de uma estrutura no C#:

struct Clientes
{
   string nome;
   string telefone;
   string email;
}


O código declara uma estrutura do tipo
Public , por isso , pode ser colocado em qualquer lugar do arquivo , exceto dentro de funções ou rotinas.


Se a estrutura for
Private podemos colocar o código dentro de uma classe ou módulo.
 

Feito isto é só usar a estrutura e criar um objeto do tipo Clientes em qualquer lugar do nosso código e atribuir as informações : 

Clientes cliente;
cliente.nome = "Macoratti";
cliente.email = "macoratti@yahoo.com";

Observe que estamos criando uma instância da estrutura sem usar o operador new mas também podemos usar este operador:

Clientes cliente = new Clientes();
cliente.nome = "Macoratti";
cliente.email = "macoratti@yahoo.com";

Principais características de uma estrutura (struct):

Criando o projeto

Abra o Visual C# 2010 Express Edition e no menu File clique em New Project e escolha o template WIndows Forms Application com o nome TratandoNumerosRacionais e clique em OK;

A seguir no menu Project clique em Add New Item e selecione o template Class e informe o nome NumeroRacional.cs;

Vamos iniciar o código definido uma estrutura com o nome NumeroRacional:

struct NumeroRacional
{ }

Vamos precisar criar duas propriedades na estrutura NumeroRacional. As propriedades são valores inteiros que representam o numerador e o denominador da fração do numero racional. Cada uma vai ser uma propriedade somente-leitura(read-only) de forma que a definição desses valores estará restrito ao construtor da estrutura.

Abaixo temos o código que cria as propriedades Numerador e Denominador somente-leitura. (note que definimos somente o get e não o set)

struct NumeroRacional
{
        private int _numerador;
        private int _denominador;

        public int Numerador
        {
            get { return _numerador; }
        }

        public int Denominador
        {
            get { return _denominador; }
        }
}

Vamos agora definir um construtor que será responsável por definir os valores do numerador e denominador. Para isso adicione o seguinte código na estrutura:

   public NumeroRacional(int numerador, int denominador)
    {
            if (denominador == 0)
                throw new ArgumentException("O denominador não pode ser igula a zero.");
 
            if (denominador < 0)
            {
                numerador *= -1;
                denominador *= -1;
            }
 
            _numerador = numerador;
            _denominador = denominador;
     
            ReducaoMenorTermo();
   }

O construtor faz o seguinte:

Um único número racional pode ser expresso através de muitas frações diferentes. Por exemplo, uma metade (½) pode ser escrita com uma numerador de dois e um denominador de quatro, ou como 4/8 ou 5/10. Cada uma destas representações é válida, mas geralmente é melhor exibir uma fração usando os seus menores termos. Isto significa que o numerador e denominador devem ser os menores números possíveis para o número racional.

Para reduzir uma fração a seus menores termos, o maior divisor comum (MDC) do numerador e denominador deve ser encontrado. Este é o maior número pelo qual ambas as partes da fração pode ser dividido, embora mantendo dois números inteiros. O método ReducaoMenorTermo chama um método adicional para determinar o MDC e divide o numerador e o denominador por este valor.

Inclua o método ReducaoMenorTermo na estrutura com o seguinte código:

 private void ReducaoMenorTermo()
 {
             int maiorDivisorComun = NumeroRacional.Get_MDC(_numerador, _denominador);
            _numerador /= maiorDivisorComun;
            _denominador /= maiorDivisorComun;
}

O MDC de dois valores pode ser determinado usando o algoritmo euclidiano. Este algoritmo segue um processo iterativo. O primeiro valor é dividido pelo segundo valor e o resto é determinado. Se o resto é zero, o MDC é o primeiro valor. Se o resto é diferente de zero, o processo é repetido usando o termo segundo e o novo resto é calculado. (Para mais detalhes sobre esse assunto veja o link: Euclidean Algorithm.)

Para implementar o algoritmo, adicione método Get_MDC chamando-o iterativamente até que o máximo divisor comum seja determinado.

 private static int Get_MDC(int termo1, int termo2)
 {
            if (termo2 == 0)
                return termo1;
            else
                return Get_MDC(termo2, termo1 % termo2);
 }

Assim quando formos criar um novo número racional, o construtor já faz a redução da fração ao seu menor termo usando os dois métodos adicionados.

Definindo as operações com Números Racionais

Agora que a estrutura do número racional é capaz de tratar uma fração em seus menores termos, podemos adicionar as funções que irão realizar as operações aritméticas de adição, subtração, multiplicação e divisão.

Para conseguir isso, vamos sobrecarregar os operadores de +, -, * e / com novas versões que fornecem a funcionalidade correta para adição, subtração, multiplicação e divisão de frações. O primeiro a ser criado é o operador de adição.

Para adicionar dois números racionais juntos, definimos o seguinte código usando o operador sobrecarregado +:

 public static NumeroRacional operator +(NumeroRacional r1, NumeroRacional r2)
 {
     return new NumeroRacional((r1.Numerador * r2.Denominador) + (r2.Numerador * r1.Denominador), r1.Denominador * r2.Denominador);
 }

Vamos repetir o processo desta vez sobrecarregando o operador - que será usado para subtração de dois números racionais:

  public static NumeroRacional operator -(NumeroRacional r1, NumeroRacional r2)
        {
            return new NumeroRacional((r1.Numerador * r2.Denominador)
                - (r2.Numerador * r1.Denominador), r1.Denominador * r2.Denominador);
        }

Fazendo a mesma coisa para o operador * que representa a multiplicação de dois números racionais:

  public static NumeroRacional operator *(NumeroRacional r1, NumeroRacional r2)
        {
            return new NumeroRacional(r1.Numerador * r2.Numerador, r1.Denominador * r2.Denominador);
        }

E por fim definimos para a sobrecarga do operador / que representa a divisão de dois números racionais:


        public static NumeroRacional operator /(NumeroRacional r1, NumeroRacional r2)
        {
            return new NumeroRacional(r1.Numerador * r2.Denominador, r1.Denominador * r2.Numerador);
        }

Definindo a interface com o usuário e testando a estrutura NumeroRacional

No formulário form1.cs do projeto vamos definir a interface conforme mostra a figura abaixo:

Após isso defina o seguinte código no formulário:

using System;
using System.Windows.Forms;

namespace TratandoNumerosRacionais
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCalcular_Click(object sender, EventArgs e)
        {
            int nr1, dr1, nr2, dr2;
     
      //define a primeira fração
            nr1 = Convert.ToInt16(txt_num_r1.Text);
            dr1 = Convert.ToInt16(txt_den_r1.Text);
          
 //defina a segunda fração
            nr2 = Convert.ToInt16(txt_num_r2.Text);
            dr2 = Convert.ToInt16(txt_den_r2.Text);

    
       //cria os dois números racionais
            NumeroRacional r1 = new NumeroRacional(nr1, dr1);
            NumeroRacional r2 = new NumeroRacional(nr2, dr2);

       
    //fazendo os calculos
            NumeroRacional Adicao = r1 + r2;
            NumeroRacional Subtracao = r1 - r2;
            NumeroRacional Multiplicacao = r1 * r2;
            NumeroRacional Divisao = r1 / r2;

     
      //atribuindo os numeradores e denominadores
            //adicao

            txt_num_add.Text = Adicao.Numerador.ToString();
            txt_den_add.Text = Adicao.Denominador.ToString();
       
    //subtracao
            txt_num_sub.Text = Subtracao.Numerador.ToString();
            txt_den_sub.Text = Subtracao.Denominador.ToString();
        
   //multiplicacao
            txt_num_mul.Text = Multiplicacao.Numerador.ToString();
            txt_den_mul.Text = Multiplicacao.Denominador.ToString();
      
     //divisao
            txt_num_div.Text = Divisao.Numerador.ToString();
            txt_den_div.Text = Divisao.Denominador.ToString();
        }

        private void btnSair_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

No evento Click do botão de comando estamos definindo os numeradores e denominadores das duas frações e estamos usando o construtor e os métodos da estrutura NumeroRacional para realizar as operações de adição, subtração, multiplicação e divisão de dois números racionais expressos como frações.

Eu não estou realizando nenhuma validação no formulário por pura preguiça mas é recomendável que isso seja feito.

Executando o projeto e definindo duas frações quaisquer obtemos o seguinte resultado:

Pegue o projeto completo aqui: TratandoNumerosRacionais.zip

1Ts 1:6 E vós vos tornastes imitadores nossos e do Senhor, tendo recebido a palavra em muita tribulação, com gozo do Espírito Santo.

1Ts 1:7 De sorte que vos tornastes modelo para todos os crentes na Macedônia e na Acaia.

1Ts 1:8 Porque, partindo de vós fez-se ouvir a palavra do Senhor, não somente na Macedônia e na Acaia, mas também em todos os lugares a vossa fé para com Deus se divulgou, de tal maneira que não temos necessidade de falar coisa alguma;

1Ts 1:9 porque eles mesmos anunciam de nós qual a entrada que tivemos entre vós, e como vos convertestes dos ídolos a Deus, para servirdes ao Deus vivo e verdadeiro,

1Ts 1:10 e esperardes dos céus a seu Filho, a quem ele ressuscitou dentre os mortos, a saber, Jesus, que nos livra da ira vindoura.

Referências:


José Carlos Macoratti