MAC 115 - Bacharelado em Física/2001 - diurno

Introdução a Computação para Ciências Exatas e Tecnologia

Terceiro Exercício-Programa (EP3)

Entrega: 26 de novembro

Este exercício-programa pode ser feito em dupla. O objetivo dele é a construção de uma calculadora simplificada capaz de manipular números inteiros de tamanho arbitrário. Para isso, os números não podem ser armazenados em variáveis do tipo int ou mesmo long int, já que estas armazenam valores de magnitude limitada. Uma técnica é utilizar para cada número um vetor que contenha em cada componente um dígito do número.

Detalhes sobre a implementação

  1. O número máximo de dígitos deve ser fixado através de uma constante, por exemplo, 60. Para isso, coloque no início do seu programa

    #define MAX_DIGITOS 60

  2. O sinal do número deve ser guardado na posição zero do vetor. Considere a seguinte convenção: se o número é positivo, coloque 0 (zero) nessa posição; caso contrário coloque 1 (um). Por exemplo, o número -321938492382734 será armazenado em um vetor como

    1 0 0 ... 0 0 3 2 1 9 3 8 4 9 2 3 8 2 7 3 4

    Note que o número de casas de um vetor que armazena um número deve ser MAX_DIGITOS+1, devido à casa reservada para o sinal.

  3. A entrada deverá ser feita a partir de um arquivo texto e não do teclado. Abaixo explicamos como fazer isso.

Funções que você deve implementar

Neste exercício-programa, você deve implementar obrigatoriamente as seguintes funções:
  1. void zera (int numero[]);
    Preenche o vetor numero com zeros.
  2. int soma (int num1[], int num2[], int res[]);
    Soma o vetor num1 ao vetor num2 (considere que ambos os números sejam positivos ou ambos sejam negativos) e armazena o resultado no vetor res. No caso de ser detectado overflow (ocorreu "vai um" quando da soma dos dígitos mais significativos) a função deve devolver valor 1 (um). Caso contrário, a função deve devolver valor 0 (zero).
  3. void subtrai (int num1[], int num2, int res[]);
    Subtrai o vetor num1 do vetor num2, ou seja, calcula num1 - num2 (considere que ambos os números sejam positivos ou ambos sejam negativos e que o módúlo de num1 seja maior ou igual ao módulo de num2) e armazena o resultado no vetor res.
  4. int multiplica (int num1[], int num2[], int res[]);
    Multiplica o vetor num1 pelo vetor num2 e armazena o resultado no vetor res. Para fazer a multiplicação, utilize o algoritmo usual de multiplicar dois números (é claro que você terá que usar a função soma). No caso de ser detectado overflow (ocorreu "vai um" quando da soma dos dígitos mais significativos) a função deve devolver valor 1 (um). Caso contrário, a função deve devolver valor 0 (zero).
  5. int divide (int num1[], int num2[], int quoc[], int resto[]);
    Divide o vetor num1 pelo vetor num2 e armazena o quociente no vetor quoc e o resto no vetor resto. Para fazer a divisão, faça subtrações sucessivas utilizando a função subtrai. No caso do vetor num2 ser zero, a função deve devolver valor 1 (um). Caso contrário, a função deve devolver valor 0 (zero). Sua função divide deve funcionar como o / e o % do C quando num1 ou num2 são negativos.
  6. int maior_igual (int num1[], int num2[]);
    Essa função devolve 1 (um) se num1 >= num2 e 0 (zero) caso contrário.
  7. int nulo (int num[]);
    Essa função devolve 1 (um) se num = 0 e 0 (zero) caso contrário.
  8. void copia_num (int num1[], int num2[]);
    Copia o vetor num2 em num1.
  9. int incrementa (int numero[]);
    Incrementa de 1 (um) o valor armazenado no vetor numero. Devolve 1 (um) caso haja overflow e 0 (zero) caso contrário.

Leitura de arquivo

Para ler os dados de entrada de um arquivo, no seu programa, deve haver a declaração de uma variável de um tipo especial, conforme o exemplo abaixo.

  FILE *arq;

A variável arq deve ser inicializada no início do seu programa através do seguinte trecho de código.

  if ((arq = fopen("entrada.txt", "r")) == NULL) { 
    printf("Erro na abertura do arquivo de entrada!\n"); 
    return 1; 
  }

onde entrada.txt é o nome do arquivo de onde serão lidos os dados. Depois disso, a leitura do arquivo entrada.txt pode ser feita de maneira semelhante à leitura do teclado. A única diferença é que deve-se usar a função fscanf da biblioteca stdio.h no lugar da função scanf. A função fscanf tem um parâmetro a mais que a scanf: o primeiro parâmetro, que indica o arquivo de onde quer-se fazer a leitura. Assim, por exemplo, o seguinte trecho de programa (depois das declarações acima) lê uma seqüência de 20 números reais do arquivo entrada.txt e a guarda em um vetor v:

  for (i=0; i<20; i++)
    fscanf(arq, "%lf", &v[i]);
No caso deste exercício-programa, o arquivo de entrada deve ser lido caracter a caracter (utilizando-se o formato de leitura "%c"), pois os números são muito grandes para serem lidos com os formatos numéricos do C. O seu programa deve converter os caracteres lidos para números.

Formato do arquivo de entrada

O arquivo de entrada deve conter, em cada linha, os dados para uma operação da calculadora, ou seja, cada linha conterá um número inteiro (precedido ou não de sinal), um operador, dentre cinco possíveis, e um outro número inteiro (precedido ou não de sinal).

Os operadores permitidos são: + (adição), - (subtração), * (multiplicação), / (divisão inteira) e % (resto da divisão inteira).

Aqui você encontra um exemplo de arquivo de entrada. Teste o seu programa com esse arquivo!

No seu programa, para detectar se todas as linhas do arquivo de entrada já foram lidas, use a função feof, que recebe como parâmetro o indicador de um arquivo e devolve 1 (true) se não há mais nada para ser lido no arquivo de entrada e 0 (false) se ainda há algo no arquivo para ser lido. Assim, o seu programa deverá conter uma repetição do seguinte tipo no programa principal:

  while (!feof(arq)) {
    /* processamento de uma linha do arquivo */
    ...
  }
Cuidado com linhas em branco no fim do seu arquivo de entrada. Elas podem fazer diferença no seu programa! Abaixo falamos mais sobre isso.

Criação de um arquivo de entrada

Para criar um arquivo de entrada, você pode usar o próprio lcc. Para tanto, basta escolher a opção para abrir um novo arquivo (open new file), digitar os dados e guardar o arquivo com o nome entrada.txt (isto é, depois de digitar o arquivo, escolha a opção save as e escolha como nome do arquivo entrada.txt).

Certifique-se que o seu arquivo não contém linhas em branco no final, pois isso pode fazer diferença no seu programa.

Funções para entrada e saída

As seguintes funções de entrada e saída devem ser implementadas:
  1. char le_numero (FILE *arq, int numero[]);
    Lê um número do arquivo arq e guarda no vetor numero. A função devolve o último caractere lido, ou o caractere '#' se houver algum erro na leitura.
  2. void imprime_numero (int numero[]);
    Imprime o número armazenado no vetor numero.

O que o seu programa deve fazer

Para cada linha do arquivo de entrada, seu programa deve imprimir uma linha com os dados daquela linha do arquivo e o resultado da operação correspondente à linha. Caso haja overflow ou divisão por zero ou algum outro erro na leitura da linha, o seu programa deve imprimir uma mensagem de erro.

Assim, a saída do seu programa para o arquivo de entrada acima deverá ser:

Operacao 1: 999999 + 1 = 1000000
Operacao 2: -999999 + -1 = -1000000
Operacao 3: -100000 - 1 = -100001
Operacao 4: 100000 - 1 = 99999
Operacao 5: -999999 - 1 = -1000000
Operacao 6: 999999999999999999999999999999999999999999999999999999999999 + 3
   Overflow!!
Operacao 7: 123456789123456789 + 123456789 = 123456789246913578
Operacao 8: 123456789123456789 - 123456789 = 123456789000000000
Operacao 9: 98765432 - 123456789 = -24691357
Operacao 10: 1234567 * 987654321 = 1219325432114007
Operacao 11: -23456789 * 987654 = -23167191483006
Operacao 12: 987654321987654321987654321987654321987654321987654321 * 987654321
   Overflow!!
Operacao 13: 2222 / 2 = 1111
Operacao 14: 2222 % 2 = 0
Operacao 15: 1867 % 0
   Divisao por zero!
Operacao 16: 987654321 / 123456789 = 8
Operacao 17: 87654321 / -23456789 = -3
Operacao 18: 324567 / 0
   Divisao por zero!
Operacao 19: 987654321 % 123456789 = 9

Sugestões de divisão e estratégia de trabalho

Este programa pode ser feito em dupla. Não deixe que seu parceiro na dupla faça o programa todo sozinho! Isso só vai prejudicar o seu rendimento na última prova.

Aqui vai uma sugestão estratégica. Primeiro, escrevam e testem as funções sem se preocuparem com arquivos: leiam números do teclado, como uma seqüência de caracteres, e imprimam o resultado de cada uma das operações na tela. Depois que as funções das operações estiverem funcionando, passem à leitura do arquivo.

Agora uma sugestão de divisão de trabalho. Um dos dois participantes da dupla pode ficar encarregado de escrever e testar as funções soma, zera, multiplicação e imprime_numero, bem como a parte do programa principal que trata das operações de adição e multiplicação. O outro participante pode ficar com as funções subtrai, copia_num, nulo, maior_igual, incrementa e divide, bem como com a parte do programa principal que trata das operações de subtração e divisão. Os dois participantes da dupla podem escrever juntos a parte de leitura do arquivo, que sempre é uma aventura. E podem depurar (tirar os erros) do programa completo juntos.

Bom divertimento!!!!

Observações