pós-graduação em ciência da computação · pessoas que conviveram comigo durante o curso de...

106
Pós-Graduação em Ciência da Computação “UMA BIBLIOTECA INTERVALAR BASEADA EM PROCESSAMENTO DE CARACTERES” POR Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite Dissertação de Mestrado Universidade Federal de Pernambuco [email protected] www.cin.ufpe.br/~posgraduacao RECIFE, Agosto/2007

Upload: others

Post on 11-Jun-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

Pós-Graduação em Ciência da Computação

“UMA BIBLIOTECA INTERVALAR BASEADA EM

PROCESSAMENTO DE CARACTERES”

POR

Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite Ivan Oliveira Bernardo Leite

Dissertação de Mestrado

Universidade Federal de Pernambuco

[email protected]

www.cin.ufpe.br/~posgraduacao

RECIFE, Agosto/2007

Page 2: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos
Page 3: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

UNIVERSIDADE FEDERAL DE PERNAMBUCO

CENTRO DE INFORMÁTICA

PÓS-GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO

IVAN OLIVEIRA BERNARDO LEITE

“UMA BIBLIOTECA INTERVALAR BASEADA EM PROCESSAMENTO DE STRINGS”

ESTE TRABALHO FOI APRESENTADO À PÓS-GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO DO CENTRO DE INFORMÁTICA DA UNIVERSIDADE FEDERAL DE PERNAMBUCO COMO REQUISITO PARCIAL PARA OBTENÇÃO DO GRAU DE MESTRE EM CIÊNCIA DA COMPUTAÇÃO.

ORIENTADOR(A): MARCÍLIA ANDRADE CAMPOS

RECIFE, AGOSTO/2007

Page 4: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

Leite, Ivan Oliveira Bernardo Uma biblioteca intervalar baseada em

processamento de strings / Ivan Oliveira Bernardo Leite. – Recife: O Autor, 2007. xi, 93 p. : il., fig., tab., quadro. Dissertação (mestrado) – Universidade Federal de Pernambuco. CIn. Ciência da Computação, 2007. Inclui bibliografia. 1. Análise intervalar. 2. Computação científica. 3. Processamento de strings. 4. JAVA I. Título. 511.42 CDD (22.ed.) MEI2008-008

Page 5: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

DEDICATÓRIA

Dedico esta dissertação em

especial a meu pai, minha mãe e meus

irmãos, minha orientadora Marcília

Andrade Campos e minha analista

Patrícia Pires.

Page 6: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos
Page 7: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

AGRADECIMENTOS

Agradeço a meus pais por terem, em momentos de dificuldade, priorizado a

educação e formação dos seus filhos e incentivando a todo instante a conclusão

deste projeto.

Professora Marcília Andrade Campos, sem ela este trabalho não teria sido

concluído, sempre acreditou, motivou e lutou por este trabalho. Um agradecimento

especial a Neíldes Paiva Vieira Pedrosa, minha gerente de projetos no CESAR, por

ser muito paciente, compreensiva e flexível para a realização do mestrado. Ao

Professor Antônio Carlos Monteiro do departamento de Matemática da UFPE pela

ajuda com algoritmos envolvendo números inteiros.

Aos amigos Edmo Ribeiro, Vanilson Burgos, Jorge Mascena, Taíssa Rocha,

Taciana Amorim, Eduardo Dominoni e Isabel Wanderley que foram espelhos e

pessoas que conviveram comigo durante o curso de pós-graduação, interagindo,

cooperando, dividindo angustias e incentivando.

A todos os meus amigos que me dão a alegria e o prazer da convivência. Por

entenderem minha ausência durante a conclusão deste trabalho.

Page 8: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

RESUMO

Java é uma linguagem multiplataforma amplamente utilizada nos dias atuais.

Sistemas cliente-servidor, aplicações embarcadas e desktop são desenvolvidos a

partir da facilidade que Java oferece. A comunidade que utiliza Java cria suas

próprias bibliotecas e as disponibiliza na Web para que todos possam compartilhar de

suas facilidades. Bibliotecas para criar servidores HTTP, processar imagens, conectar

banco de dados fazem parte do núcleo da linguagem.

O objetivo deste trabalho é desenvolver uma biblioteca em Java para

representar um novo sistema numérico que utiliza a matemática intervalar e a

aritmética de exatidão máxima. As operações aritméticas são realizadas através de

processamento de Strings.

As principais conclusões deste trabalho foram: (i) a representação de números

racionais processados através de strings permite que se trabalhe com precisão e

exatidão superiores à Java-XSC e o Maple Intervalar, sendo o custo desta exatidão

refletido no tempo das operações; (ii) para qualquer uma das operações, repetidas

1000 vezes, seu tempo total de processamento é menor do que 1 segundo.

Palavras-Chaves: Matemática intervalar, Computação científica, Java,

Processamento de Strings..

Page 9: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

ABSTRACT

Java is a multiplatform language widely used nowadays. Client-server systems,

embedded systems and desktop applications have been developed from java

facilities. Java community creates his own libraries and publishes it on the web to

share its functionalities with other members. Libraries to create HTTP Servers,

process Images, Database connection are part of Java core.

The goal of this work is to develop a Java library to represent a new numerical

system that uses Interval mathematic and high precision arithmetic all arithmetic

operations shall be executed using string processing.

The main conclusions are: the rational number representation processed using

Strings enables exactness and precision greater than Java-XSC and Interval Maple,

being the exactness cost reflected in operations time. For any operation, 1000 times

repeated, process total time is least than one second.

Keywords: Intervals, Computational Mathematics, Java, String Processing.

Page 10: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

LISTA DE FIGURAS

Figura 1. Seqüência de passos para processamento numérico ............................... 7

Figura 2. Distribuição dos números reais na reta ................................................... 10

Figura 3. Representação da primitiva float ............................................................. 13

Figura 4. Representação da primitiva double ......................................................... 13

Figura 5. Espaços da Computação Numérica ........................................................ 20

Figura 6. Representação gráfica do espaço dos Intervalos.................................... 24

Figura 7. Estrutura hierárquica da representação numérica................................... 25

Figura 8. Diagrama UML das Classes do Sistema. ................................................ 30

Figura 9. Diagrama UML da Classe Number.......................................................... 33

Figura 10.Somador (Modelo do full adder) ............................................................. 40

Figura 11. Resolução do carry e do caractere resultante por iteração. .................. 43

Figura 12. Elementos utilizados no algoritmo da divisão ........................................ 46

Figura 13. Definição mdc........................................................................................ 51

Figura 14. Definição mmc....................................................................................... 52

Figura 15. Dividendo da Iteração igual a Zero........................................................ 53

Figura 16 - Identificação da Periodicidade de uma Divisão .................................... 54

Figura 17. Colocando sob a mesma base de denominadores................................ 63

Figura 18. Exemplo do efeito do método que elimina o expoente e o sinal do

denominador........................................................................................................... 63

Figura 19. Ajuste de um tipo Number com denominador null para a realização de

uma adição ou subtração com outro tipo Number com denominador não-nulo...... 65

Figura 20 - Operação de normalização que iguala os expoentes de dois números 67

Page 11: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

SUMÁRIO 1. Introdução ................................................................................................................ 1

1.2 Objetivos............................................................................................................. 4

2. Fundamentos ........................................................................................................... 6

2.1 Representação dos Reais nos Computadores ................................................... 7

2.1.1 Sistema de Ponto-flutuante .......................................................................... 8

2.2 Ponto-flutuante em Java................................................................................... 11

2.3 Aritmética intervalar .......................................................................................... 15

2.5 Aritmética de Exatidão máxima: ....................................................................... 20

2.5.1 Semimorfismo ............................................................................................ 22

2.6 Matemática Intervalar ....................................................................................... 23

2.6.1 Intervalo de Números Reais....................................................................... 23

2.6.2 Conjunto de Intervalos ............................................................................... 24

2.6.3 Operações aritméticas ............................................................................... 25

2.6.3.1 Adição Intervalar.................................................................................. 25

2.6.3.2 Pseudo-Inverso Aditivo Intervalar ........................................................ 25

2.6.3.3 Subtração Intervalar ............................................................................ 25

2.6.3.4 Multiplicação Intervalar ........................................................................ 25

2.6.3.5 Pseudo-Inverso Multiplicativo Intervalar .............................................. 25

2.6.3.6 Divisão Intervalar ................................................................................. 26

2.6.4 Operações Entre Conjuntos ....................................................................... 26

2.6.4.1 Interseção de Intervalos ...................................................................... 26

2.6.4.2 União de Intervalos.............................................................................. 26

2.6.4.3 União Convexa de Intervalos............................................................... 26

2.6.5 Outras operações....................................................................................... 26

2.6.5.1 Distância entre Intervalos .................................................................... 26

2.6.5.2 Diâmetro de um Intervalo .................................................................... 26

2.6.5.3 Ponto Médio de um Intervalo............................................................... 27

2.6.5.4 Valor Absoluto de um Intervalo............................................................ 27

2.7. Java-XSC ........................................................................................................ 28

3 Intervalos de Strings Numéricos ............................................................................ 29

Page 12: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

3.1 Arquitetura ........................................................................................................ 30

3.2 Processamento de Strings................................................................................ 31

3.3 Processamento de Strings em Java ................................................................. 32

3.4 Definição do Tipo Number ................................................................................ 33

3.5. Operações ....................................................................................................... 38

3.5.1 Comparação entre Strings ......................................................................... 38

3.5.2 Operações Aritméticas ............................................................................... 40

3.5.2.1 Adição Natural ..................................................................................... 40

3.5.2.2 Subtração Natural................................................................................ 41

3.5.2.3 Multiplicação Natural ........................................................................... 43

3.5.2.4 Divisão Natural .................................................................................... 46

3.5.2.5 Máximo Divisor Comum (MDC) ........................................................... 51

3.5.2.6 Mínimo Multiplo Comum (MMC) .......................................................... 52

3.5.2.7 Divisão Racional Desconsiderando o Sinal ......................................... 52

3.5.3 Métodos Aritméticos Públicos do Tipo Number.......................................... 60

3.5.3.1 Adição e Subtração Racional .............................................................. 61

3.5.3.2 Multiplicação Racional ......................................................................... 67

3.5.3.3 Divisão Racional.................................................................................. 68

3.5.4 Comparação entre tipos Number ............................................................... 69

4. Resultados ............................................................................................................. 71

4.1 Operações aritméticas com Strings .................................................................. 71

4.1.1 Adição ........................................................................................................ 71

4.1.2 Subtração................................................................................................... 72

4.1.3 Multiplicação .............................................................................................. 73

4.1.4 Divisão Inteira ............................................................................................ 73

4.1.5 Resto da divisão inteira .............................................................................. 74

4.1.6 máximo divisor comum .............................................................................. 74

4.1.7 mínimo multiplo comum ............................................................................. 75

4.2 Operações aritméticas com o Tipo Number ..................................................... 75

4.2.1 Adição ........................................................................................................ 75

4.2.2 Subtração................................................................................................... 76

4.2.3 Multiplicação .............................................................................................. 76

Page 13: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

4.2.4 Divisão ....................................................................................................... 76

4.2.5 Inverso multiplicativo.................................................................................. 77

4.3 Operações Intervalares .................................................................................... 78

4.3.1 Adição ........................................................................................................ 78

4.3.2 Subtração................................................................................................... 79

4.3.3 MultiplicaçÃo .............................................................................................. 79

4.3.4 Inverso Multiplicativo.................................................................................. 80

4.3.5 Divisão ....................................................................................................... 81

4.3.6 Intersecção................................................................................................. 82

4.3.7 União.......................................................................................................... 83

4.3.8 Distância .................................................................................................... 83

4.3.9 Diâmetro..................................................................................................... 84

4.3.10 Ponto Médio ............................................................................................. 84

4.3.11 Valor Absoluto.......................................................................................... 85

4.4 comparação entre resultados ........................................................................... 85

4.5 Comparação do Desempenho.......................................................................... 87

5 Conclusões e trabalhos futuros ............................................................................... 89

5.1 Trabalhos Futuros............................................................................................. 89

Referências ................................................................................................................ 91

Page 14: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

1

1. INTRODUÇÃO

Erros numéricos à primeira vista podem errôneamente ter suas conseqüências

restringidas a domínios simplistas, sem grandes repercussões ou implicações no dia-

a-dia das pessoas. O mundo e as sociedades em que vivemos hoje dependem de

processos rápidos, ferrementas que auxiliem tomadas de decisão, sistemas de

informação, entre outros sistemas que utilizam computadores que realizam cálculos

através de sistemas numéricos como o de ponto-fixo ou ponto-flutuante. Estes

sistemas são limitados e apresentam problemas e conseqüências quando utilizados.

Por exemplo, em 1991 [VUIK] durante a guerra do golfo uma bateria de

mísseis patriot americanos falhou ao interceptar um missil Iraquiano scud. O Míssil

Iraquiano terminou matando 28 pessoas que estavam num acampamento militar. Um

relatorio do Incidente revelou que um problema de software levou a falha. A causa foi

um cálculo impreciso do tempo, devido a erros de aritmética computacional. O tempo

do clock do sistema era medido em decimos de segundos sendo multiplicado por 1/10

para que o sistema trabalhaste em segundos. O cálculo era efetuado utilizando-se

registradores de 24 bits de ponto-fixo. O resultado da multiplicação era truncado

depois do 24º bit. O erro de truncamento quando multiplicado por um grande número

provocava um erro significativo de aproximadamente 0.000000095. O míssil patriot

ficou armazenado cerca de 100 horas. Multiplicando-se o erro gerado pela quantidade

de decimos de segundos em 100 horas temos 0.000000095×100×60×60×10=0.34s.

Sabendo-se que um Missil scud viaja a aproximadamente 1676 metros por segundo,

Em 0.34s o míssil iraquiano percorre mais de meio Kilometro o suficiente para sair do

alcance de rastreamento do míssil patriot.

Em junho de 1996, o Ariane 5 [VUIK], foguete da companhia espacial européia

explodiu 40 segundos após o seu lançamento, por ter perdido controle e altitude. Era

sua primeira viagem e seu projeto tinha custado $7 bilhões. Investigações concluiram

que a causa da explosão aconteceu por erro no software que controlava o sistema de

referência inercial. Especificamente um número de ponto-flutuante de 64 bits relativo

a velocidade horizontal do foguete em relação a plataforma de lançamento, foi

convertido em um inteiro com sinal de 16 bits. O número era maior que 32768, o

maior inteiro armazenável em um registrador de 16 bits.

Page 15: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

2

Erros numéricos também afetaram as eleições na Alemanha, problema só

descoberto em 1992. Existia uma cláusula que afirmava que um partido só poderia ter

cadeiras no parlamento se atingisse uma votação mínima de 5%. Se não atigiste este

valor os votos eram perdidos. Num domingo o partido verde teria atingido exatamente

5% dos votos Depois dos resultados da eleição terem sido divulgados, descobriu-se

que o partido verde tinha obtido apenas 4,97% dos votos. O programa que calculava

as percentagens utilizava apenas uma casa decimal de precisão depois da vírgula e

tinha arredondado para cima. Este sistema vinha sendo usado há muitos anos e

ninguem tinha percebido este erro. Os votos foram recontados e o partido verde

perdeu suas cadeiras no parlamento.

No Brasil, a Embratel teve problemas no seu sistema de contabilidade

implementado em Java. Uma falta de R$100.000 no faturamento mensal, gerando

prejuízos para empresa devido a erros de arredondamento.

À medida que a tecnologia de fabricação de hardware avança, a velocidade

dos processadores aumenta permitindo um poder de processamento maior. Até que

ponto pode-se abrir mão da precisão e exatidão numérica em troca de uma melhor

performance de processamento? Lembrando que o erro na matemática abstrata é

encarado como uma distância entre o valor real e sua aproximação e que num

cenário do mundo real significa desperdício ou perda, em algum momento teremos

que nos preocupar com a precisão, uma vez que a velocidade de processamento

futuramente em alguns contextos não será mais um fator determinante.

No cenário acadêmico atual de desenvolvimento de software no Brasil, a

linguagem de programação Java [Sun Microsystems] tem grande influência por ter

características como portabilidade, segurança e uma infinidade de bibliotecas para

atender às mais diversas necessidades. A deficiência que esta linguagem apresenta

no tratamento numérico é o que impulsiona a realização deste trabalho. A utilização

de Java como uma linguagem para a computação científica tem como base a

aritmética intervalar.

Java, por ser uma linguagem multiplataforma e interpretada, tem que tratar as

plataformas variadas com sistemas numéricos diversos. A solução atual para resolver

problemas de ordem numérica é utilizar o tipo “java.math.BigDecimal”, esta

abordagem, ainda assim não controla a propagação do erro e falha em algumas

operações aritméticas simples. O Exemplo 1 foi implementado utilizando-se o JDK

Page 16: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

3

1.4.2 da Sun [Sun Microsystems]. Neste exemplo 10 iterações adicionam a razão de

0,1 à variável “d”. Todos os algoritmos estarão coloridos em vermelho e seus

resultados na saída do console estarão coloridos em azul.

Exemplo 1. Erro na adição entre tipos double

private static void main(String args[]) {

double d = 0.0;

for (int i = 0 ; i < 10; i++) {

d += 0.1;

}

System.out.println("Resultado = " + d);

}

Resultado = 0,9999999999999999.

Analisando o resultado da execução do trecho de código do Exemplo 1 acima,

escrito em linguagem Java, o resultado esperado deveria ser 1, mas o valor obtido é

0,9999999999999999.

Como é de conhecimento público, o sistema de ponto-flutuante representa

apenas um subconjunto do conjunto dos números reais. À medida que operações

aritméticas são realizadas o erro inerente a esta falha na representação dos números

reais é propagado. A aritmética intervalar surge então como a técnica para controlar o

erro máximo produzido por uma seqüência qualquer de cálculos.

Sistemas computacionais que interagem com ferramentas de medição,

amplamente utilizadas em Engenharia, Química e Física, sofrem da incerteza

intrínseca do ato de medir. Medidas sujeitas a erro podem ser substituídas por um

intervalo que contenha os limites da incerteza.

Sistemas computacionais numéricos necessitam, em algum momento, de

cálculo de fórmulas, somatórios, taxa de juros, percentagens, realizar

arredondamentos, truncamentos, etc. Em sistemas computacionais científicos esta

necessidade é crítica, como em aplicações espaciais, bancárias, sistemas de

segurança ou que tratem de grandezas numéricas de ordens elevadas. Nos

bancários, por exemplo, qualquer erro de natureza numérica pode trazer prejuízo

para os usuários e mantenedores do sistema.

Page 17: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

4

1.2 OBJETIVOS Sistemas computacionais, tanto antigos como os mais recentes, são incapazes

de representar todos os números reais. A representação dos números reais em

computação é realizada através dos números de ponto-flutuante [CAMPOS e

FIGUEIREDO, 2005], que provê uma representação binária capaz de armazenar uma

quantidade finita de valores.

O objetivo deste trabalho é desenvolver uma biblioteca em Java para

representar um novo sistema numérico que utiliza a matemática intervalar e a

aritmética de exatidão máxima. As operações aritméticas são realizadas através de

processamento de Strings. No modelo convencional, as operações aritméticas são

realizadas em registradores com uma quantidade finita de bits ou emuladas em

linguagem de programação (como Java) com tipos de dados com quantidades fixas

de casas decimais.

1. A nova abordagem proposta utiliza intervalos com limites superiores e

inferiores representados por um novo tipo que processa strings, assumindo a forma

de números racionais. Calcular processando Strings é mais custoso do que utilizar a

ULA, mas o quanto mais custoso é o que será avaliado. Será então medido o

desempenho deste sistema comparando com o sistema numérico padrão de Java,

Portanto o objetivo deste trabalho é alcançado através dos seguintes passos:

Desenvolver uma biblioteca que contemple a inclusão do tipo Intervalo e das

operações sobre esse tipo na linguagem Java, onde todas as operações serão

realizadas por processamento de caracteres. A biblioteca será estruturada e

implementada de forma modular da seguinte maneira:

• Definição do tipo Number,

• Operações com o tipo Number,

• Definição do tipo Intervalo,

• Operações com o tipo Intervalo.

Page 18: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

5

2. Comparar a performance do sistema desenvolvido neste trabalho com

operações em Java e Java-XSC [JAVA-XSC]. Validação através da comparação dos

resultados com o Maple Intervalar [MAPLE, INTPAKX] será realizada.

Esta dissertação está estruturada como descrito abaixo:

O Capítulo 2 contém os fundamentos dos sistemas de ponto-flutuante,

aritmética de exatidão máxima e dos sistemas intervalares.

O Capítulo 3 define o tipo Number como um tipo de Java e mostra como as

operações são realizadas através de processamento de caracteres, suas regras de

formação e restrições. Introduz o tipo Intervalo que utiliza o tipo Number.

O Capítulo 4 apresenta o resultado das comparações das operações com os

tipos Java nativos, comparações de exatidão com os tipos Java-XSC e o Maple

Intervalar e testes comparativos de performance com o Java-XSC.

Finalmente, o Capítulo 5 mostra as conclusões obtidas com o este trabalho,

bem como explicita trabalhos futuros que podem ser realizados a partir da primeira

versão da biblioteca desenvolvida.

Page 19: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

6

2. FUNDAMENTOS

Este capítulo apresenta os fundamentos para o desenvolvimento deste

trabalho. Portanto, serão abordados sistemas de ponto-flutuante, ponto-flutuante em

Java, matemática intervalar e aritmética de exatidão máxima.

A representação dos números reais em computadores é uma questão

importante desde os primórdios da história dos computadores. Os números reais

inicialmente eram representados no sistema numérico de ponto fixo. A passagem

para a representação em ponto-flutuante iniciou-se na década de 50 e caracterizou

uma evolução significativa na área da computação científica, principalmente pela

melhora da exatidão nos resultados de operações efetuadas em ponto-flutuante com

cada vez mais dígitos significativos na mantissa. O tamanho da mantissa era

dependente da máquina e ainda poderia ser variado, resultando os formatos

conhecidos como precisão simples, dupla precisão e precisão estendida.

A representação de um número em ponto-flutuante proporcionou muitas

vantagens, mas também introduziu algumas desvantagens com a geração do

problema do controle de erros nas computações numéricas, que muitas vezes

proporcionaram resultados totalmente errados com aparência de serem corretos, ou

seja, um procedimento correto, mas com o resultado perdendo o significado devido à

inexatidão da representação numérica e de arredondamentos aplicados nas

avaliações das operações e expressões aritméticas em ponto-flutuante.

A resolução de problemas de computador na maioria das vezes é feita através

de algoritmos. Os algoritmos numéricos são geralmente definidos e projetados no

espaço dos números reais e complexos. Os cálculos, por sua vez, são efetuados no

conjunto dos números representáveis e operáveis no computador. Este conjunto é

finito e varia de máquina para máquina.

Existem dois tipos de sistemas numéricos usados em computadores digitais, o

sistema de ponto fixo e o sistema de ponto-flutuante. Cada um deles tem seu próprio

conceito de aritmética computacional. O sistema de ponto fixo é utilizado em alguns

sistemas financeiros e comerciais. No modelo de representação de números em

ponto fixo, considera-se duas partes, uma inteira e outra fracionária. A caracterização

consiste da base numérica utilizada (b), o número de dígitos (n) e o número de dígitos

Page 20: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

7

da parte fracionária (f). Sendo representada pela terna ordenada P(b, n , f). Na seção

seguinte veremos a caracterização dos sistemas de ponto-flutuante.

2.1 REPRESENTAÇÃO DOS REAIS NOS COMPUTADORES

Nesta seção será mostrado como os números reais são representados nos

computadores, porque têm de ser representáveis e exemplos de propriedades

algébricas dos reais que são perdidas com essa representação [CAMPOS e

FIGUEIREDO, 2005].

Por último, além dos erros citados acima, o processamento numérico nos

computadores é realizado na base 2, Figura 1, portanto, adicionalmente ainda têm-se

os erros de conversão de base.

O conjunto dos números reais é um corpo ordenado completo. O fato dos reais

constituírem um corpo possibilita que nele sejam resolvidas equações do tipo ax = b,

a ≠ 0, onde a solução única é x = a-1b. Como é completo, equações do tipo x2 = 2 têm

solução. Por razões de ordem prática, os computadores, em geral, representam um

número em ponto-flutuante com uma quantidade constante de bits.

Figura 1. Seqüência de passos para processamento numérico.

No caso dos reais é necessário substituí-los por outro conjunto que os

represente, usualmente o dos números de ponto-flutuante. O problema, porém é que

o conjunto dos números de ponto-flutuante, diferentemente dos reais, não tem

Usuário digita números na base 10

Base 10

Base 2

Base 10

Base 2

Cálculos realizados na base 2

Usuário recebe resultados na base 10

Page 21: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

8

propriedades algébricas que garantam os resultados dos cálculos efetuados, além de

não existir uma bijeção entre os conjuntos, um conjunto finito representando um não

enumerável. Por exemplo, a soma de dois números de grande magnitude pode gerar

overflow, ou seja, nem sempre a soma de dois números de ponto-flutuante é um

número flutuante.

2.1.1 SISTEMA DE PONTO-FLUTUANTE

Um número de ponto-flutuante, x, é da forma :

x = m x be = d1.d2...dl x be,

onde, m é uma mantissa de comprimento l, b é a base, a qual é um inteiro

maior ou igual a 2, e e é o expoente, tal que emin ≤ e ≤ emax são números inteiros. Os

dígitos da mantissa são restritos a 1 ≤ d1 ≤ b -1 e 0 ≤ dk ≤ b -1, k =2,..., n. Porque d1

≠ 0, x é denominado um número de ponto-flutuante normalizado. Um sistema de

ponto-flutuante, F, é usualmente representado por:

F (b, l, emin, emax)

Sendo um número real X não nulo representado em F, na forma:

A representação do zero real, dos elementos de menor e maior valor absoluto,

xmin e xmax, e o número de elementos de F, são respectivamente:

0 = + 0.000…0 x bemin,

xmin = + 0.10…0 x bemin,

xmax = + 0.(b-1)(b-1)...(b-1) x bemax,

#F = 2(b-1)bl -1(emax – emin + 1) + 1.

O termo número de ponto-flutuante deve-se ao fato de que o ponto se move no

número dependendo do expoente da base. Alguns autores usam vírgula ao invés do

ponto; neste trabalho adotou-se o ponto e que é a notação usada nas máquinas

digitais.

Page 22: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

9

Exemplo 2. Seja o sistema de ponto-flutuante F = F(2, 3, -1, 2). Portanto F tem base

binária, mantissa de 3 dígitos, o menor expoente é emin = -1 e o maior expoente emax =

2, assim a excursão do expoente vai de -1 a 2 e todos os expoentes deste sistema

são {-1,0,1,2}, isto é, tem-se um total de 4 expoentes, que vem do cálculo emax – emin +

1. Para este sistema tem-se:

Representação do zero: 0 = + 0.000 x 2-1,

Maior elemento de F: xmax = + 0.111 x 22,

Menor elemento de F: -xmax = - 0.111 x 22,

Menor elemento positivo de F: xmin = + 0.100 x 2-1,

Maior elemento negativo de F: -xmin= - 0.100 x 2-1,

Número de elementos de F: #F = 33.

Exemplos de sistemas de ponto-flutuante, são dados a seguir com diferentes

bases: decimal (10), binária (2), octal (8), hexadecimal (16).

a) PDP-11 : F(2, 24, -128, 127),

b) Texas SR52 : F(10, 12, -98, 100),

c) HP41C : F(10, 10, -98, 100),

d) IBM 360/370: F(16, 6, -64, 63),

e) B6700 : F(8, 13, -51, 77),

f) UNICAC 1108 : F(2, 27, -128, 127).

O subconjunto dos números reais, R, que é representável em F são os

números não igualmente espaçados localizados na região hachurada mais o zero na

Figura 2 a seguir.

Page 23: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

10

Figura 2. Distribuição dos números reais na reta

Além da restrição ao número de elementos, uma vez que R é não-enumerável

e F é finito, propriedades algébricas que são válidas em R não são válidas em F. A

referência [HÖLBIG] traz vários exemplos, porém aqui será mostrado no Exemplo 3, a

falha na lei do corte aditiva que consiste na seguinte afirmação:

∀a, b, c ∈ R, a + b = a + c ⇒ b = c.

Será mostrado que em F,

∃a, b, c ∈ F, tais que a + b = a + c não implica b = c .

Exemplo 3. Sejam F = F (10, 4, -9, 9), a = 0.3245 x 102, b = 0.4587 x 10-3, c =

0.8764 x 10-4.

Colocando os números na potência do maior expoente,

a = 0.3245 x 102,

b = 0.00004587 x 102,

c = 0.000008764 x 102.

Somando,

a + b = 0.32454587 x 102,

a + c = 0.324508794 x 102.

Arredondando porque l = 4,

a + b = 0.3245 x 102,

0 -xmax -xmin xmin xmax

regiões de underflow

regiões de overflow

Page 24: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

11

a + c = 0.3245 x 102.

Portanto, tem-se que a + b = a + c, mas b ≠ c!

2.2 PONTO-FLUTUANTE EM JAVA

A linguagem Java oferece os seguintes elementos para representação de

ponto-flutuante:

• Tipo primitivo float.

• Tipo primitivo double.

• Wrapped Classes – Float / Double do pacote java.lang [Sun

Microsystems, 2005].

A seguir, nos Exemplos 4, 5, 6 e 7 estão erros em operações de ponto-

flutuante obtidos a partir da execução de um código fonte em Java, utilizando-se o

JDK 1.4.2 da Sun:

Exemplo 4. Subtração entre tipos double.

public class exemplo4 {

private static void main(String args[]) {

double d = 3.9-3.8;

if(d==0.1) {

System.out.println("igual");

} else {

System.out.println("diferente");

}

}

}

diferente

A resposta para Exemplo 4 deveria ser a literal igual, mas executando-se o

programa Java acima, obtém-se como resposta “diferente”, devido à operação

resultar o valor 0.10000000000000009.

Page 25: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

12

No Exemplo 5 utilizamos um laço para adicionar um tipo double com ele

mesmo quatro vezes e dividir pelo seu valor multiplicado por quatro, este processo se

repete 10 vezes:

Exemplo 5. Laço de divisões com o tipo double

public static void main(String args[]) {

double d = 0.1;

for(int i = 0; i < 10; i++) {

d = (d+d+d+d)/4*d;

}

System.out.println(“divisão==” + d);

}

divisão==0.0

O resultado desta seqüência de operações retorna na saída padrão o resultado

zero!

Costuma-se utilizar o tipo java.math.BigDecimal para contornar este tipo

de problemas com o tipo double porém o Exemplo 6 abaixo demonstra que o tipo

BigDecimal também apresenta falhas:

Exemplo 6. Adição e Subtração com o tipo java.math.BigDecimal

public static void main () {

BigDecimal f = new BigDecimal(10E29);

BigDecimal g = new BigDecimal(1);

BigDecimal h = new BigDecimal(10E28);

f = f.add(g);

f = f.subtract(h);

System.out.println("f=" + f);

}

f=900000000000000028451473981441

O resultado apresentado na saída padrão de Java para f é

900000000000000028451473981441!

Esses fatos ocorrem porque a implementação da representação de ponto-

flutuante na linguagem Java implementa de maneira parcial o padrão IEEE Standard

for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 de aritmética de

ponto-flutuante [Macaulay Institute, 2004]. Os tipos primitivos float e double da

linguagem representam a precisão simples (32 bits) e a precisão dupla (64 bits),

respectivamente, seguindo parcialmente o padrão. A seguir estão duas figuras que

ilustram a representação das primitivas da linguagem Java.

Page 26: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

13

1 bit 8 bits 23 bits

sinal expoente significante

Figura 3. Representação da primitiva float

1 bit 11 bits 52 bits

sinal expoente significante

Figura 4. Representação da primitiva double

A representação através da primitiva float oferece de seis a nove dígitos de

precisão, enquanto que a representação double dentre quinze e dezessete dígitos

de precisão [GOSLING, 1996].

Java requer que os resultados nas operações de ponto-flutuante sejam

arredondados para o número mais exato que a máquina consiga representar. Para

resultados inexatos a resposta deve ser aproximada para o valor mais próximo

representável. Esta é mais uma definição do padrão 754 IEEE conhecida como round

to nearest [GOSLING, 1996].

Operações entre números de ponto-flutuante em Java podem produzir

exceções, que são tratadas da seguinte maneira:

• Operações que produzem overflow como resultado, apresentam uma

representação de infinito como resposta;

• Operações que produzem underflow como resultado, apresentam o valor

zero como resposta;

• Operações que geram resultados matemáticos não definidos produzem

como resposta a notação NaN (Not a Number).

Uma vez que apresentamos problemas da representação de números de

ponto-flutuante na linguagem Java e detalhamos sua representação e seus

propósitos, vamos agora apresentar as discordâncias e inadequações dessa

implementação com o padrão internacionalmente reconhecido ANSI/IEEE Standard

Page 27: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

14

754-1985. A implementação de ponto-flutuante em Java falha nos seguintes aspectos

em relação ao padrão IEEE [Macaulay Institute, 2004]:

• Ausência de suporte às seguintes flags de exceções:

o Operação inválida,

o Overflow,

o Underflow,

o Divisão por zero,

o Resultados inexatos.

• Não implementação dos arredondamentos direcionados;

• Não oferecimento de suporte para os seguintes tipos de dados

próprios para aritmética intervalar de máquina:

o Tipo intervalo,

o Tipos complexos,

o Operações aritméticas intervalares,

o Matrizes intervalares,

o Operações matriciais intervalares.

Como mostrado acima, embora existam evoluções na representação numérica

para os computadores, os problemas das operações computacionais não foram

completamente resolvidos. Mesmo com o surgimento da padronização da

representação de ponto-flutuante, que podemos eleger como a primeira solução para

problema de exatidão e precisão de máquina, os erros de computação não estavam

descartados de maneira satisfatória.

Como a representação numérica de ponto-flutuante não conseguiu de maneira

satisfatória minimizar esses problemas para programas de computação científica,

surgiu a proposta de incluir a matemática intervalar como auxílio às linguagens de

computador. Dessa maneira, pelo menos é possível garantir aquela incerteza das

respostas. A definição e os propósitos da aritmética intervalar são descritos na

Page 28: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

15

próxima seção deste documento para mostrar ao leitor como este novo tipo de

informação contribui para a garantia e exatidão de rotinas computacionais científicas.

2.3 ARITMÉTICA INTERVALAR

As pesquisas em aritmética computacional estão sendo desenvolvidas desde

os anos sessenta, com o objetivo de controlar os erros computacionais e para que os

computadores suportem uma aritmética muito mais poderosa e precisa que a

aritmética empregada na maioria das linguagens modernas de programação.

A aritmética proposta por Moore [MOORE], em 1966, possibilitou um grande

desenvolvimento destas pesquisas. Esta aritmética trata com dados na forma de

intervalos numéricos e tem como objetivo automatizar a análise de erro

computacional. Serve para controlar o erro de arredondamento e para representar

dados inexatos, aproximações e erros de truncamento de procedimentos. Atualmente,

também vem sendo empregada na elaboração de algoritmos numéricos auto-

validáveis.

O uso da aritmética intervalar permite alta exatidão que é uma qualidade

necessária nos ambientes que propiciam a resolução de problemas da computação

cientifica e das engenharias. A minimização dos arredondamentos resulta em

qualidade no resultado que também é uma das características necessárias à

resolução de problemas de verificação de resultado.

A necessidade de termos linguagens de programação para estas aritméticas

computacionais com suporte à computação científica fez que surgissem, na

cooperação de institutos de pesquisas universitários e empresas (como a IBM) as

linguagens com extensões científicas, conhecidas como XSC, que é o acrônimo de

Language Extensions for Scientific Computation.

As linguagens XSC provêem características indispensáveis para o

desenvolvimento de softwares numéricos modernos e aplicações científicas tais

como: controle de arredondamento, tipos de dados com exatidão após a vírgula;

bibliotecas com as principais rotinas matemáticas para a resolução de problemas,

com arrays dinâmicos, conceito de operador (operadores definidos pelo usuário),

tipos de dados não existentes nas linguagens comuns como o dado complex

(complexo), interval (intervalo), além de outras características.

Page 29: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

16

Nos últimos anos, a visão sobre a computação de rede ganhou uma crescente

aceitação à medida que cada vez mais soluções computacionais se voltam para

internet e redes sem fio. A linguagem de programação JAVA é amplamente utilizada

para programação deste tipo de aplicações.

Java é orientada a objetos, independente de plataforma, tem API’s destinadas

a dispositivos móveis, servidores de aplicação e uma grande comunidade de usuários

que colaboram criando novas bibliotecas. Esta linguagem associada a uma extensão

científica poderá ser a primeira escolha nas aplicações computacionais e científicas

na solução de problemas físicos, químicos e das engenharias que necessitam de alta

exatidão ainda com a facilidade da programação voltada a Internet, dispositivos

móveis e tv digital.

A matemática intervalar busca resolver problemas que se concentram

basicamente em dois aspectos:

I. Na criação de um modelo computacional que reflita fidedignamente o

controle e análise dos erros que ocorrem no processo computacional e,

II. Na escolha de técnicas de programação adequadas para

desenvolvimento de softwares científicos buscando minimizar os erros

nos resultados.

O usuário não pode afirmar a exatidão da resposta estimada sem o auxílio de

uma análise de erro, que é extensa, dispendiosa e nem sempre viável. Assim, a

matemática intervalar busca dar suporte a estes problemas.

Para computarmos qualquer objeto é necessário representá-lo em um

dispositivo computacional o qual é em essência finito. Infelizmente, muitos objetos

fundamentais para resolvermos problemas do dia-a-dia, não são finitamente

representáveis em máquinas. A solução de equações reais complexas é essencial

para resolvermos problemas das engenharias e ciências da natureza, e economia,

entretanto um número irracional não é finitamente representável.

A solução deste tipo de plataforma utiliza as conhecidas aproximações que

induzem a erros. O numero racional 3.14 aproxima o número irracional π =3.1415...

Esta abordagem remete ao problema das limitações dessas aproximações,

suscitando questões tais como se elas possuem as mesmas propriedades algébricas

que os objetos que aproximam.

Page 30: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

17

No caso dos números racionais, do ponto de vista algébrico, eles são perfeitos

como aproximações de números reais, pois assim como os números reais, eles

também constituem um corpo, possibilitando a substituição de equações de

coeficientes irracionais por coeficientes racionais.

Entretanto esta abordagem nos conduz ao bem conhecido erro de

aproximação que é a distância entre o irracional e sua aproximação racional. Esse

ainda não é o principal problema desta abordagem. O problema maior reside no fato

de que esse erro de aproximação não obedece a qualquer lei durante as

computações, o que pode levar a distorções em uma computação. O controle deste

erro de aproximação durante as computações é feito por uma computação em

paralelo, requerendo esforços computacionais extras.

Existem três fontes de erros de computação numérica:

A propagação de erros nos dados e parâmetros iniciais: Ao se tentar

representar um fenômeno do mundo físico por meio de um modelo matemático

raramente se tem uma descrição correta deste fenômeno. Normalmente, são

necessárias várias simplificações do mundo físico para que se tenha um modelo

matemático com o qual se posso trabalhar, tomando-se apenas algumas grandezas

como tempo, temperatura, distancia, carga, entre outras. Estas são obtidas de

instrumentos que têm precisão limitada de modo que a incerteza destes parâmetros

iniciais levará conseqüentemente a incertezas dos resultados. A questão de como a

incerteza dos dados contribui para a incerteza da resposta pode ser ignorada ou pode

ser feita uma análise profunda com simulações ou com auxílio da experiência de

pesquisador. A análise é freqüentemente difícil ou impossível, simulações são

dispendiosas, especialmente em muitas dimensões e a experiência do pesquisador

deve ser considerada com cautela. Este tipo de erro é o mais sério porque não é

possível torná-lo arbitrariamente pequeno através da computação tradicional.

• Se o problema é calcular a área de um círculo de raio 6, na fórmula c

= πr2, π deve ser representado por um número. A questão é que o

conjunto de números disponíveis em qualquer computador é finito,

assim como é finita a excursão de qualquer elemento deste conjunto.

Em outras palavras, no modelo da matemática, o conjunto dos

números reais, longe está de ser representável e operável por

Page 31: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

18

qualquer computador. Portanto, expressões como “para todo x

real...”, não têm sentido em computações que exigem um

processamento numérico automático, incluindo desde os

calculadores que realizam operações básicas, aos computadores

dos centros científicos.

Erros de Arredondamento: Para a resolução de modelos matemáticos,

muitas vezes torna-se necessária à utilização de instrumentos de cálculo como

computadores digitais. Sabemos que estes instrumentos de cálculo trabalham com

números arredondados, ou seja, representam os números em forma finita de dígitos,

de acordo com o seu sistema interno de reapresentação e que tais limitações geram

erros durante as computações numéricas.

Erros de truncamento: São erros provenientes da utilização de processos,

que deveriam ser infinitos, para a determinação de um valor e que por razões práticas

são truncados. Estes processos infinitos são muito utilizados na avaliação de funções

matemáticas, tais como a função exponencial, logarítmica, funções trigonométricas e

várias outras que uma máquina pode ter.

A limitação dos dados de entrada e a acumulação do erro de arredondamento

em qualquer seqüência finita de operações aritméticas podem ser ambas

rigorosamente controladas, simplesmente pela utilização de aritmética de máquina

ordinária. Assim espera-se que técnicas intervalares forneçam garantias e que

possam ser aplicadas quase automaticamente. Uma resposta intervalar possui a

garantia de sua incerteza.

Como calcular ∑∞

=

0

1

n

n

r r ≠ 0? Não sendo possível realizar tal feito, a

ação tomada é truncar a expressão acima, ou seja, limitar sua operação para um

valor passível de cálculo.

Foi por causa destes erros que surgiram os primeiros trabalhos buscando a

sua resolução Sunaga [SUNAGA] fez o primeiro trabalho sobre intervalos munido de

Page 32: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

19

uma aritmética. Sunaga e Moore [MOORE], aparentemente, desenvolveram a mesma

teoria, ao mesmo tempo, em lugares diferentes. Todavia sempre se refere a Moore

quando se fala em matemática intervalar. Os trabalhos de Moore e Sunaga

permitiram que uma idéia simples se transformasse numa poderosa ferramenta para

análise de erros inerentes à computação científica. Assim, os algoritmos trabalham

sobre intervalos em vez de números racionais, tendo como resultados da

computação, intervalos que contém as soluções reais desejadas, cuja amplitude dá

uma medida de sua qualidade.

As respostas aos problemas dos erros de aproximação surgiram nos anos 60

com a aritmética intervalar proposta por Moore, que introduziu operações aritméticas

de maneira a controlar este erro de aproximação, de modo que o resultado de uma

operação com intervalos é novamente um intervalo. Esta abordagem define um

intervalo fechado [a1;a2] como uma aproximação de todos os números reais

pertences a ele.

O computador pode ser definido como uma máquina digital, utilizada também

para realizar cálculos, que tem como unidade fundamental de processamento o bit.

Os bits que constituem as informações da memória do computador são utilizados

para representar letras do alfabeto, números inteiros, imagens, vídeos, sons,

aplicações comerciais, entre outros diversos tipos de informações.

Todos os elementos acima citados apresentam como característica comum e

principal o fato de poderem ser finitamente representados, ou seja, o computador

possui uma maneira exata para representá-los a partir de uma determinada

codificação. Os problemas para os computadores começam a surgir quando é

necessário operar os números reais, os quais são representados pelos números de

ponto-flutuante.

A utilização de intervalos permite diminuir e controlar a perda de exatidão

depois de repetidos cálculos numéricos com números de ponto-flutuante. Através da

aritmética intervalar [MOORE], os números reais podem ser representados na forma

de intervalos, e então passam a ser manipulados como tal, aproveitando todos os

benefícios que esta representação pode proporcionar: exatidão numérica dos cálculos

efetuados com verificação automática dos resultados.

Page 33: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

20

2.5 ARITMÉTICA DE EXATIDÃO MÁXIMA

A aritmética de exatidão máxima garante que o resultado de operações

realizadas ou é um número de máquina ou está compreendido entre dois números de

máquinas consecutivos. A Figura 5 a seguir lista todos os espaços da computação

numérica do ponto de vista da aritmética de alta exatidão ou a aritmética

computacional avançada.[CAMPOS, 1995]

Figura 5. Espaços da Computação Numérica

• D e S representam os conjuntos dos números de ponto-flutuante de

precisão dupla(D) e Simples (S). VD e VS são os conjuntos dos

vetores cujos elementos são números de ponto-flutuante de precisão

dupla e simples, respectivamente. MD e MS, conjunto das Matrizes

cujos elementos são números de ponto-flutuante de precisão dupla e

simples, respectivamente.

• R, conjunto dos números reais. VR, espaço dos vetores cujas

componentes são números reais. MR, espaço das matrizes cujos

elementos são números reais.

• C, conjunto dos complexos. VC, espaço dos vetores cujas

componentes são números complexos. MC, espaço das matrizes

cujos elementos são números complexos. VCD, MCS conjunto dos

vetores e matrizes complexas de ponto-flutuante de precisão dupla e

simples, respectivamente.

Page 34: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

21

• IR, espaço dos intervalos de reais. Os elementos da tabela iniciados

por I indicam espaços de intervalos. Assim IVR, por exemplo é o

espaço dos intervalos cujas componentes são vetores de números

reais; IS, é o conjunto dos intervalos cujos limites são números de

ponto-flutuante de precisão simples.

• Os espaços iniciados por P indicam conjunto das partes. PVR, por

exemplo, é o conjunto das partes do espaço dos vetores cujas

componentes são números reais.

A aritmética de exatidão máxima [KULISCH, 1983] foi desenvolvida para

computação científica. Ela fornece um método axiomático para as operações

aritméticas realizadas em computadores que captura propriedades essenciais

associadas com arredondamentos em computações, construindo um sistema de

axiomas para problemas gerais que permite várias aplicações; seu grande mérito é

propor uma forma de operar números reais representados por números de máquina

preservando uma estrutura algébrica chamada de anelóide ou vetóide. A forma de

operar valores mantendo essa estrutura algébrica é através do semimorfismo

[KULISCH, 1983]. Semimorfismos são arredondamentos com algumas características

básicas. Contudo a definição de semimorfismo [CAMPOS, 1995] pressupõe conjuntos

onde a existência de supremos e ínfimos seja garantida.

Não somente aos anelóides, vetóides e semimorfismos, a proposta e o

desenvolvimento da aritmética computacional avançada se deve a Moore que na

década de 60 introduziu o intervalo. Erros de medições, arredondamentos e controle

do erro podem ser resolvidos através de intervalos. Uma área de pesquisa com amplo

espectro de aplicações, além do potencial de desenvolvimento teórico, têm

progredido a partir dos trabalhos de Moore.

Seja R, o conjunto dos números reais e S (b, l, emin, emax) um sistema de

ponto-flutuante, tem-se que:

Para cada x ∈ R, | x | ≤ * • d1d2...dl • bemax , di = b-1, i = 1,...,l; existem limites

inferiores para x em S e existem limites superiores para x em S;

O conjunto dos limites inferiores de x em S tem um maior elemento e o

conjunto dos limites superiores de x em S tem um menor elemento.

Page 35: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

22

2.5.1 SEMIMORFISMO

A Questão que se coloca de modo geral, com respeito a uma computação

científica, é como operações aritméticas que são definidas numa estrutura R, *, ≤

podem ser mais bem aproximadas em uma outra estrutura S, *, ≤ com S ⊆ R.

Tomando como exemplo o conjunto dos reais R, sabe-se que R, +, • , ≤ é um

corpo [CAMPOS, 1995]. As operações aritméticas nos reais, quando realizadas em

computadores, têm que ser aproximadas em S ⊆ R, Onde S é um sistema de ponto-

flutuante de determinada máquina. Além disso, essa aproximação não pode ser

realizada por meio de um isomorfismo, nem por meio de um homomorfismo. A

Aritmética de alta exatidão, define uma técnica que permite aproximar as operações

aritméticas definidas nos reais, no Screen [CAMPOS, 1995]. Um subconjunto S de R

é um Screen de R se R é “visto” através de S. O sistema de ponto-flutuante é um

Screen para o conjunto dos números reais. Não é possível operar os reais em

qualquer sistema de computação existente, pois este dispõe apenas dos números de

máquina, os quais formam um conjunto finito enquanto os reais são não enumeráveis.

Assim, os reais são vistos através dos números de ponto-flutuante.

Seja :R → S tal que a = a, ∀a ∈ S. O mapeamento , é denominado

arredondamento. Homomorfismos preservam operações nas transformações entre

estruturas, mas não é um homomorfismo como pode ser visto através do exemplo a

seguir.

Seja R o conjunto dos reais e S(10,1,-1,1) um sistema de ponto-flutuante; é o

arredondamento para o mais próximo ou, se ponto médio para o extremo superior e +

a operação de adição em S. Sejam a,b ∈ R, a = 0.34 e b = 0.54. Então:

a = 0.34 = 0.3

b = 0.54 = 0.5

(a + b) = (0.34 + 0.54) = 0.88 = 0.9

( a) + ( b) = ( 0.34) + ( 0.54) = 0.3 + 0.5 = 0.8

logo,

(a + b) ≠ (a) + (b)

Page 36: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

23

2.6 MATEMÁTICA INTERVALAR

As primeiras pesquisas e trabalhos na área da matemática intervalar foram

desenvolvidos por Moore [MOORE, 1979] que propôs a utilização de intervalos

numéricos para operar entre si. Assim, os problemas de aproximação passaram a ser

contornados, pois a resposta das operações seria agora um intervalo que conteria o

resultado esperado, caso este não pudesse ser representado de forma exata pela

máquina. O passo seguinte foi incorporar os intervalos e sua aritmética, bem como

os princípios da aritmética de exatidão máxima [KULISCH, 1983], às linguagens de

programação.

A utilização de intervalos para expressar resultados permite controlar a perda

de exatidão depois de repetidos cálculos numéricos computacionais. Através da

aritmética intervalar, os números reais podem ser representados na forma de

intervalos, e então passam a ser manipulados como tal, aproveitando todos os

benefícios que esta representação pode proporcionar, seja na exatidão numérica dos

cálculos efetuados ou nas linguagens com verificação automática dos resultados

Esta abordagem define um intervalo como sendo uma aproximação de todos

os números reais pertencentes a ele, ou seja, se a representação de um intervalo for

[i1, i2], então todos os números reais entre i1 e i2, inclusive, farão parte deste intervalo,

abstraindo dessa maneira a representação numérica limitada da máquina.

Concluindo, a aritmética intervalar trata da representação numérica através de

intervalos e das operações neles realizadas. A seguir serão detalhadas as principais

definições, bem como as operações de maior destaque. Esta seção visa ambientar os

leitores no universo da Aritmética Intervalar, para um melhor entendimento e

compreensão do restante do documento de dissertação.

2.6.1 INTERVALO DE NÚMEROS REAIS

Um intervalo de números reais, R, é da forma

I = [x1, x2],

onde, x1 e x2 pertencem ao conjunto dos números reais, tal que x1 ≤ x2. São

exemplos de intervalos: [1,2], [-2,-1], [1.9, 4.8], [1,1].

Page 37: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

24

2.6.2 CONJUNTO DE INTERVALOS

O conjunto de todos os intervalos de reais pode ser definido da seguinte forma:

IR = { [x1, x2] | x1, x2 ∈ R , x1 ≤ x2}.

Associando-se a cada intervalo [x1, x2] ∈ IR um ponto (x1, x2) ∈ R2, obtemos

uma representação geométrica para IR, conforme a Figura 6 a seguir:

Figura 6. Representação gráfica do espaço dos Intervalos

Importante ressaltar que todo e qualquer número real x ∈ R pode ser visto

como um intervalo de IR. Basta identificar os pontos x ∈ R com os intervalos pontuais

X = [x, x] ∈ IR. Estes intervalos também são chamados de intervalos degenerados,

porém a nomenclatura de intervalo pontual é comumente utilizada. A Figura 7

representa a hierarquia numérica dos conjuntos, onde N é o conjunto dos números

naturais, Z, dos inteiros, Q, dos racionais, R, dos reais e IR dos intervalos.

R

[x1, x2]

0

x1 = x2

Page 38: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

25

Figura 7. Estrutura hierárquica da representação numérica

2.6.3 OPERAÇÕES ARITMÉTICAS

Nesta subseção serão exibidas as definições das operações intervalares

[MOORE, 1979, DIVÉRIO,1997]. Considerando os intervalos A = [a1, a2] e B = [b1,b2],

a definição das operações aritméticas entre intervalos pode ser generalizada através

da fórmula a seguir.

A * B = {a * b | a ∈ A, b ∈ B}, * ∈ {+, -, *, / }.

2.6.3.1 ADIÇÃO INTERVALAR

A + B = [ (a1 + b1 ) , ( a2 + b2 ) ].

2.6.3.2 PSEUDO-INVERSO ADITIVO INTERVALAR

-A = [-a2 ,- a1].

2.6.3.3 SUBTRAÇÃO INTERVALAR

A - B = A + (-B) = [ (a1 - b2 ) , ( a2 - b1 ) ].

2.6.3.4 MULTIPLICAÇÃO INTERVALAR

A x B = [ mim { a1.b1,a1.b2,a2.b1.,a2.b2} , max{ a1.b1,a1.b2,a2.b1.,a2.b2} ].

2.6.3.5 PSEUDO-INVERSO MULTIPLICATIVO INTERVALAR

A-1 = 1/A = [1 / a2 ,1 / a1], 0 ∉A.

Page 39: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

26

2.6.3.6 DIVISÃO INTERVALAR

A / B= [ min{a1/b1,a1/b2,a2/b1,a2/b2 } , max{ a1/b1,a1/b2,a2/b1,a2/b2 ].

2.6.4 OPERAÇÕES ENTRE CONJUNTOS

Nesta subseção serão exibidas as funções envolvendo conjuntos de intervalos.

2.6.4.1 INTERSEÇÃO DE INTERVALOS

Se max {a1, b1} ≤ min {a2, b2}, então

A ∩ B = [ max {a1, b1} , min {a2, b2} ].

Caso min {a2, b2} < max {a1, b1} então

A ∩ B = Ø.

2.6.4.2 UNIÃO DE INTERVALOS

A ∪ B = [ min {a1, b1} , max {a2, b2} ], A ∩ Β ≠ Ø.

2.6.4.3 UNIÃO CONVEXA DE INTERVALOS

A ∪ B = [ min {a1, b1} , max {a2, b2} ].

Um ponto importante é que ao contrário da operação de união, em operações

de união convexa, a intersecção entre intervalos é permitida ser vazia.

2.6.5 OUTRAS OPERAÇÕES

2.6.5.1 DISTÂNCIA ENTRE INTERVALOS

d = max{|a1 − b1|, |a2 − b2|}.

2.6.5.2 DIÂMETRO DE UM INTERVALO

w = a2 – a1.

Page 40: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

27

2.6.5.3 PONTO MÉDIO DE UM INTERVALO

Seja A = [a1, a2] um intervalo pertencente ao intervalo dos números reais. O

ponto médio deste intervalo define-se como sendo a média aritmética dos seus

valores extremos.

m = (a1 + a2) / 2.

2.6.5.4 VALOR ABSOLUTO DE UM INTERVALO

|A| = max{|a1|,| a2|}

O Exemplo 7 ilustra as definições anteriormente mostradas.

Exemplo 7. Sejam os seguintes intervalos A = [0,10], B = [-3, 6] e C = [1, 3]. Então:

A + B = [0, 10] + [-3, 6] = [-3, 16].

A - B = [0, 10] + ( - [-3, 6] ) = [0, 10] + [-6, 3] = [-6, 13].

A x B = [0, 10] X [-3, 6]

= [min {0 X -3, 0 X 6, 10 x -3, 10 X 6}, max {0 X -3, 0 X 6, 10 x -3, 10 X 6}] = [0, 60].

A / B = [0, 10] / [-3, 6]

= [ min {0 / -3, 0 / 6, 10 / -3, 10 / 6}, max {0 / -3, 0 / 6, 10 / -3, 10 / 6}] = [0, 10/6].

C-1 = [1/3, 1].

A ∩∩∩∩ B = [0, 10] ∩ [-3, 6] = [ max (0, -3), min (10, 6) ] = [ 0, 6].

A ∪∪∪∪ B = [0, 10] ∪ [-3, 6] = [ min (0, -3), max (10, 6) ] = [-3, 10].

d(A, B) = max { |0 – (-3)| , |10 - 6| } = max (3, 4) = 4

w(A) = w (10 – 0 ) = 10

m(A) = m ((10 + 0) / 2) = 5

|A| = max{|0|, |10|} = 10

Page 41: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

28

2.7. JAVA-XSC

Concebida na década de 90, a linguagem de programação Java alcançou

enorme popularidade desde o início de sua utilização. Sua rápida ascensão e grande

aceitação devem-se, principalmente, às propriedades do paradigma de orientação a

objeto, particularmente, o fato de ser portável. Ou seja, as aplicações desenvolvidas

na linguagem Java podem ser executadas em diferentes tipos de plataformas.

Uma sucinta e boa definição para a linguagem Java pode ser encontrada em

um artigo próprio de sua empresa, criadora e mantenedora, a Sun Microsystems, que

a define da seguinte maneira: Java é simples, orientada a objeto, distribuída,

interpretada, robusta, segura, neutra de arquitetura, portável, multi-thread e dinâmica

[CHOUDHARI, 2001].

Outras características que fazem de Java uma linguagem de alto nível de

abstração e de fácil manipulação pelos programadores são o suporte a herança entre

os objetos, abolição do uso de ponteiros, alocação dinâmica de memória e, por fim,

utilização do processo interno de garbage collector para desalocação de memória.

Embora apresente todas as vantagens acima citadas, Java apresenta falhas

na sua implementação que comprometem aplicações de caráter matematicamente

computacional. Conforme apresentado anteriormente, a implementação do padrão

ANSI/IEEE Standard 754-1985 referente a representação de ponto-flutuante,

inviabiliza o desenvolvimento de aplicações matemáticas que necessitem de alta

exatidão nos resultados. Um trabalho nesta linha foi desenvolvido pelo grupo de

matemática computacional da Universidade Federal de Pernambuco [JAVA-XSC] e

Universidade Federal do Rio grande do Norte [DUTRA] que implementaram uma

biblioteca intervalar utilizando os tipos primitivos de Java como limites superiores e

inferiores dos Intervalos. Porem esta abordagem está passível dos erros

apresentados nos Exemplos 4, 5, 6 e 7. Exatamente, neste ponto é que entra a

contribuição deste trabalho propondo a inclusão de uma biblioteca intervalar que

processa caracteres tendo como linguagem alvo Java .

A biblioteca desenvolvida neste trabalho conterá a definição de novos tipos de

dados, entre eles o tipo Number (Strings numéricos), e Intervalo que utilizam

Strings Numéricos. Fazendo uso dos conceitos da Matemática Intervalar [MOORE,

1979] na definição das operações entre esses novos tipos.

Page 42: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

29

3 INTERVALOS DE STRINGS NUMÉRICOS

Os algoritmos que utilizamos para realizar cálculos sem a ajuda de

máquinas podem ser implementados numa linguagem de programação. No primário

escolar, aprendemos a processar cadeias de caracteres (Strings) em um papel,

portanto, tendo isto em mente por que não ensinar a máquina a fazer o mesmo

utilizando Strings numéricos?

Os algoritmos que realizam operações aritméticas utilizam truncamentos e

arredondamentos para contornar a sua incapacidade de representar todos os

números reais. A solução alternativa proposta para diminuir a propagação de erro ao

se trabalhar com números, outrora não representáveis, é mudar o paradigma de

representação.

O conjunto dos números racionais tem uma característica peculiar que é a

possibilidade de ser representado numericamente por uma quantidade finita de

símbolos; até uma dízima periódica pode facilmente ser representada por um

conjunto finito de símbolos na sua representação fracionária.

Uma limitação desta abordagem é representar e realizar operações sem

propagação de erro com o conjunto dos números irracionais em que é impossível

obter a representação de seus elementos por uma seqüência finita de caracteres. Por

este motivo, intervalos são utilizados para representar um número irracional. Para

calcularmos operações com o menor erro possível, um sistema intervalar foi

implementado como a representação genérica, tendo como limites superiores e

inferiores, strings numéricos.

Page 43: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

30

3.1 ARQUITETURA

Um sistema hierárquico é o modelo percebido naturalmente quando estamos

lidando com objetos que possuem características e comportamentos comuns que não

precisam ser modificados em tempo de execução. A biblioteca numérica intervalar foi

implementada seguindo o modelo UML [BOOCH] descrito na Figura 8.

Intervalolimi teInferior : StringNumberlimi teSuperior : StringNumber

diametro()pontoMedio()

StringNumber

divisorexpoentemantissasinalsinalExpoente

somar()subtrair()multiplicar()dividir()...()Operacao

executarOperacao()

OperacaoUnariaoperador1 : Intervalo

OperacaoBinariaoperador1 : Intervalooperador2 : Intervalo

InversoAditivo inversoMultiplicativo DivisaoUniao

Soma

Subtracao

Multiplicacao

Interseccao

...

...

Figura 8. Diagrama UML das Classes do Sistema.

A classe Intervalo é a classe base do sistema. Ela possui como atributos o

limiteSuperior e o limiteInferior que são do tipo Number (detalhes sobre

esta classe serão discutidos nas seções posteriores). Operações como calcular o

diâmetro de um intervalo, ponto médio, multiplicação por um escalar, distância e valor

absoluto são definidas nesta classe.

A classe abstrata Operacao herda da classe Intervalo e impõe que as classes

filhas concretas implementem o método abstrato executarOperacao. Este método

tem por finalidade definir a operação aritmética que será executada pelas classes

Page 44: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

31

descendentes. Nesta classe também está definido o procedimento setIntervalo

que dá valor aos limites superiores e inferiores do Intervalo resultado da operação.

Seguindo na hierarquia as classes operacaoBinaria e operacaoUnaria

são as primeiras classes descendentes de Operacao e definem construtores para

operações aritméticas binárias e unárias. No construtor dessas classes é chamado o

método executarOperacao seguido do procedimento setIntervalo para que na

construção da operação já tenhamos seu resultado computado.

As classes filhas de operacaoBinaria e operacaoUnaria só precisam

chamar em seus construtores o construtor da classe pai e implementar o método

executarOperacao, que define como esta operação intervalar deve proceder

[JAVA COM STRINGS]. As regras específicas para a multiplicação intervalar, por

exemplo, são codificadas neste método. As operações aritméticas realizadas no

método executarOperacao destas classes utilizam a classe Number, esta classe

possui a implementação de toda a aritmética baseada em processamento de

caracteres. As sessões seguintes descrevem em detalhes como foram concebidos

esta classe, seu construtor, atributos e operações aritméticas.

3.2 PROCESSAMENTO DE STRINGS

Um símbolo é uma entidade abstrata que não é definida formalmente, assim

como o ponto não é definido na Geometria. Letras e números são exemplos de

símbolos freqüentemente usados. Uma String (ou Palavra) é uma seqüência finita

de símbolos justapostos. Se a, b, e c são símbolos abcb é uma String. O

comprimento de uma String w, denotado por |w| é definido como o número de

símbolos que compõem uma String. Por Exemplo, abcb tem comprimento 4. A

String vazia é denotado por ε, e não é composta por símbolo algum. Então |ε| = 0.

Um conceito importante é a concatenação de Strings. A concatenação de duas

Strings é a String formada pela escrita da primeira seguida pela escrita da segunda

(justaposição), sem nenhum espaço entre as duas. Por exemplo, a concatenação de

passa e tempo é passatempo. A String vazia é o a identidade do operador

concatenação, ou seja, εw = wε = w para qualquer String w [HOPCROFT, 1979].

Page 45: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

32

3.3 PROCESSAMENTO DE STRINGS EM JAVA

A classe String em Java representa um conjunto de caracteres (símbolos).

Todos os strings literais do tipo "123" ou "abc" são instâncias desta classe. A

String vazia em Java é representada por "" (abre e fecha aspas);

String str = "abc";

é equivalente a :

char data[] = {'a', 'b', 'c'};

String str = new String(data);

A classe String (java.lang.String) possui métodos para obter

caracteres individuais da seqüência, comparação entre instâncias de strings, extrair

subconjuntos de strings (substrings), criar cópias, alterar para maiúsculas e

minúsculas, entre outras funcionalidades. A linguagem Java provê suporte especial

para concatenação de strings através do operador (+) e para conversão de outras

instâncias de objetos em strings (método toString()). Operações matemáticas {+,

-, *, %(resto da divisão), / (divisão inteira)} com o tipo char são permitidas e leva em

consideração o valor unicode do caractere.

O Unicode [UNICODE] é o padrão de codificação de caracteres desenvolvido

pelo Unicode Consortium. Este vem sendo adotado por muitas grandes empresas no

sentido de padronizar a codificação de caracteres. Esta codificação sempre foi

problemática devido à existência de diferentes padrões (ASCII pt, en, EBCDIC, entre

outros.) e da incompatibilidade entre eles, o que fazia com que a representação de

texto entre diferentes idiomas ficasse confusa devido às diferentes interpretações, por

exemplo, dos caracteres especiais e acentuados (ç, Ç, ã, Ã, õ, Õ, ö, Ö, etc.).O

Unicode associa um número para cada caractere, independente do programa,

plataforma ou idioma, abrangendo quase todas as escritas em uso atualmente, além

das escritas históricas já extintas e os símbolos, em especial os matemáticos e os

musicais.

Page 46: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

33

3.4 DEFINIÇÃO DO TIPO NUMBER

O sistema intervalar proposto e implementado nesta dissertação utiliza a

classe String de Java para representar a mantissa e expoente de um número. A

classe principal da biblioteca é a classe Number que representa um “número”. Ela é

descrita pelo diagrama da Figura 9. A Constante CARACTERE_ZERO representa o

valor unicode do caractere zero, ou seja, 48 (código unicode 48). As constantes

SINAL_NEGATIVO, SINAL_ZERO e SINAL_POSITIVO possuem os valores, -1, zero

e 1, respectivamente, e representam o valor atribuído aos sinais dos expoentes e dos

números.

Figura 9. Diagrama UML da Classe Number

O tipo Number possui 5 atributos:

• mantissa: É do tipo String e é o conjunto de algarismos da base do número.

• sinal: Pode tomar o valor de uma das três constantes: SINAL_NEGATIVO,

SINAL_ZERO e SINAL_POSITIVO, e representam o sinal do número. Só o

número zero possui o valor SINAL_ZERO.

• expoente: Determina a ordem de grandeza de um número. Quantas casas

decimais depois da virgula (se o sinal do expoente for negativo) ou qual potência

de 10 que multiplica a mantissa. Representa os algarismos do expoente.

• sinalExpoente: Define o sinal do expoente do número pode assumir o valor de

uma das três constantes SINAL_NEGATIVO, SINAL_ZERO e SINAL_POSITIVO.

Page 47: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

34

• denominador: Para podermos representar um número no formato fracionário o

atributo denominador foi criado. Ele é do tipo Number podendo também ter um

denominador. A seguir será visto que na construção do objeto Number,

recursivamente, será eliminado denominadores de denominadores, simplificando

desta forma o denominador final.

Utilizando-se este modelo atingi-se uma precisão máxima de 231 - 1 casas

decimais na base 10, que é o tamanho máximo que o tipo inteiro em Java pode

assumir, pois em Java laços e índices de arrays são indexados por este tipo de dado

(int). Uma outra abordagem alternativa seria utilizar uma lista encadeada desta forma

poderíamos aumentar a precisão para o limite da memória, porém perderíamos

muitas funcionalidades que o tipo String nos fornece. O mesmo número, (231 - 1), é

o limite do tamanho do expoente. Sabendo que (231 - 1) é igual a 2147483647, temos

que o modulo máximo do expoente é 102147483647 – 1. Este mesmo número é o valor

máximo do módulo da mantissa. Conseqüentemente o sistema numérico pode deixar

uma boa margem para se efetuarem cálculos sem necessidade de arredondamentos

ou truncamentos.

Conseqüentemente o sistema numérico criado pelo tipo Number possui as

seguintes especificações:

N = (10, (231 - 1), -(102147483647 - 1), 102147483647 - 1).

É importante lembrar que nem todos os cálculos utilizam a precisão máxima,

pois estamos utilizando strings e podemos variar a precisão da mantissa de acordo

com a necessidade de casas decimais que a operação requerer, diferentemente dos

sistemas de ponto-flutuantes que utilizam uma quantidade fixa de símbolos (os bits).

O tipo Number possui dois construtores públicos: o construtor padrão, não

recebe qualquer parâmetro e cria o número zero e um construtor que recebe uma

instância de um objeto do tipo String. Para construirmos um objeto Number a partir

de uma String precisamos fazer um reconhecimento de padrão (retirar as partes

relativas à mantissa, sinais, expoente e denominadores), identificando cada um de

seus atributos e se o mesmo obedece a uma gramática. A gramática define a regra

de formação do tipo Number e está definida no Quadro 1 a seguir.

Page 48: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

35

Quadro 1: Gramática de formação do tipo Number

Esta gramática [HOPCROFT] aceita no seu conjunto de símbolos os

caracteres: {‘0’, ’1’, ’2’, ‘3’ , ‘4’, ’5’, ’6’, ‘7’, ‘8’, ‘9’, ‘.’, ‘,’, ‘e’, ‘E’, ‘/’, ‘+’, ‘-’} e segue as

regras de formação descritas acima.

Um autômato [HOPCROFT] foi implementado para reconhecer a gramática do

Quadro 1. Quando a regra de formação da linguagem é desrespeitada a

ExcecaoFormatoNumeroInvalido é lançada, indicando que a String passada

como parâmetro no construtor do tipo Number [JAVA COM STRINGS] não pôde ser

reconhecido pelo autômato e a instância do objeto do tipo Number não pôde ser

construída. À medida que o autômato vai lendo a cadeia de caracteres de entrada,

são identificados: sinal, mantissa, expoente, sinal do expoente e denominador.

Segundo a gramática do quadro 1 podemos ter denominadores de denominadores e,

portanto é preciso ter recursividade na função que cria o tipo Number. Os passos da

função createNumber [JAVA COM STRINGS], que cria um tipo Number a partir de

um tipo String (cadeia de caracteres) estão descritos abaixo. O método

createNumber é responsável por implementar o autômato relativo à gramática do

Quadro 1.

Passo 0: Remover os zeros à esquerda.

Passo 1: Identificar o sinal:

Se a mantissa, ao removerem-se os zeros à esquerda, estiver formada apenas

pelo caractere ‘0’, é atribuído o sinal relativo ao número zero.

S → + | - | D

D→ 0D | 1D | 2D | 3D | 4D | 5D | 6D | 7D | 8D | 9D | .P| ,P P → 0P | 1P | 2P | 3P | 4P | 5P | 6P | 7P | 8P | 9P | eF | EF F → + | - | N N → 0N | 1N | 2N | 3N | 4N | 5N | 6N | 7N | 8N | 9N | /S

Page 49: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

36

Se o primeiro caractere for ‘.’ ou ‘,’ o sinal é positivo e o sistema saberá que

deve ler caracteres depois da virgula. Assim para cada caractere lido uma unidade

deverá ser subtraída do expoente.

Passo 2: Ler os caracteres da mantissa (caracteres ‘0’ a ‘9’) até não encontrarmos

caracteres numéricos. Se ao ler os caracteres depois da virgula não tivermos mais

caracteres numéricos para serem lidos, o sistema lança uma exceção se o próximo

caractere for diferente de ‘e’, ‘E’ ou ‘/’. Caso contrário, o sistema então segue ao

Passo 3 ou 4 dependendo do caractere encontrado.

Passo 3: Se foi encontrado o caractere ‘e’ ou ‘E’ o expoente deve ser lido, primeiro

seu sinal e depois sua mantissa.

Passo 4: Se foi encontrado o caractere ‘/’ deve-se chamar recursivamente a função

createNumber passando como parâmetro a substring que vem após o caractere ‘/’

para que seja identificado o denominador do número. Volta-se então ao Passo 0.

Desta forma são válidas as seguintes construções: 1. Cria o número Zero: Number n = new Number();

2. Cria um número sem levar em consideração o expoente: Number n = new Number(“76345”);

3. Cria um número sem denominador: Number n = new Number(“-76345.345E-34”);

4. Cria um número com denominador: Number n = new Number(“76.345E-34/657.657E-98”);

5. Cria um número com um denominador que possui outro denominador Number n = new Number(“76.45/+.7686/345E-67”);

6. Desrespeitando-se a gramática a exceção ExcecaoFormatoNumeroInvalido é lançada.

Algumas regras de validação e simplificação são usadas para auxiliar a

construção do objeto Number, retirando redundâncias, diminuindo o custo do seu uso

nos cálculos e diminuindo a quantidade de caracteres a serem processados

futuramente:

Page 50: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

37

1. Verifica-se recursivamente se existe divisão por zero na cadeia de denominadores.

O algoritmo para realizar a verificação está descrito no Exemplo 8, neste exemplo

EDPZ é ExcecaoDivisaoPorZero:

Exemplo 8. Algoritmo que verifica divisão por zero na cadeia de denominadores

private boolean verificaDivisaoPorZero(Number n) throws EDPZ{

boolean edpz = false;

if (n.denominador != null) {

if (n.denominador.mantissa.equals("0")){

throw new EDPZ(n.denominador.toString());

} else {

edpz = edpz || verificaDivisaoPorZero (n.denominador);

}

}

if (n.mantissa.equals("0")){

throw new EDPZ(n.toString());

}

return edpz;

}

2. Se a mantissa for igual a zero e não ocorreu divisão por zero na cadeia de

denominadores, o atributo denominador é alterado para o valor null (Instância

Nula).

3. Simplifica-se recursivamente a cadeia de denominadores, de forma que tenhamos

apenas um denominador, ou seja, se um denominador possui um denominador,

efetuar-se-á todas as divisões até que tenhamos apenas um denominador para o

numerador e este denominador não precise ser simplificado. O algoritmo está descrito

no Exemplo 9, onde multiplicacaoDesconsiderandoDenominador é a

multiplicação de dois objetos do tipo Number desconsiderando-se seus

denominadores [ver Exemplo 37], ou seja, só os numeradores são levados em

consideração.

Exemplo 9. Resolver recursivamente os denominadores de denominadores.

private static void simplificarDenominadores (Number a) {

Number c;

if (a.div != null && a.div.div != null){

simplificarDenominadores(a.div);

c = multiplicacaoDesconsiderandoDenominador (a,a.div.div);

a.mantissa = c.mantissa;

a.sinal = c.sinal;

a.sinalExpoente = c.sinalExpoente;

a.expoente = c.expoente;

a.div.div = null;

}

}

Page 51: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

38

4. Resolve o sinal do número, verifica-se o sinal do numerador e do denominador e se

o sinal do denominador for negativo, inverte-se sinal do número e se atribui o

sinal.positivo para o denominador.

5. Efetua a divisão pelo expoente do denominador, subtraindo do expoente do

numerador, o expoente do denominador.

6. Simplifica as mantissas de numeradores e denominadores se houver um m.d.c.

(máximo divisor comum) entre eles diferente de 1.

3.5. OPERAÇÕES

O primeiro passo para criarmos um sistema numérico baseado em

processamento de caracteres é definir suas operações mais básicas, no decorrer do

desenvolvimento do sistema naturalmente surgiu a necessidade de criar operações

de forma hierárquica, ou seja, criar as operações que efetuem cálculos considerando

apenas strings numéricos, depois levar em consideração os expoentes e os sinais,

em seguida levar em consideração os denominadores e por último realizar as

operações intervalares.

As quatro operações básicas que processam Strings são o pilar para

desenvolver as demais operações, pois elas realizam operações sob a mantissa dos

números, todas elas são private static, ou seja são métodos de classe, não é

necessário instanciar um objeto para utilizá-las e as mesmas possuem apenas

visibilidade dentro da classe.

3.5.1 COMPARAÇÃO ENTRE STRINGS

A classe String de Java possui um o método compareTo para comparar

strings lexicograficamente, a comparação é baseada no valor unicode de cada

caractere das strings. Este método recebe como parâmetro outra String a fim de

efetuar a comparação e tem como resultado um inteiro (int). Este inteiro retorna:

• Um valor menor que zero se a instância da String que executa o método for

menor lexicograficamente que a String passada como parâmetro.

• Zero se as strings forem idênticas.

Page 52: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

39

• Um valor maior que zero se a instância que executa o método de comparação

é maior lexicograficamente que a String passada como parâmetro.

Diferença lexicográfica: Se duas strings são diferentes, então elas devem ter

caracteres diferentes em algum índice para um determinado índice válido em ambas

as strings, seus comprimentos diferem ou as duas situações acontecem. Se elas

possuem caracteres diferentes em um ou mais índices, seja k o menor índice onde

esta situação acontece. Assim a String cujo caractere na posição k possui o menor

valor, determinado através do operador “<”, lexicograficamente precede a outra

String. O método compareTo retorna a diferença dos caracteres na posição k.

return this.charAt(k) - stringParametro.charAt(k);

Se não existir um índice k em que as Strings se diferenciem. Então a menor

String em comprimento (quantidade de caracteres) precede lexicograficamente o

maior String. Neste caso, compareTo retorna a diferença de comprimento dos Strings:

return this.length() - stringParametro.length();

Portanto, só pudemos usar nesta biblioteca a comparação lexicográfica

quando as mantissas fossem do mesmo tamanho. Uma vez que a String “8” seria

maior que a String “300”!

O algoritmo da comparação, usado para determinar igualdade, maior que (>) e

menor que (<) (Exemplo 10), inverte a ordem em que as avaliações são feitas:

• Passo 1: Removem-se os possíveis zeros à esquerda.

• Passo 2: Avalia-se o tamanho da String. Quanto maior o comprimento da

String maior seu valor numérico.

• Passo 3: Se os tamanhos forem iguais, avalia-se lexicograficamente as

Strings.

• Passo 4: Um inteiro é retornado com valor –1 se a String do primeiro

parâmetro for menor lexicograficamente do que a String do segundo

parâmetro, 0 (zero) se elas forem iguais e 1 se a String do primeiro

parâmetro for maior lexicograficamente do que a String do segundo

parâmetro.

Page 53: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

40

O algoritmo da comparação está descrito no exemplo 10:

Exemplo 10. Comparação entre Strings Numéricas

private static int compareStrings(String a, String b) {

int ret = 0;

a = removeLeftZeros(a);

b = removeLeftZeros(b);

if (a.length() > b.length()) {

ret = 1;

} else if (a.length() < b.length()) {

ret= -1;

} else {

ret = a.compareTo(b);

}

return ret;

}

3.5.2 OPERAÇÕES ARITMÉTICAS 3.5.2.1 ADIÇÃO NATURAL

A adição de Strings foi implementada seguindo o modelo do Full-Adder

[GAJSKI], que é um somador binário utilizado nas unidades lógicas e aritméticas

(ULA) de um microprocessador. Só que no contexto de processamento de caracteres

a base é 10 e uma adaptação é necessária. Sejam A e B Strings que representam a

mantissa de dois números, os caracteres da mantissa da soma S é dada pela figura

10, Onde % é a operação que calcula o resto da divisão e div é a operação de

divisão natural.

Figura 10.Somador (Modelo do full adder)

É importante ressaltar que o que acontece são adições de caracteres e quando

as strings possuem tamanhos diferentes, se completa com zeros à esquerda a menor

String, até que as duas Strings sejam do mesmo tamanho. O código em Java está

descrito no Exemplo 11, onde Z é a constante Number.CARACTERE_ZERO.

C0 = 0

Ci+1 = (Ai + Bi + Ci) div 10

Si = (Ai + Bi + Ci) % 10

Page 54: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

41

Exemplo 11. Adição Natural de Strings

private static String adicaoNaturalStrings(String a, String b){

//resultado da adicao

StringBuffer soma = new StringBuffer("");

//diferenca entre o tamanho das Strings

int difTamanho = a.length() - b.length();

if (difTamanho > 0) {

b = completeWithLeftZeros(b,difTamanho);

} else if (difTamanho < 0){

a = completeWithLeftZeros(a,-difTamanho);

}

//valor a ser incluido na proxima adicao

int carry = 0;

//soma em valores absolutos dos caracteres

int somaCaracteres = 0;

//variavel auxiliar para armazenar momentaneamente a soma

char temp = '0';

for (int i = a.length()-1; i >= 0; i--) {

somaCaracteres = carry + a.charAt(i) + b.charAt(i)- 2*Z;

temp = (char)((somaCaracteres%10) + Z);

carry = (somaCaracteres/10);

soma.append(temp);

}

if (carry != 0) {

soma.append(carry);

}

soma.reverse();

return soma.toString();

}

É necessária a inclusão do valor unicode do caractere zero uma vez que os

caracteres ‘0’ até ‘9’ possuem valores seqüenciais de 48 a 57. A adição recebe duas

strings formadas apenas por números (caracteres de ‘0’ a ‘9’) e retorna uma nova

String representando a soma destes números.

3.5.2.2 SUBTRAÇÃO NATURAL

A subtração natural foi implementada segundo o Exemplo 12, Onde Z é a

constante Number.CARACTERE_ZERO e subNaturalStrings é o método

subtracaoNaturalStrings:

O método subtracaoNaturalStrings requer que a String “a” seja maior

que a String “b”, lembrando que este método é visível apenas dentro da classe

(private), outro método desta classe se preocupará em comparar [ver Seção 3.5.4]

se “a” é maior que “b” e chamará o método com os parâmetros na ordem correta.

O algoritmo da subtração inicia completando com zeros à esquerda da String

de menor comprimento até que as duas strings tenham a mesma quantidade de

caracteres.

Page 55: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

42

Exemplo 12. Subtração Natural de Strings

private static String subNaturalStrings(String a, String b){

//variavel de retorno

String subtracao = "";

int dif = a.length() - b.length();

if (dif > 0) {

b = completeWithLeftZeros(b, dif);

} else if (dif < 0) {

a = completeWithLeftZeros(a, -dif);

}

//define se adicionaremos mais 1 ao subtraendo ou não

int carry = 0;

//valor de cada caractere resultado da subtracao

char temp = '0';

for (int i = a.length()-1; i >= 0; i--) {

if (a.charAt(i) < b.charAt(i) + carry) {

temp = (char)(a.charAt(i) + 10);

temp = (char)(temp - (b.charAt(i) + carry) + Z);

subtracao = temp + subtracao;

carry = 1;

} else {

temp = (char)(a.charAt(i) - (b.charAt(i) + carry) + Z);

subtracao = temp + subtracao;

carry = 0;

}

subtracao = Number.removeLeftZeros(subtracao);

return subtracao;

}

A cada iteração (Exemplo 13) é verificado se o valor unicode [UNICODE] do

caractere da String “a” na posição i, é menor que a soma do valor unicode do

caractere da String “b’, na posição i somado com o carry. O carry, é uma

variável utilizada no algoritmo de subtração, para indicar se na próxima iteração deve

ser adicionada ou não, uma unidade ao subtraendo. O carry é iniciado com o valor

zero, pois inicialmente não é necessária a adição de uma unidade ao subtraendo”.

Exemplo 13. Inicialização do carry e avaliação do uso da dezena emprestada.

int carry = 0;

for (int i = a.length()-1; i >= 0; i--) {

if (a.charAt(i) < b.charAt(i) + carry) {

Se o resultado da comparação:

a.charAt(i) < b.charAt(i) + carry (Exemplo 12, 13) (I)

for verdade: ao caractere da posição i da String “a” é adicionado 10 unidades

(dezena emprestada Exemplo 14 e 12) e armazenada na variável temp. Desta

variável (temp) é subtraído o caractere da posição i da String “b” somado com o

carry. Sempre se deve adicionar o valor unicode do caractere zero para que a

Page 56: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

43

variável “temp” guarde o caractere numérico resultante da subtração. À String

“subtracao”, que armazena os caracteres da operação subtrair, é atribuído o valor

da concatenação (+) entre ela e a variável “temp”. Neste caso atribui-se ao carry o

valor 1 para indicar um futuro acréscimo a subtração da próxima iteração devido ao

uso da dezena emprestada utilizada na iteração corrente (Exemplo 14 e 12).

Exemplo 14. Calculo do caractere da diferença com uso da dezena emprestada.

temp = (char)(a.charAt(i) + 10);

temp = (char)(temp - (b.charAt(i) + carry) + Z);

subtracao = temp + subtracao;

carry = 1;

Caso o resultado da comparação definida em (I) seja verdadeiro, deve-se

subtrair do caractere situado na posição i da String “a”, o caractere da posição i da

String “b” adicionado do carry. Seguindo a mesma filosofia da adição, adiciona-se a

esta diferença o valor unicode do caractere zero. Em seguida atribui-se ao carry o

valor zero, uma vez que não houve a necessidade de se utilizar uma dezena

emprestada (Exemplo 15).

Exemplo 15.Calculo do caractere da iteração sem o uso da dezena emprestada.

temp = (char)(a.charAt(i) - (b.charAt(i) + carry) + Z);

subtracao = temp + subtracao;

carry = 0;

3.5.2.3 MULTIPLICAÇÃO NATURAL O algoritmo da multiplicação possui complexidade O(n2) pois é necessário

multiplicar todos os caracteres das strings parcelas. Sejam A e B strings, M o

resultado da multiplicação parcial e C o carry, no laço mais interno (Figura 11) um

caractere da segunda String multiplica todos os caracteres da primeira String.

Figura 11. Resolução do carry e do caractere resultante por iteração.

No Laço mais interno Figura 11 (Exemplo 18). um carry (dezena da

multiplicação anterior, que inicialmente é zero) é avaliado para ser somado a próxima

C0,j = 0

Ci,j+1 = (Aj x Bi + Ci,j) div 10

Mi,j = (Aj x Bi + Ci,j) % 10

Page 57: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

44

multiplicação. Concatenam-se as unidades da soma (soma mod 10) do carry com o

resultado da multiplicação de A e B a String que armazena a soma parcial (variável

subproduto).

O algoritmo completo está descrito no Exemplo 16, onde Z é a constante

CARACTERE_ZERO, mult a variável multiplicacaoCaracteres e multNaturalString

é o método multiplicacaoNaturalStrings:

Exemplo 16. Multiplicação Natural de Strings

private static String multNaturalStrings(String a, String b){

//produto da multiplicacao

String produto = "0";

StringBuffer subProduto = new StringBuffer("");

b = removeLeftZeros(b);

a = removeLeftZeros(a);

//valor a ser incluido na proxima multiplicacao

int carry = 0;

//soma em valores absolutos dos caracteres

int mult = 0;

//variável para armazenar momentaneamente o produto

char temp = '0';

StringBuffer deslocamento = new StringBuffer("");

for (int i = b.length()-1; i >= 0; i--) {

carry = 0;

subProduto = new StringBuffer(deslocamento.toString());

for (int j = a.length()-1; j >= 0; j--) {

mult = carry +((a.charAt(j) - Z) * (b.charAt(i)-Z)) ;

temp = (char)((mult%10) + Z);

carry = mult/10;

subProduto.append(temp);

}

if (carry != 0) {

subProduto.append(carry);

}

subProduto.reverse();

String subProd = subProduto.toString();

produto = Number.adicaoNaturalStrings(produto, subProd);

deslocamento = deslocamento.append('0');

}

return produto;

}

Inicialmente são removidos, se houverem, os zeros à esquerda das Strings a e

b. Um laço duplo percorre ambas as Strings e realiza a multiplicação de todos os

caracteres de A pelos caracteres de B (Exemplo 17, laço externo). À variável

“deslocamento” é acrescentado um zero a cada iteração para que as somas

parciais contemplem a ordem de grandeza (dezena, centena, milhar,...) que está

Page 58: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

45

sendo multiplicada na iteração. Na variável subproduto estão sendo armazenadas

as multiplicações parciais. Onde a cada iteração um caractere da string “b” é

multiplicado pela String “a”. As somas dos produtos parciais são armazenadas na

variável produto que no final da execução do algoritmo (Exemplo 17) conterá o valor

da multiplicação.

Exemplo 17. Laço mais externo do algoritmo da Multiplicação Natural.

StringBuffer deslocamento = new StringBuffer("");

for (int i = b.length()-1; i >= 0; i--) {

subProduto = new StringBuffer(deslocamento.toString());

for (int j = a.length()-1; j >= 0; j--) {

}

String subProd = subProduto.toString();

produto = Number.adicaoNaturalStrings(produto, subProd);

deslocamento = deslocamento.append('0');

}

No Laço mais interno (Exemplo 18, Figura 11), inicialmente é calculado o valor

da multiplicação do caractere da String A pelo caractere da String B, mais uma

vez é preciso subtrair cada caractere numérico do caractere zero unicode (variável Z).

Na terceira linha, é atribuída a variável temp o valor do caractere a ser concatenado

às multiplicações das iterações anteriores que formarão o “produto parcial”

(armazenado na variável subProduto, operação de append). Na quarta linha

calcula-se o carry para a próxima iteração (Exemplo 18).

Exemplo 18 Laço mais interno do algoritmo da Multiplicação Natural

for (int j = a.length()-1; j >= 0; j--) {

mult = carry +((a.charAt(j) - Z) * (b.charAt(i)-Z)) ;

temp = (char)((mult%10) + Z);

carry = mult/10;

subProduto.append(temp);

}

Page 59: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

46

3.5.2.4 DIVISÃO NATURAL

O algoritmo da divisão natural recebe como parâmetro dois strings e retorna

um array de Strings de duas posições. Na primeira posição está o quociente e na

segunda posição é atribuído o resto. A partir do resto e do quociente é possível

calcular a divisão racional [ver seção 3.5.2.7]. Duas funções auxiliares foram criadas

para ajudar o calculo da divisão natural (Figura 12):

Figura 12. Elementos utilizados no algoritmo da divisão

• acharSubstringDividendoMaiorQueDivisor: Este método recebe

como parâmetro o dividendo e o divisor e como o nome já diz tem como

responsabilidade procurar no dividendo o primeiro substring (subconjunto de

uma String) do dividendo, que seja maior que o divisor. A relação de

precedência é definida pelo método que compara Strings descrito na Seção

3.5.4. O algoritmo está descrito no Exemplo 19.

Exemplo 19. Encontra a maior substring do dividendo maior que o divisor

private static String ex19 (String dividendo, String divisor){

String aux = "";

boolean cond = true;

for (int i=0; i <= dividendo.length() && cond; i++) {

aux = dividendo.substring(0,i);

if (compareStrings(aux,divisor) > 0) {

cond = false;

}

}

return aux;

}

Page 60: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

47

• “acharMaiorMultiploMenorQueSubstringDividendo”: Este método

auxiliar procura o maior múltiplo do divisor que não seja maior que uma

determinada substring do dividendo. Esta substring é obtida pelo método

descrito no Exemplo 20. O método recebe como parâmetro uma substring do

dividendo e o divisor, retornando um array de strings com duas posições. Na

primeira posição é colocado o múltiplo do divisor que queremos encontrar. E

na segunda posição é colocado o caractere (de ‘1’ a ‘9’) que multiplica o divisor

e dá como resultado o múltiplo colocado na primeira posição do array. O

algoritmo (Exemplo 20) inicia a busca utilizando o caractere “5”, ou seja,

multiplica o caractere ‘5’ pelo divisor e avalia se a multiplicação foi maior ou

menor que o dividendo:

o Se a multiplicação for maior: Subtrai-se uma unidade do caractere

multiplicador (utilizar-se-á o caractere “4” na próxima iteração). E volta-

se a multiplicar o substring do dividendo pelo novo caractere. Até que

encontremos um múltiplo que seja menor que a substring do dividendo.

Este então é o múltiplo que desejamos e o caractere multiplicador fará

parte do quociente da divisão.

o Se o produto foi menor: Incrementa-se de uma unidade o caractere

multiplicador. E volta-se a multiplicar o dividendo pelo novo caractere.

Este procedimento se repete até quando encontrarmos um múltiplo que

seja maior que a substring do dividendo. Devemos então retornar este

caractere subtraído de uma unidade, pois queremos um múltiplo menor

que o substring, e o múltiplo relativo a este caractere.

o Se o produto for igual: retorna-se o caractere multiplicador e a

multiplicação deste com o divisor.

O procedimento é ilustrado no Exemplo 20 onde multNaturalStrings é o

método multiplicacaoNaturalStrings e subNaturalStrings é o método

subtracaoNaturalStrings.

Page 61: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

48

Exemplo 20 Calcula o maior múltiplo do divisor menor que a substring do dividendo

private static String[] ex20 (String sub, String divisor){

String retorno[] = null;

String initialNumber ="5";

String condAnterior = "";

String produto = "";

boolean cond = true;

produto = multNaturalStrings(divisor,initialNumber);

while (cond) {

if (compareStrings(produto,subDividendo) > 0) {

if (condAnterior.equals("menor")) {

cond = false;

retorno = new String [] {

subNaturalStrings(produto,divisor),

subNaturalStrings(initialNumber,"1")

};

} else {

condAnterior = "maior";

initialNumber =subNaturalStrings(initialNumber,"1");

produto = subNaturalStrings(produto,divisor);

}

} else if (compareStrings(produto,subDividendo) < 0) {

if (condAnterior.equalsIgnoreCase("maior")){

retorno = new String[]{produto,initialNumber};

cond = false;

} else {

condAnterior = "menor";

initialNumber = adicaoNaturalStrings(initialNumber,"1");

produto = adicaoNaturalStrings(produto, divisor);

}

} else {

cond = false;

retorno = new String[]{produto,initialNumber};

}

}

return retorno;

}

Com os métodos do Exemplo 19 e 20 construímos o algoritmo que divide uma

cadeia de caracteres por outra (Exemplo 21), onde remLZeros é o método

removeLeftZeros, div é o dividendo, subDiv é o substringDividendo,

divIncial é o dividendoInicial, ex19 é o método acharSubstring-

DividendoMaiorQueDivisor, ex20 é o método acharMaiorMultiploMenor-

QueSubstringDividendo e subtracao é o método subtracaoNatural-

Strings.

Page 62: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

49

Exemplo 21. Divisão Natural de Strings

private static String[] divisaoRestoNaturalStrings(String div,

String divisor) throws ExcecaoDivisaoPorZero {

String quociente = "";

String resto = "";

if (igualdadeStrings(divisor,"0")) {

throw new ExcecaoDivisaoPorZero(dividendo, divisor);

}

int comparacao = compareStrings(dividendo, divisor);

if (comparacao < 0) {

quociente = "0";

resto = dividendo;

} else if (comparacao == 0){

quociente = "1";

resto = "0";

} else {

String divInicial = ex19(div,divisor);

String subDiv = div.replaceFirst(divInicial,"");

String[] multiplo = ex20(divInicial, divisor);

quociente = quociente + multiplo[1];

String diferenca = subtracao(divInicial,multiplo[0]);

while(subDiv.length() > 0) {

diferenca = diferenca + subDiv.charAt(0);

subDiv = subDiv.substring(1,subDiv.length());

if (compareStrings(diferenca, divisor) < 0) {

quociente = quociente + "0";

} else {

multiplo = ex20(diferenca, divisor);

quociente = quociente + multiplo[1];

diferenca = subtracao (diferenca, multiplo[0]);

}

}

resto = diferenca;

}

return new String[]{remLZeros(quociente),remLZeros(resto)};

}

Inicialmente é verificado se o divisor é igual a zero, se sim a

“ExcecaoDivisaoPorZero” é lançada. Em seguida, compara-se o dividendo com o

divisor: Se o dividendo for menor que o divisor, o quociente é igual a zero e ao resto é

atribuído o dividendo. Se o dividendo for igual ao divisor: atribui-se 1 ao quociente e o

resto será igual a zero.

Se nenhum dos casos anteriores ocorrer (Exemplo 22): executa-se o algoritmo

do Exemplo 20 que acha a primeira substring do dividendo maior que o divisor. Esta

String é armazenada na variável “divInicial”. Na variável “subDiv” é atribuído a

chamada do método “replaceFirst”. O método “replaceFirst” da classe

String substitui a primeira ocorrência de uma String (no caso “divInicial”) por

outra String passada como parâmetro (String vazia ou “”). Desta forma, da

Page 63: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

50

variável “div” (do dividendo) é retirado a String encontrada no algoritmo do

Exemplo 20 (menor substring do dividendo maior que o divisor). Em seguida utiliza-

se o algoritmo do Exemplo 21 para achar o maior múltiplo do divisor que ainda seja

menor que esta substring do dividendo. O algoritmo do Exemplo 21 nos retorna um

caractere multiplicador e o múltiplo do divisor, sendo seu retorno atribuído a variável

“multiplo”. Este caractere multiplicador vai ser atribuído a String que formará o

“quociente”. Para encontramos o próximo caractere do quociente é preciso subtrair

do substring do dividendo (divInicial) o múltiplo encontrado (multiplo[0]).

Esta subtração é armazenada na String ”diferenca”.

Exemplo 22. Caso em que o dividendo é maior que o divisor

} else {

String divInicial = ex19(div,divisor);//substring

String subDiv = div.replaceFirst(divInicial,"");

String[] multiplo = ex20(divInicial, divisor);//multiplo

quociente = quociente + multiplo[1];

String diferenca = subtracao(divInicial,multiplo[0]);

...

}

O algoritmo do Exemplo 23 segue iterativamente até acabar os

caracteres do dividendo. O algoritmo verifica se a concatenação da variável

diferenca com o próximo caractere do dividendo é maior que o divisor.

• Se for menor, um caractere zero é concatenado ao quociente.

• Caso contrário (Exemplo 23), executa-se o algoritmo do Exemplo 20 (achar o

maior múltiplo do divisor menor que a substring do dividendo). Concatena-se

ao quociente o caractere multiplicador, e é realizada a subtração entre a

variável “diferenca” e o múltiplo encontrado.

Exemplo 23.Laço principal da divisão natural.

while(subDiv.length() > 0) {

diferenca = diferenca + subDiv.charAt(0);

subDiv = subDiv.substring(1,subDiv.length());

if (compareStrings(diferenca, divisor) < 0) {

quociente = quociente + "0";

} else {

multiplo = ex20(diferenca, divisor);

quociente = quociente + multiplo[1];

diferenca = subtracao (diferenca, multiplo[0]);

}

}

resto = diferenca;

Page 64: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

51

Quando os caracteres do dividendo tiverem chagado ao fim

(subDiv.length() > 0) a variável “quociente” conterá o quociente da divisão e

a variável “resto”, o resto da divisão. Na primeira posição do array de retorno é

colocado o quociente e na segunda posição o resto (Exemplo 21) removendo-se

os zeros à esquerda.

3.5.2.5 MÁXIMO DIVISOR COMUM (MDC)

O algoritmo do “mdc” é muito importante nesta biblioteca, pois possibilita a

simplificações entre numeradores e denominadores e o cálculo do mínimo múltiplo

comum (mmc) que é utilizado na adição de frações com denominadores diferentes. O

algoritmo de Euclides foi utilizado, ele é recursivo e segue a indução descrita na

Figura 13.

Figura 13. Definição mdc

Exemplo 24. Algoritmo para cálculo do mdc

public static String mdc(String m, String n){

String mdc = "1";

if (m.equals("1") || n.equals("1")) {

mdc = "1";

} else {

mdc = mdcRecursivo(m,n);

}

return mdc;

}

private static String mdcRecursivo(String m, String n) throws

ExcecaoDivisaoPorZero {

String mdc = "";

if (m.equals("0")) {

mdc = n;

} else {

if (compareStrings(n,m) >=0) {

mdc = mdcRecursivo(restoDivisaoNaturalStrings(n,m),m);

} else {

mdc = mdcRecursivo(restoDivisaoNaturalStrings(m,n),n);

}

}

mdc(0,n) = n

mdc(m,n) = mdc ( n mod m, m) se m > 0

Page 65: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

52

A função “restoDivisaoNaturalStrings” utiliza a divisão natural de

Strings [ver Seção 3.5.2.4] e retorna apenas o resto da divisão. O algoritmo para

calcular o mdc está descrito no Exemplo 24.

3.5.2.6 MÍNIMO MULTIPLO COMUM (MMC) O mmc é calculado a partir do mdc e é encontrado através da relação descrita

na Figura 14:

Figura 14. Definição mmc

No exemplo 25 está descrito algoritmo do mmc que utiliza a multiplicação

natural de Strings [ver Seção 3.5.2.3], onde EDPZ é a ExcecaoDivisaoPorZero.

Exemplo 25. Algoritmo para calcular o mmc

public static String mmc (String a, String b) throws EDPZ{

return divisaoNaturalStrings(

multiplicacaoNaturalStrings(a,b),

mdc(a,b)

);

}

3.5.2.7 DIVISÃO RACIONAL DESCONSIDERANDO O SINAL

A divisão Racional utiliza a divisão natural [ver Seção 3.5.2.4] como ponto de

partida. O algoritmo da divisão [referencia link] está dividido nos Exemplos 26 a 31.

Esta divisão ainda não leva em consideração o sinal, esta avaliação será feita na

operação de divisão que será vista na Seção 3.5.3.3. O método que implementa a

divisão recebe como parâmetro o dividendo, o divisor, uma variável boleana que

assumindo o valor “verdadeiro” (true) coloca o resultado da divisão em forma

fracionária. Por ultimo um parâmetro que indica a precisão. Este último parâmetro só

será usado, se o indicador de formato fracionário estiver com valor “falso” (false)

atribuído. O método retorna um tipo Number e lança a exceção

ExcecaoDivisaoPorZero se o divisor for igual a zero. A assinatura do método está

descrita no Exemplo 26:

mmc (m,n) = (m * n) / mdc (m, n)

Page 66: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

53

Exemplo 26. Assinatura método da Divisão Racional

private static Number

divisaoRacionalStrings(String dividendo,

String divisor,

boolean fracionar,

int casasDecimais)

throws ExcecaoDivisaoPorZero;

Inicialmente (Exemplo 27) calcula-se o mdc [ver Seção 3.5.2.5], se este existir,

entre o dividendo e o divisor, a fim de simplificar suas mantissas e menos caracteres

serem processados. Depois é calculada a divisão natural, obtendo-se quociente e

resto.

Exemplo 27. simplificando as mantissas do dividendo e do divisor

String mdc = mdc(dividendo,divisor);

if (!mdc.equals("1")) {

dividendo = divisaoNaturalStrings(dividendo,mdc);

divisor = divisaoNaturalStrings(divisor,mdc);

}

String [] divisaoInteira = divisaoRestoNaturalStrings(dividendo, divisor);

Sendo o resto da divisão inteira igual zero (String “0”, Exemplo 28), não há

necessidade de realizar a divisão pelo resto. Portanto é atribuída a mantissa do

objeto resultado, o valor do quociente da divisão inteira. Quando o resto da divisão

inteira é diferente de Zero na divisão racional, o algoritmo continua dividindo o resto

até que uma dízima periódica seja identificada (Figura 16) o dividendo da iteração

seja igual a zero (Figura 15) ou a precisão desejada seja alcançada.

Figura 15. Dividendo da Iteração igual a Zero

Page 67: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

54

Uma “Hashtable” (java.util.Hashtable) tabela hash que armazena uma

tupla (chave, valor) é utilizada para identificar a existência de dízimas periódicas. O

conjunto das chaves do Hashtable é formado pelo resto da divisão natural e dos

resultados das subtrações dos dividendos das interações pelo maior múltiplo do

divisor (Figura 16). Na tupla (chave, valor) é atribuído ao valor o comprimento da

String que representa o quociente da divisão do resto na iteração corrente. Este

comprimento quando armazenado possibilita identificar onde começa o período da

dízima. Na Figura 16, por exemplo os valores 30 e 80 são colocados como chave da

hashtable, na terceira iteração, o número 30 reaparece nas subtrações sucessivas,

caracterizando uma periodicidade:

Figura 16 - Identificação da Periodicidade de uma Divisão

. No algoritmo do Exemplo 28, zeros são concatenados a variável “resto” até

que o resultado destas concatenações seja maior que o divisor. O primeiro zero

atribuído não é contabilizado na variável quocienteDepoisVirgula, somente a

partir do segundo zero, um zero é concatenado a variável

quocienteDepoisVirgula.

Page 68: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

55

Exemplo 28.Núcleo do algoritmo da Divisão desconsiderando o sinal

Hashtable ht = new Hashtable();

divisaoInteira[1] = removeLeftZeros(divisaoInteira[1]);

Number resultado = new Number();

if (divisaoInteira[1].equalsIgnoreCase("0")) {

resultado.sinal = SINAL_POSITIVO;

resultado.mantissa = divisaoInteira[0];

} else {

//guarda o quociente da divisao calculado apos a virgula

String quocienteDepoisVirgula = "";

int inicioDizima = 0;

String resto = divisaoInteira[1];

String multiplo[] = new String[2];

boolean cond = true;

String quocienteAvaliado = "";

boolean isparteInteiraZero = divisaoInteira[0].equalsIgnoreCase("0");

while (cond && !removeLeftZeros(resto).equalsIgnoreCase("0")

&&(fracionar || quocienteAvaliado.length() < casasDecimais + 1)){

resto = resto + "0";

while (compareStrings(resto, divisor) < 0 ) {

resto = resto + "0";

quocienteDepoisVirgula = quocienteDepoisVirgula + "0";

}

if (!ht.containsKey(resto)) {

ht.put(resto, "" + quocienteDepoisVirgula.length());

multiplo = ex20(resto, divisor);

quocienteDepoisVirgula = quocienteDepoisVirgula + multiplo[1];

resto = subtracaoNaturalStrings(resto,multiplo[0]);

} else {

cond = false;

inicioDizima = Integer.parseInt((String)ht.get(resto));

}

quocienteAvaliado = removeLeftZeros(quocienteDepoisVirgula);

}

...

}

A divisão segue como na divisão natural determina-se o maior múltiplo do

divisor que seja menor que o dividendo (neste caso o dividendo no laço é a variável

“resto”) [ver Exemplo 21, Seção 3.5.2.4]. À variável quocienteDepoisVirgula é

concatenado o caractere multiplicador e da variável resto subtrai-se o múltiplo. Este

processo continua até atingirmos o critério de parada do laço principal (While

(cond && !RLZ(resto).equalsIgnoreCase("0")), Figuras 15 e 16 ou a

precisão ser atingida quando escolhe-se não fracionar (&&(fracionar ||

quocienteAvaliado.length() < casasDecimais + 1)). Onde a variável

cond indica existência de periodicidade, RLZ é o método removeLeftzeros e o

método equalsIgnoreCase verifica a igualdade entre strings. Se foi identificada a

periodicidade, cond é atribuída o valor false e a variável inicioDizima passa a

guardar o índice na String quocienteDepoisVirgula onde começa a parte

Page 69: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

56

periódica. A precisão é definida pela variável quocienteAvaliado que é utilizada

para garantir a flutuação do ponto quando o resultado da divisão natural for igual a

zero.

Se ao sairmos do laço (Exemplo 29) a variável resto (que guarda o último

dividendo de iteração) for igual a zero, significa que não encontramos uma dízima

periódica, e criamos um objeto do tipo Number como resultado da operação.

Exemplo 29. Construção do tipo Number resultado quando não existe período

if (removeLeftZeros(resto).equalsIgnoreCase("0")){

resultado = new Number(

divisaoInteira[0] + quocienteDepoisVirgula,

SINAL_POSITIVO,

SINAL_NEGATIVO,

"" + quocienteDepoisVirgula.length(),

null);

} else {

}

Como ainda não estamos levando em consideração o sinal, foi atribuído o sinal

positivo ao tipo Number “resultado”, a mantissa é atribuída à concatenação do

quociente da divisão inteira com o valor da variável quocienteDepoisVirgula. O

tipo Number guarda a mantissa sem levar em conta a posição da vírgula. A vírgula é

determinada pelo comprimento da String do expoente. O sinal do expoente neste

caso é sempre negativo, pois estamos concatenando a mantissa caracteres depois

da virgula. O valor do expoente é dado pela quantidade de casas depois da vírgula

que é determinado pelo comprimento da cadeia de caracteres da variável

quocienteDepoisVirgula. Como não existe um objeto denominador é passado

null no ultimo parâmetro do construtor.

Se foi encontrada uma dízima periódica, ou seja, a variável resto do Exemplo

31 é diferente da String Zero, utilizaremos a indicação da variável boleana

fracionar (Exemplo 26) passada como parâmetro. A fração geratriz que é a função

que gera a dízima periódica sendo calculada da seguinte forma:

1.Suponha uma dízima periódica simples m formada por n algarismos:

m = 0, k1k2k3...kn....

multiplicando-se os dois membros por 10n, temos:

Page 70: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

57

10n • m = k1k2k3...kn , k1k2k3... kn...., Subtraindo m de cada lado da equação:

10n • m - m= k1k2k3...kn , k1k2k3... kn - 0, k1k2k3... kn … ,Este cálculo nos leva a

m = (k1k2k3...kn ) / (10n - 1) .

Por exemplo, m = 0,67896789.... ,n = 4. logo m = 6789/(1000-1) = 6789/9999

2. Dada uma dízima periódica m composta:

m = 0,BK , onde B é a parte não periódica com p algarismos e K a parte

periódica com q algarismos. Multiplicando-se os dois membros por 10p+q e 10p

temos:

10p+q • m = BK,K (I) e 10p • m = B,K (II); subtraindo (II) de (i) temos:

(10p+q - 10p) • m = BK – B, assim temos a fórmula geral:

m = (BK – B) / ((10q-1) 10p). (Regra Geral)

para m = 0,322222…. K = 2, B = 3, p = 1 e q = 1 logo m = (32 - 3)/((10-1)•10)

Portanto m = 29/90.

O algoritmo do Exemplo 30 implementa a fórmula geral para calcular a fração

geratriz. Na primeira linha é obtida a parte periódica da dízima através do índice que

determina seu início (inicioDizima). A variável do tipo String mantissa-

Dividendo armazena o numerador da fração geratriz que é dado pela subtração da

parte não periódica concatenada com a parte periódica da dízima

(divisaoInteira[0] + quocienteDepoisVirgula + dizima) pela parte

após a virgula (BK-B da Regra Geral) .

A função CZDFG é o método calcularZerosDenominadorFracao-

Geratriz. Ele recebe como parâmetro uma String com a mantissa do numerador da

fração geratriz (mantissaDividendo) e a quantidade de casas decimais após a

vírgula até a primeira ocorrência do período da dízima (quocienteDepois-

Virgula.length()). Este método retorna um array de String de duas posições:

• Na posição 0 é retornada a mantissa normalizada do numerador da

fração geratriz, sem os zeros que estariam à direita depois da vírgula,

estes zeros surgem após a subtração que determina o dividendo da

fração geratriz (BK - K) e não devem ser incluídos na mantissa.

Page 71: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

58

• Na posição 1 é colocada uma String com zeros a serem concatenados

no denominador da fração geratriz (10p da regra Geral).

Exemplo 30. Coloca o resultado na forma fracionária.

String dizima = quocienteDepoisVirgula.substring(

inicioDizima, quocienteDepoisVirgula.length());

if (fracionar) {

String mantissaDividendo = subtracaoNaturalStrings(

divisaoInteira[0] + quocienteDepoisVirgula + dizima,

divisaoInteira[0] + quocienteDepoisVirgula);

String[] calcularDizima =

CZDFG (mantissaDividendo, (quocienteDepoisVirgula).length());

mantissaDividendo = calcularDizima[0];

String denominadorFracao =

subtracaoNaturalStrings(completeWithRightZeros("1",

dizima.length()),"1");

denominadorFracao = denominadorFracao + calcularDizima[1];

if (compareStrings(denominadorFracao,divisor) < 0) {

resultado.sinal = SINAL_POSITIVO;

resultado.mantissa = mantissaDividendo;

resultado.denominador = new Number();

resultado.denominador.sinal = SINAL_POSITIVO;

resultado.denominador.mantissa = denominadorFracao;

} else {

resultado.sinal = SINAL_POSITIVO;

resultado.mantissa = dividendo;

resultado.denominador = new Number();

resultado.denominador.sinal = SINAL_POSITIVO;

resultado.denominador.mantissa = divisor;

}

} else {...}

No Exemplo 30, a mantissa do denominador da fração geratriz da dízima

(variável denominadorFracao) é determinada pela formula ((10q - 1) 10p). O valor

10q é calculado a partir da parte periódica da dízima (variável dizima). O cálculo do

denominador utiliza a função completeWithRightZeros que concatena zeros à

direita de uma String numérica. A quantidade de zeros a serem concatenados a

String numérica “1” é determinada por (10q) que é o comprimento da parte periódica

(dizima.length()). Deve-se subtrair o resultado da concatenação (concatenar

zeros a uma String numérica é o mesmo que multiplicar por potências de 10) por 1

(10q - 1) e depois concatenar os zeros retornados (10p) pelo método

calcularZerosDenominadorFracaoGeratriz ao resultado.

Page 72: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

59

Exemplo 31. Resultado na forma não fracionária, com precisão especificada.

if (fracionar) {

} else {

if (casasDecimais <= quocienteDepoisVirgula.length()) {

String arredondamento = null;

if (!isparteInteiraZero){

arredondamento = arredondar(divisaoInteira[0] +

quocienteDepoisVirgula + dizima.charAt(0),

casasDecimais + divisaoInteira[0].length());

} else {

arredondamento = arredondar(quocienteAvaliado, casasDecimais);

}

resultado = new Number(

arredondamento,

SINAL_POSITIVO,

SINAL_NEGATIVO,(casasDecimais +

(quocienteDepoisVirgula.length() - quocienteAvaliado.length())) + "",

null);

} else {

int numeroCaracteresAntesDizima = quocienteDepoisVirgula.length() -

dizima.length();

int repeticoesDizima = (casasDecimais –

numeroCaracteresAntesDizima)/dizima.length();

int tamanhoSubStringDizima =

(casasDecimais - numeroCaracteresAntesDizima) % dizima.length();

String resultadoString =

quocienteDepoisVirgula.replaceFirst(dizima,"");

for (int i = 0; i < repeticoesDizima; i++) {

resultadoString = resultadoString + dizima;

}

resultadoString = resultadoString +

dizima.substring(0,tamanhoSubStringDizima + 1);

resultadoString = arredondar(resultadoString, casasDecimais);

resultadoString = divisaoInteira[0] + resultadoString;

resultado = new Number(

resultadoString,

SINAL_POSITIVO,

SINAL_NEGATIVO,

"" + casasDecimais,

null);

}

}

O algoritmo (Exemplo 30) que coloca a dízima na forma fracionária avalia se o

denominador da dízima seria menor que o divisor passado como parâmetro se isso

acontecer o denominador da dízima será utilizado, caso contrário é colocado na

forma fracionária os parâmetros de entrada, sendo o dividendo o numerador e o

divisor o denominador.

Page 73: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

60

Se foi encontrada uma dízima periódica e a variável boleana fracionar

tenha o valor false, é preciso usar o parâmetro casasDecimais (Exemplo 26) para

determinar com que precisão será retornada, o resultado da divisão. O Exemplo 31

apresenta o algoritmo utilizado para recuperar o resultado da divisão com uma

precisão especifica

O algoritmo verifica se a quantidade de casas decimais depois da virgula é

menor do que a precisão desejada. Caso o resultado da divisão inteira seja zero,

arredonda-se a variável quocienteAvaliado que despreza os zeros anteriores ao

primeiro algarismo diferente de zero. Isto é necessário para garantir a flutuação do

ponto. Caso o resultado da divisão inteira seja diferente de zero,

um tipo Number é criado tendo como mantissa o arredondamento da concatenação

do quociente da divisão natural com o quociente da divisão do resto

(quocienteDepoisVirgula). O arredondamento utilizado é o arredondamento

para o mais próximo. A mantissa do expoente neste caso é dada pelo tamanho de

casas decimais especificado.

Se a quantidade de casas decimais especificadas for maior do que a

quantidade de caracteres da parte não periódica da dizima concatenada com a parte

periódica (quocienteDepoisVirgula Exemplo 31): Na determinação da mantissa

do resultado, é preciso calcular quantas vezes a parte periódica da dízima se repete a

fim de retornar o resultado da divisão com a precisão desejada

(repeticoesDizima). Também é preciso calcular até qual caractere da substring da

parte periódica da dízima é atingida a precisão especificada

(tamanhoSubStringDizima). São concatenadas a parte não periódica da dízima

(quocienteDepoisVirgula.replaceFirst(dizima,"")), a quantidade de

repetições da parte periódica e a substring da parte periódica que atinge a precisão.

Por ultimo arredonda-se este valor para a precisão desejada.

3.5.3 MÉTODOS ARITMÉTICOS PÚBLICOS DO TIPO NUMBER

Os métodos públicos do tipo Number utilizam os métodos das seções

anteriores como base para realizar operações aritméticas com suas mantissas e

expoentes que são formados por cadeia de caracteres (strings). As operações no

conjunto dos números racionais requerem a resolução de sinais e denominadores de

Page 74: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

61

números fracionários. Estas regras estão definidas nas operações racionais descritas

nas próximas sessões.

3.5.3.1 ADIÇÃO E SUBTRAÇÃO RACIONAL

As operações de adição e subtração no conjunto dos números racionais têm

que verificar se os denominadores dos números a serem adicionados são iguais, e

avaliar os sinais dos mesmos. Se os sinais forem opostos uma operação de

subtração deverá ser realizada, sinais iguais indicam que uma adição será efetuada.

Na operação de subtração antes de se avaliar os sinais inverte-se o sinal do

subtraendo. A adição e a subtração racional utilizam o método do Exemplo 32 para

calcular seus resultados:

Exemplo 32. Adição ou Subtração Racional

private static Number adicaoOuSubtracaoRacional(Number a, Number b,

boolean isAdicao) {

Number r = new Number();

colocarMesmaBaseDenominadores(a,b);

r = somaOuSubtracaoSemDenominadores(a,b,isAdicao);

if (a.denominador != null) {

r.denominador = a.denominador.clonar();

}

return r;

}

O método adicaoOuSubtracaoRacional recebe como parâmetro dois

objetos do tipo Number e uma variável boleana que ao assumir o valor verdadeiro

indica adição, e caso contrário indica subtração. O método retorna um tipo Number

com o resultado da adição ou da subtração. O método

colocarMesmaBaseDenominadores (Exemplo 33) calcula o mínimo múltiplo

comum [ver Seção 3.5.2.6] dos denominadores (se estes existirem) e faz as devidas

multiplicações nos numeradores para manter a proporcionalidade da fração. O

método adicaoOuSubtracaoSemDenominadores (Exemplo 38) é responsável por

avaliar os sinais dos números, normalizar (adicionar zeros à direita) a mantissa do

tipo Number de maior expoente e realizar uma adição ou subtração das mantissas

dos números com os parâmetros na ordem correta. A normalização é necessária para

que se possa efetuar as operações com as mantissas sob a mesma ordem de

grandeza. O ultimo passo do método adicaoOuSubtracaoRacional é atribuir ao

Page 75: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

62

denominador do Objeto Number resultado, o mmc calculado a partir dos

denominadores dos parâmetros de entrada. O método clonar é responsável por

retornar uma nova instância (diferente referência na memória) do objeto a ser copiado

ou “clonado”, contendo os mesmos valores para todos os atributos.

O método colocarMesmaBaseDenominadores (Exemplo 33) é responsável

por calcular o mmc dos denominadores e ajustar (Exemplo 36, método

ajustarParaNovaBase) os numeradores dos parâmetros para a novo denominador

(Figura 17). O método começa avaliando o atributo denominador dos números

passados como parâmetro. Se o atributo denominador de um dos números for nulo

(nulo significa que o denominador é igual ao número “1”), é atribuído o denominador

do outro.

Exemplo 33. Colocar dois números sob a mesma base de denominadores

private static void colocarMesmaBaseDenominadores(Number a, Number b){

if (a.denominador != null && b.denominador == null) {

ajustarParaNovaBase(a,b);

} else if (a.denominador == null && b.denominador != null) {

ajustarParaNovaBase(b,a);

} else if (a.denominador != null && b.denominador != null){

//elimina sinal e potencia dos denominadores

eliminaExpoenteSinalDenominador(a);

eliminaExpoenteSinalDenominador(b);

//calcula mmc

String mmc = mmc(a.denominador.mantissa, b.denominador.mantissa);

//calcula o resultado da divisao do mmc pelo denominador atual

String fatorMultiplicativoA =

divisaoNaturalStrings(mmc,a.denominador.mantissa);

String fatorMultiplicativoB =

divisaoNaturalStrings(mmc,b.denominador.mantissa);

//atribui ao denominador o mmc

a.denominador.mantissa = mmc;

b.denominador.mantissa = mmc;

//calcula o dividendo

a.mantissa = multiplicacaoNaturalStrings(fatorMultiplicativoA,a.mantissa);

b.mantissa = multiplicacaoNaturalStrings(fatorMultiplicativoB,b.mantissa);

}

Page 76: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

63

××

=

),(

),(

,),(

),(

,dbmmc

d

dbmmcc

dbmmc

b

dbmmca

d

c

b

af

Figura 17. Colocando sob a mesma base de denominadores

Caso os atributos denominadores (denominadores) sejam diferentes é preciso

calcular seu mmc. Para otimizar a busca pelo mmc e utilizar o mesmo algoritmo da

Seção 3.5.2.6 foi criado o método eliminaExpoenteSinalDenominador

(Exemplo 34) que retira, caso se aplique, o sinal negativo do expoente e iguala o

expoente a zero. subtraindo o expoente do numerador pelo expoente do denominador

(Figura 18).

4647

10834784

104647

10834784 4

9

13×−

=

×−

×f

Figura 18. Exemplo do efeito do método que elimina o expoente e o sinal do denominador

Exemplo 34. Resolve o denominador eliminando ordem de grandeza e sinal negativo

private static void eliminaExpoenteSinalDenominador(Number m) {

String [] expoenteA =

avaliaSinaisRealizaOperacaoAdicaoOuSubtracao(m.expoente,

m.denominador.expoente, m.sinalExpoente, -m.denominador.sinalExpoente);

m.denominador.expoente = "0";

m.denominador.sinalExpoente = SINAL_ZERO;

m.sinalExpoente = Integer.parseInt(expoenteA[0]);

m.expoente = expoenteA[1];

if (m.denominador.sinal == SINAL_NEGATIVO){

m.sinal = -m.sinal;

m.denominador.sinal = SINAL_POSITIVO;

}

}

O método do Exemplo 34 inverte o sinal do objeto Number (numerador), se o

sinal do denominador for negativo e coloca o sinal do denominador como positivo. O

método também precisa subtrair o expoente do numerador pelo expoente do

denominador, e para isto utiliza a função avaliaSinaisRealizaOperacao-

AdicaoOuSubtracao (Exemplo 35) que avalia os sinais passados como parâmetro

e decide se será realizada uma Adição ou subtração. Esta função retorna um array

com duas posições. Na primeira posição é retornado o sinal do resultado da operação

e na segunda posição é retornada uma String numérica representando a mantissa

do resultado. O algoritmo do Exemplo 35 assume que será sempre realizada uma

Page 77: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

64

Adição, portanto os métodos que utilizam este algoritmo desejando calcular uma

subtração, invertem um dos sinais passados como parâmetro. Este algoritmo

(Exemplo 35) pode ser visto como a Adição no conjunto dos Inteiros.

No Exemplo 35 se na avaliação dos sinais forem identificados sinais iguais o

sinal da variável resultado (result) será igual a este sinal e uma operação de

Adição natural [ver Seção 3.5.2.1] será efetuada. Caso os sinais sejam diferentes,

verifica-se qual das mantissas é maior através do método compareStrings [ver

Seção 3.5.1]. Realiza-se a subtração da maior mantissa pela menor mantissa. O sinal

retornado, neste caso, é o sinal relativo a maior mantissa. Caso o retorno do método

compareStrings indique igualdade entre as mantissas (retorno do método igual a

zero) é atribuido ao resultado a String numérica “0” (zero).

Exemplo 35. Avalia mantissas e sinais calculando uma adição ou subtração.

private static String[] avaliaSinaisRealizaOperacaoAdicaoOuSubtracao(

String mantissaA, String mantissaB,

int sinalA, int sinalB) {

String result [] = new String[2];

if (sinalA == sinalB) {

result[0] = sinalA + "";

result[1] = adicaoNaturalStrings(mantissaA, mantissaB);

} else {

if (compareStrings(mantissaA, mantissaB) > 0) {

result[0] = sinalA + "";

result[1] = subtracaoNaturalStrings(mantissaA, mantissaB);

} else if (compareStrings(mantissaA, mantissaB) < 0){

result[0] = sinalB + "";

result[1] = subtracaoNaturalStrings(mantissaB, mantissaA);

} else {

result[0] = SINAL_ZERO + "";

result[1] = "0";

}

}

return result;

}

O método ajustarParaNovaBase (Exemplo 36) é utilizado na operação que

iguala os denominadores de dois números que serão somados ou subtraídos

(Exemplo 33, colocarMesmaBaseDenominadores). Este método (ajustarPara-

NovaBase) é usado quando um dos objetos do tipo Number envolvidos na operação

de adição ou subtração tem o atributo denominador igual a null. Ter um

denominador igual a null é o mesmo que ter um denominador de fração igual a 1.

Portanto, no ajuste para um mínimo múltiplo comum (mmc), o mmc é igual ao mmc

Page 78: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

65

do atributo denominador não-nulo. Simplificando a operação descrita na Figura 17

temos a operação do Exemplo 37 definida na Figura 19.

×=

d

b

d

da

d

baf ,,

1

Figura 19. Ajuste de um tipo Number com denominador null para a realização de uma adição ou subtração com outro tipo Number com denominador não-nulo.

Exemplo 36. Iguala o denominador e faz o ajuste no numerador

private static void ajustarParaNovaBase(Number m, Number n) {

Number produto =

multiplicacaoDesconsiderandoDenominador(n, m.denominador);

n.sinal = produto.sinal * m.denominador.sinal;

n.mantissa = produto.mantissa;

n.expoente = produto.expoente;

n.sinalExpoente = produto.sinalExpoente;

n.denominador = m.denominador.clonar();

}

Para realizar o ajuste do Exemplo 36 foi preciso criar um método

(multiplicacaoDesconsiderandoDenominador, Exemplo 37) que recebe dois

objetos do tipo Number e realiza a multiplicação entre as mantissas (numeradores)

desconsiderando os denominadores (denominadores).

Exemplo 37. Multiplicação desconsiderando o denominador

private static Number multiplicacaoDesconsiderandoDenominador(Number a,

Number b){

Number n = new Number();

n.sinal = a.sinal * b.sinal;

String expoente [] =

avaliaSinaisRealizaOperacaoAdicaoOuSubtracao(

a.expoente, b.expoente, a.sinalExpoente, b.sinalExpoente);

n.sinalExpoente = Integer.parseInt(expoente[0]);

n.expoente = expoente[1];

n.mantissa = multiplicacaoNaturalStrings(a.mantissa, b.mantissa);

return n;

}

O método descrito no Exemplo 37 calcula a soma dos expoentes dos tipos

Number passados como parâmetro através do método avaliaSinaisRealiza-

OperacaoAdicaoOuSubtracao definido no Exemplo 35. Ele utiliza a multiplicação

natural [ver Seção 3.5.2.3] para calcular a multiplicação entre as mantissas. O tipo

Number retornado desconsidera o denominador. isto é, calcula multiplicações

considerando apenas mantissas, sinais, expoentes e sinais dos expoentes. A

Page 79: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

66

operação que considera o denominador será vista mais a frente e utiliza o método do

Exemplo 38.

O Exemplo 32 usa o método adicaoOuSubtracaoSemDenominadores do

Exemplo 38. Este método adiciona apenas os numeradores dos tipos Number

passados como parâmetro desconsiderando o denominador, assim como o método

do Exemplo 37. Este método é sempre usado após ser efetuada o ajuste dos

denominadores ou cálculo do mmc para os denominadores. O método recebe como

parâmetro os números a serem adicionados e um boleano que indica se verdadeiro

uma adição, caso contrário uma subtração. Se uma subtração for escolhida inverte-se

o sinal do segundo número passado como parâmetro.

Exemplo 38. Adição ou Subtração desconsiderando os denominadores

private static Number adicaoOuSubtracaoSemDenominadores(Number a, Number b,

boolean isAdicao){

Number n = new Number();

if (!isAdicao) {

b.sinal = -b.sinal;

}

normalizarNumerosDesconsiderandoDenominador(a,b);

String [] soma = avaliaSinaisRealizaOperacaoAdicaoOuSubtracao(

a.mantissa, b.mantissa, a.sinal, b.sinal);

n.sinalExpoente = a.sinalExpoente;

n.expoente = a.expoente;

n.sinal = Integer.parseInt(soma[0]);

n.mantissa = soma[1];

if (!isAdicao) {

b.sinal = -b.sinal;

}

return n;

}

Antes de avaliar os sinais e o tamanho das mantissas para efetuar a Adição ou

subtração é preciso coloca-las sob a mesma ordem de grandeza, isto é, igualar os

expoentes adicionando zeros à esquerda da mantissa do número de maior expoente.

O método normalizarNumerosDesconsiderandoDenominador do Exemplo 39

realiza este procedimento. Inicialmente o método avalia os sinais dos expoentes, se

os sinais forem iguais uma subtração entre as mantissas será realizada para calcular

a quantidade de zeros que serão concatenados a mantissa de maior expoente.

Se os expoentes tiverem sinais diferentes, uma adição será realizada para a

determinar quantidade de zeros a serem concatenados. Os zeros sempre são

Page 80: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

67

concatenados a mantissa do número que contem o maior expoente e os expoentes

igualados. A Figura 20 ilustra esta operação:

( ) ( )5535 104500000000,101031045,10103 −−−

××=××f

Figura 20 - Operação de normalização que iguala os expoentes de dois números

Exemplo 39. Coloca dois números sob a mesma ordem de grandeza

private static void normalizarNumerosDesconsiderandoDenominador(Number a,

Number b){

boolean aMaior = false;

//mesmos sinais

if (a.sinalExpoente == b.sinalExpoente) {

String diferencaExpoentes = "0";

if ((compareStrings(a.expoente, b.expoente) > 0)) {

diferencaExpoentes = subtracaoNaturalStrings(a.expoente, b.expoente);

aMaior = true;

} else if (compareStrings(a.expoente, b.expoente) < 0){

diferencaExpoentes = subtracaoNaturalStrings(b.expoente, a.expoente);

}

if ((a.sinalExpoente == SINAL_POSITIVO && aMaior) ||

(a.sinalExpoente == SINAL_NEGATIVO && !aMaior)) {

a.expoente = b.expoente;

a.mantissa =

completeWithRightZeros(a.mantissa,diferencaExpoentes);

} else {

b.expoente = a.expoente;

b.mantissa =

completeWithRightZeros(b.mantissa,diferencaExpoentes);

}

} else {

//sinais diferentes

String somaExpoentes = "";

somaExpoentes = adicaoNaturalStrings(a.expoente,b.expoente);

if (a.sinalExpoente > b.sinalExpoente) {

a.sinalExpoente = b.sinalExpoente;

a.expoente = b.expoente;

a.mantissa = completeWithRightZeros(a.mantissa, somaExpoentes);

} else if (b.sinalExpoente > a.sinalExpoente){

b.sinalExpoente = a.sinalExpoente;

b.expoente = a.expoente;

b.mantissa = completeWithRightZeros(b.mantissa, somaExpoentes);

}

}

}

3.5.3.2 MULTIPLICAÇÃO RACIONAL

O algoritmo da multiplicação (Exemplo 40) multiplica inicialmente os

denominadores através do algoritmo do Exemplo 37 que desconsidera os

denominadores, verifica se o denominador de algum dos denominadores é igual a

Page 81: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

68

null. Se algum deles for nulo: o denominador do resultado será igual ao denominador

não-nulo. Se ambos forem não-nulos o denominador do resultado é calculado pela

multiplicação dos denominadores utilizando-se o algoritmo do Exemplo 37.

Exemplo 40. Algoritmo da Multiplicação.

public static Number multiplicar (Number a, Number b) {

Number r = multiplicacaoDesconsiderandoDenominador(a,b);

if (a.denominador == null && b.denominador != null) {

r.denominador = b.denominador.clonar();

} else if (a.denominador != null && b.denominador == null) {

r.denominador = a.denominador.clonar();

} else if (a.denominador != null && b.denominador != null) {

r.denominador = multiplicacaoDesconsiderandoDenominador(a.denominador,

b.denominador);

}

return r;

}

3.5.3.3 DIVISÃO RACIONAL

Este algoritmo utiliza a operação de divisão racional que desconsidera o sinal

[ver Seção 3.5.2.7]. O algoritmo recebe como parâmetro o dividendo, o divisor, uma

variável boleana que indica se o resultado da operação permanecerá na forma de

fracionária ou não e a quantidade de casas decimais (precisão), caso se decida por

um resultado na forma não fracionária.

O primeiro passo do algoritmo da divisão racional é verificar se os

denominadores são nulos, se isto for verdade, é atribuído ao denominador o número

“1”. Multiplica-se então o numerador do dividendo pelo denominador do divisor e é

atribuído ao numerador da variável de retorno (r) este resultado. Em seguida, é

multiplicado o denominador do dividendo pelo numerador do divisor e este resultado é

atribuído ao denominador da variável de retorno. Para diminuir a quantidade de

caracteres no denominador utiliza-se o método eliminaExpoenteSinal-

Denominador do Exemplo 34.

Se a variável que decide se o resultado permanecerá na forma fracionária foi

atribuído o valor falso, o algoritmo realiza a operação de divisão racional

desconsiderando o sinal [ver Seção 3.5.2.7] entre as mantissas do numerador e do

denominador da variável de retorno. O resultado desta divisão retorna um tipo

Number que ainda precisa ser multiplicado pela ordem de grandeza da variável de

retorno, determinada pelo seu expoente que não foi considerado na divisão. A

Page 82: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

69

multiplicação entre as ordens de grandeza é calculada através do método

avaliaSinaisRealizaOperacaoAdicaoOuSubtracao definido no Exemplo 35.

Exemplo 41. Algoritmo da Divisão.

public static Number dividir (Number a, Number b, boolean fracionar,

int casasDecimais) throws ExcecaoDivisaoPorZero {

if (a.denominador == null) {

try {

a.denominador = new Number("1");

} catch (Exception e) {}

}

if (b.denominador == null) {

try {

b.denominador = new Number("1");

} catch (Exception e) {}

}

Number r = multiplicacaoDesconsiderandoDenominador(a,b.denominador);

r.denominador = multiplicacaoDesconsiderandoDenominador(a.denominador,b);

eliminaExpoenteSinalDenominador(r);

if (!fracionar) {

Number divisaoMantissas = divisaoRacionalStrings(

r.mantissa,r.denominador.mantissa,false, casasDecimais);

r.mantissa = divisaoMantissas.mantissa;

String [] expoente = avaliaSinaisRealizaOperacaoAdicaoOuSubtracao(

r.expoente, divisaoMantissas.expoente, r.sinalExpoente,

divisaoMantissas.sinalExpoente);

r.sinalExpoente = Integer.parseInt(expoente[0]);

r.expoente = expoente[1];

r.denominador = null;

} else {

simplificarMantissaDenominadores(r);

}

return r;

}

Caso se deseje manter o resultado da divisão na forma fracionária, simplifica-

se a mantissa do denominador e do numerador da variável resultado se for possível

(mdc > 1), calculando-se o mdc [ver Seção 3.5.2.5] entre o numerador e o

denominador e dividindo estes últimos pelo seu mdc.

3.5.4 COMPARAÇÃO ENTRE TIPOS NUMBER

O algoritmo da comparação recebe dois tipos Number e define a relação de

ordem entre o primeiro parâmetro e o segundo parâmetro. Se o primeiro parâmetro

for maior que o segundo o método retorna um inteiro (int) igual a 1 . Se os

parâmetros forem iguais retorna o valor 0 e se o primeiro parâmetro for menor que o

segundo retorna o valor –1. o algoritmo da comparação está definido no Exemplo 42.

Page 83: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

70

O método comparar (Exemplo 42) clona os dois parâmetros de entrada e os

atribui a duas variáveis diferentes pois modificações em seus atributos podem ser

efetuadas no decorrer da operação. Para determinar a ordem de precedência

inicialmente avaliam-se os sinais dos números. Caso os sinais sejam iguais é preciso

igualar seus denominadores utilizando o método colocarMesmaBase-

Denominadores (Exemplo 33). Depois de igualar os denominadores é necessário

colocar os numeradores sob a mesma ordem de grandeza através do método

normalizarNumerosDesconsiderandoDenominador (Exemplo 39). Estando

números sob a mesma ordem de grandeza comparam-se suas mantissas com o

método compareStrings (Exemplo 10). Se os sinais dos números passados como

parâmetro forem negativos, o resultado da comparação de mantissas precisa ser

invertido.

Exemplo 42. Algoritmo de Comparação

public static int comparar (Number a, Number b) {

int ret = 0;

Number m = a.clonar();

Number n = b.clonar();

if (m.sinal > n.sinal){

ret = 1;

} else if (m.sinal < n.sinal) {

ret = -1;

} else { //sinais iguais

colocarMesmaBaseDenominadores(m,n);

m.denominador = null;

n.denominador = null;

normalizarNumerosDesconsiderandoDenominador(m,n);

ret = compareStrings(m.mantissa,n.mantissa);

if (m.sinal == SINAL_NEGATIVO) {

ret = -ret;

}

}

return ret;

}

Page 84: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

71

4. RESULTADOS

Neste Capítulo serão apresentados os resultados obtidos com a biblioteca

desenvolvida nesta dissertação. Para validar estes resultados foi utilizado o JDK 1.4.2

da Sun [ref] rodando num Sistema Operacional Windows XP com processador

Celeron da Intel de 1.8 GHz.

Para validação dos resultados da biblioteca intervalar Java neste trabalho

desenvolvida foram realizados cálculos utilizando as operações definidas nas seções

anteriores e os resultados foram comparados com o software IntpakX [INTPAKX], que

é uma extensão intervalar do Maple [MAPLE] e com a biblioteca JAVA-XSC [JAVA-

XSC] em alguns casos. A escolha da ferramenta Maple foi motivada por sua ampla

divulgação na literatura. A versão do software escolhida foi a 9.0.

Para realização dos testes foram criados nas próprias classes da biblioteca

métodos estáticos (assinatura public static void main(String args[]))

para execução das funções implementadas. Ou seja, para cada operação

desenvolvida eram realizados testes unitários, cujos valores eram comparados com

os resultados do Maple. Os Algoritmos de teste estão em vermelho e os resultados da

saída padrão estão destacados em azul.

4.1 OPERAÇÕES ARITMÉTICAS COM STRINGS

Os resultados desta seção envolvem operações com cadeias de caracteres

numéricos portanto, cadeias de números naturais. Têm-se então as operações

aritméticas, o máximo divisor comum e o mínimo múltiplo comum. Em cada

subseção, é comparado, quando possível, o resultado utilizando a técnica de

processamento de Strings utilizando as operações do tipo Number, o tipo double, o

tipo java.math.BigDecimal e o resultado do Maple.

4.1.1 ADIÇÃO

Esta seção mostra como foi validada a operação de adição de Strings [ver

Seção 3.5.2.1].

Page 85: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

72

Java

private void testeAdicao(){

String a = "9857433243498432837248972948792837424";

String b = "932480923482343248923897492734987324879832";

System.out.println("adicao: " + Number.adicaoNaturalStrings(a,b));

double ad = 9857433243498432837248972948792837424.;

double bd = 932480923482343248923897492734987324879832.;

System.out.println("adicao double: " + (ad + bd));

BigDecimal ab =

new BigDecimal(9857433243498432837248972948792837424.);

BigDecimal bb =

new BigDecimal(932480923482343248923897492734987324879832.);

System.out.println("adicao BigDecimal: " + (ab.add(bb)));

}

adicao : 932490780915586747356734741707936117717256

adicao double : 9.324907809155868E41

adicao BigDecimal: 932490780915586727029735553981435325972480

Maple

> 9857433243498432837248972948792837424 +

932480923482343248923897492734987324879832;

932490780915586747356734741707936117717256

4.1.2 SUBTRAÇÃO

Esta seção mostra como foi validada a operação de subtração de Strings [ver

Seção 3.5.2.2].

Java

private static void testeSubtracao(){

String a = "100000000000000000000004343494";

String b = "10000000000000234343443430000000";

System.out.println("subtracao: " +

Number.subtracaoNaturalStrings(b,a));

double ad = 100000000000000000000004343494.;

double bd = 10000000000000234343443430000000.;

System.out.println("subtracao double : " + (bd - ad));

BigDecimal ab = new BigDecimal(100000000000000000000004343494.);

BigDecimal bb = new BigDecimal(10000000000000234343443430000000.);

System.out.println("subtracao BigDecimal: " + (bb.subtract(ab)));

}

subtracao : 9900000000000234343443425656506

subtracao double : 9.900000000000233E30

subtracao BigDecimal: 9900000000000233831643767373824

Page 86: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

73

Maple

> 10000000000000234343443430000000 -

100000000000000000000004343494;

9900000000000234343443425656506

4.1.3 MULTIPLICAÇÃO

Esta seção mostra como foi validada a operação de multiplicação de Strings

[ver Seção 3.5.2.3].

Java

private static void testeMultiplicacao(){

String a = "9864959123598765767463";

String b = "19875897459485";

System.out.println("mult: " +

Number.multiplicacaoNaturalStrings(b,a));

double ad = 9864959123598765767463.;

double bd = 19875897459485.;

System.out.println("mult double : " + (bd * ad));

BigDecimal ab = new BigDecimal(9864959123598765767463.);

BigDecimal bb = new BigDecimal(19875897459485.);

System.out.println("mult BigDecimal: " + (bb.multiply(ab)));

}

mult : 196074915982660080627999427973736555

mult double : 1.9607491598266007E35

mult BigDecimal: 196074915982660079675725304792350720

Maple

> 9864959123598765767463 * 19875897459485; 196074915982660080627999427973736555

4.1.4 DIVISÃO INTEIRA

Para a operação de divisão inteira [ver Seção 3.5.2.4] não foram considerados

os tipos BigDecimal e double pois não existe a operação definida para estes tipos

em java. Os tipos long e int que possuem a operação de divisão inteira, trabalham

com poucas casas decimais e não foram levados em consideração. A função evalf do

maple avalia a expressão que o Maple consideraria como uma fração e converte-a

para ponto-flutuante com a precisão especificada entre colchetes([]);

Page 87: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

74

Java

private static void testaDivisaoInteira () {

String a = "97435987459375743";

String b = "3427598782398423894983248973284324324";

System.out.println("div: " + Number.divisaoNaturalStrings(b,a));

}

div: 35177955001764642450

Maple

> evalf[20](3427598782398423894983248973284324324 /

97435987459375743);

4.1.5 RESTO DA DIVISÃO INTEIRA Para validar o resto da divisão inteira [ver Seção 3.5.2.4] não foram levados

em consideração, assim como na validação da divisão inteira, os tipos BigDecimal,

double, long e int pelos mesmos motivos.

Java

private static void testaRestoDivisaoInteira () {

String a = "97435987459375743";

String b = "3427598782398423894983248973284324324";

System.out.println("resto: " +

Number.restoDivisaoNaturalStrings(b,a));

}

resto: 9201886686233974

Maple

> 3427598782398423894983248973284324324 mod 97435987459375743;

9201886686233974

4.1.6 MÁXIMO DIVISOR COMUM Para validar o mdc [ver Seção 3.5.2.5] foi utilizado o método gcd (greatest

common divisor) do Maple que calcula o máximo divisor comum.

Java

private static void testeMDC() {

String a = "8726346348765843765";

String b = "982734873297492837432";

System.out.println("mdc: " + Number.mdc(b,a));

}

mdc: 3

Maple

> gcd (8726346348765843765, 982734873297492837432);

3

Page 88: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

75

4.1.7 MÍNIMO MULTIPLO COMUM

Para validar o mmc [ver Seção 3.5.2.6] foi utilizado o método lcm (least

common multiple) do Maple que calcula o máximo divisor comum.

Java

private static void testeMMC() {

String a = "50400";

String b = "1230390";

System.out.println("mmc: " + Number.mmc(b,a));

}

mmc: 98431200

Maple

> lcm (50400, 1230390); 98431200

4.2 OPERAÇÕES ARITMÉTICAS COM O TIPO NUMBER

A partir das operações com cadeias de caracteres, o passo seguinte no

desenvolvimento da biblioteca desta dissertação foi implementar as operações com

números racionais, as quais são realizadas com o tipo Number definidos no Capítulo

3. O inverso aditivo não foi implementado como um método, pois é facilmente

calculado através da troca do sinal algébrico.

4.2.1 ADIÇÃO

Esta seção mostra como foi validada a operação de adição de tipos Number

[ver Seção 3.5.3.1]. o método toScientificNotation(int)retorna uma String

com a representação do objeto Number na notação científica, e o inteiro passado

como parâmetro especifica a quantidade de casas após a virgula que devem ser

mostradas. No método abaixo foram escolhidas 20 casas.

Java

private static void testeAdicaoNumber(){

Number a = new Number("-98123713E234/123123E123");

Number b = new Number("-9123923829/12");

System.out.println(“=” +

Number.adicionar(a,b).toScientificNotation(20));

}

=-7,96956807420222054368E113

Page 89: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

76

Maple

> eval[20]((-98123713E234/123123E123) + (-9123923829/12));

4.2.2 SUBTRAÇÃO Esta seção mostra como foi validada a operação de subtração de tipos

Number [ver Seção 3.5.3.1].

Java

private static void testeSubtracaoNumber(){

Number a = new Number("34765783465E3");

Number b = new Number("8123923829345435/12");

System.out.println("=" +

Number.subtrair(a,b).toScientificNotation(20));

}

=-6,42227868980452916667E14

Maple

> 34765783465E3 - 8123923829345435/12;

4.2.3 MULTIPLICAÇÃO

Esta seção mostra como foi validada a operação de multiplicação de tipos

Number [ver Seção 3.5.3.2].

Java

private static void testeMultiplicaoNumber(){

Number a = new Number("762354762534765324/-987");

Number b = new Number("-25647864356254874/32E-2");

System.out.println("=" +

Number.multiplicar(a,b).toScientificNotation(10));

}

=6,1907204727E31

Maple

> (762354762534765324/(-987)) * (-25647864356254874/32E-2);

4.2.4 DIVISÃO Esta seção mostra como foi validada a operação de divisão de tipos Number

[ver Seção 3.5.3.3]. Três possíveis tipos de retorno foram testados utilizando a

notação científica, a representação interna e a forma fracionária.

Page 90: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

77

Java

private static void testeDivisaoNumber(){

Number a = new Number("9873892173983/234342");

Number b = new Number("3489756345345/49538457");

System.out.println("=" +

Number.dividir(a,b,true,1).toScientificNotation(10));

System.out.println("=" + Number.dividir(a,b,false,10));

System.out.println("=" + Number.dividir(a,b,true,10));

}

=5,9811627215E2

=5981162721534E-10

=54348598098165929359/(90866275720093110)

Maple

>eval[10]((9873892173983/234342)/(3489756345345/49538457));

4.2.5 INVERSO MULTIPLICATIVO

Esta seção mostra como foi validada a operação de inverso multiplicativo de

um tipo Number. No Maple o símbolo “^” significa elevar o numero a um expoente.

Java

private static void testeInversoMultiplicativoNumber(){

Number a = new Number("436587436756/1234");

System.out.println("=" + Number.inversoMultiplicativo(a));

System.out.println("=" +

Number.inversoMultiplicativo(a).toScientificNotation(10));

}

=617/(218293718378)

=2,8264670398E-9

Page 91: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

78

Maple

> evalf[10]((436587436756/1234)^(-1));

4.3 OPERAÇÕES INTERVALARES

As operações Intervalares utilizam o tipo Intervalo desenvolvido neste trabalho

[ver Seção 3.1]. Os resultados das operações serão comparados com o Maple

utilizando sua biblioteca intervalar (intpakX [INTPAKX]) e a biblioteca Java-XSC

[JAVA-XSC] que implementa operações intervalares utilizando o tipo primitivo double.

4.3.1 ADIÇÃO Esta seção compara os resultados das operações de adição intervalar. [ver

Seção 2.6.3.1].

Java com strings

private static void testeAdicaoIntervalar(){

Intervalo a = new Intervalo("822/1234","833/1234");

Intervalo b = new Intervalo("-12E-3","1582E-2");

System.out.println("=" + new Adicao

(a,b).toScientificNotation(10));

}

=[6,5412641815E-1 , 1,6495040519E1]

Java-XSC

private static void adicaoJavaXSC(){

double a1 = 822./1234.;

double a2 = 833./1234.;

double b1 = -12E-3;

double b2 = 1582E-2;

Interval a = new Interval(a1,a2,10);

Interval b = new Interval(a1,a2,10);

System.out.println("=" + IntervalElementary.add(a,b));

}

=[0.6541264181, 16.4950405187]

Maple

> a:=construct(822/1234,833/1234);type(a,interval);

> b:=construct(-12E-3,1582E-2);type(b,interval);

> a&+b;

Page 92: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

79

4.3.2 SUBTRAÇÃO

Esta seção compara os resultados das operações de subtração intervalar. [ver

Seção 2.6.3.3], que usa o pseudo-inverso aditivo [ver Seção 2.6.3.2].

Java com strings private static void testeSubtracaoIntervalar(){

Intervalo a = new Intervalo("-199121212","-199121212.1");

Intervalo b = new Intervalo("-199121211.77","-199121211.7");

System.out.println("=" + new Subtracao

(a,b).toScientificNotation(10));

}

=[-4E-1 , -2,3E-1]

Java-XSC private static void subtracaoJavaXSC(){

double a1 = -199121212;

double a2 = -199121212.1;

double b1 = -199121211.77;

double b2 = -199121211.7;

Interval a = new Interval(a1,a2,10);

Interval b = new Interval(b1,b2,10);

System.out.println(“=” + IntervalElementary.sub(a,b));

}

=[-0.400000006, -0.2299999892]

Maple >a:=construct(-199121212,-199121212.1);type(a,interval);

>b:=construct(-199121211.77,-199121211.7);type(b,interval);

> a&-b;

4.3.3 MULTIPLICAÇÃO

Esta seção compara os resultados das operações de multiplicação intervalar.

[ver Seção 2.6.3.4].

Page 93: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

80

Java com strings private static void testeMultiplicacaoIntervalar(){

Intervalo a = new Intervalo("-0.1212212","0.32456");

Intervalo b = new Intervalo("1.00034444","1.00000345");

System.out.println("=" +

new Multiplicacao (a,b).toScientificNotation(10));

}

=[-1,2126295343E-1 , 3,2467179145E-1] Java-XSC private static void multiplicacaoJavaXSC(){

double a1 = -0.1212212;

double a2 = 0.32456;

double b1 = 1.00034444;

double b2 = 1.00000345;

Interval a = new Interval(a1,a2,10);

Interval b = new Interval(b1,b2,10);

System.out.println("=" + IntervalElementary.mult(a,b));

}

=[-0.1212629535, 0.3246717915]

Maple > a:=construct(-0.1212212,0.32456);type(a,interval);

> b:=construct(1.00034444,1.00000345);type(b,interval);

> a&*b;

4.3.4 INVERSO MULTIPLICATIVO

Esta seção compara os resultados das operações de Inverso multiplicativo

Intervalar (ou recíproco). [ver Seção 2.6.3.5].

Java com Strings private static void testeInversomultiplicativo(){

Intervalo a = new Intervalo("98383838","98383849");

System.out.println("=" + new

InversoMultiplicativo(a).toScientificNotation(10));

}

=[1,016426995E-8 , 1,0164271087E-8]

Page 94: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

81

Java-XSC private static void reciprocoJavaXSC(){

double a1 = 98383838.;

double a2 = 98383849.;

Interval a = new Interval(a1,a2,10);

System.out.println("=" + a.reciprocal());

}

=[1.01E-8, 1.02E-8]

Maple > a:=construct(98383838,98383849);type(a,interval);

> Interval_reciprocal(a);

4.3.5 DIVISÃO Esta seção compara os resultados das operações de divisão intervalar. [ver

Seção 2.6.3.6].

Java com strings private static void testeDivisaoIntervalar(){

Intervalo a = new Intervalo("-2010220.2234","-2010220.9899");

Intervalo b = new Intervalo("50021","50224.23");

System.out.println("=" + new

Divisao(a,b).toScientificNotation(10));

}

=[-4,0187541031E1 , -4,0024908762E1]

Java-XSC private static void divisaoJavaXSC(){

double a1 = -2010220.2234;

double a2 = -2010220.9899;

double b1 = 50021;

double b2 = 50224.23;

Interval a = new Interval(a1,a2,10);

Interval b = new Interval(b1,b2,10);

System.out.println("=" + IntervalElementary.div(a,b));

}

=[-40.1875410308, -40.0249087621]

Page 95: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

82

Maple > a:=construct(-2010220.2234,-2010220.9899);

type(a,interval);

> b:=construct(50021, 50224.23);type(b,interval);

> a&/b;

4.3.6 INTERSECÇÃO Esta seção compara os resultados entre da operação Intersecção de intervalos

[ver Seção 3.6.4.1];

Java com strings private static void testeInterseccaoIntervalar(){

Intervalo a = new Intervalo("230203","-2010220.9899");

Intervalo b = new Intervalo("87686","-120215.23");

System.out.println("=" + new

Interseccao(a,b).toScientificNotation(10));

}

=[-1,2021523E5 , 8,7686E4]

Java-XSC private static void intersecacaoJavaXSC() {

Interval a = new Interval(230203., -2010220.9899,10);

Interval b = new Interval(87686.,-120215.23,10);

System.out.println("=" + IntervalSet.interscetion(a,b));

}

=[-120215.23, 87686.0]

Maple > a:=construct(230203,-2010220.9899);type(a,interval);

> b:=construct(87686, -120215.23);type(b,interval);

> a &intersect b;

Page 96: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

83

4.3.7 UNIÃO

Esta seção compara os resultados entre da operação União de intervalos [ver

Seção 3.6.4.2];

Java com strings private static void testeUniaoIntervalar(){

Intervalo a = new Intervalo("-0.120120","1.12122112");

Intervalo b = new Intervalo("0.0001212","-1.234434");

System.out.println("=" + new Uniao(a,b).toScientificNotation(10));

}

=[-1,234434 , 1,12122112]

Java-XSC private static void uniaoJavaXSC() {

Interval a = new Interval(-0.120120,1.12122112,10);

Interval b = new Interval(0.0001212,-1.234434,10);

System.out.println("=" + IntervalSet.union(a,b));

}

=[-1.234434, 1.12122112]

Maple > a:=construct(-0.120120,1.12122112);type(a,interval);

> b:=construct(0.0001212,-1.234434);type(b,interval);

> a &union b;

4.3.8 DISTÂNCIA Esta seção compara os resultados entre da operação Distância de intervalos

[ver Seção 3.6.5.1]; O Maple não implementa esta operação.

Java com strings

private static void testeDistanciaIntervalar(){

Intervalo a = new Intervalo("-0.3726483838","1.293874");

Intervalo b = new Intervalo("0.39393993933","-3.983274");

System.out.println("=" +

Intervalo.getDistancia(a,b).toScientificNotation(10));

}

=3,6106256162

Page 97: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

84

Java-XSC private static void distanciaJavaXSC(){

Interval a = new Interval(-0.3726483838,1.293874,10);

Interval b = new Interval(0.39393993933,-3.983274,10);

System.out.println("=" + IntervalUtil.distance(a,b));

}

=3.6106256162

4.3.9 DIÂMETRO Esta seção compara os resultados entre da operação diâmetro de intervalos

[ver Seção 3.6.5.2];

Java com strings private static void testeDiametro(){

Intervalo a = new Intervalo("873264263.12341243","873264263.2948");

System.out.println("=" +

a.getDiametro().toScientificNotation(16));

}

=1,7138757E-1

Java-XSC private static void diametroJavaXSC(){

Interval a = new Interval(873264263.12341243,873264263.2948, 16);

System.out.println("=" + a.width());

}

=0.1713876724243164

Maple >a:=construct(873264263.12341243,873264263.2948);

type(a,interval);

> width(a);

4.3.10 PONTO MÉDIO

Esta seção contém os resultados da operação ponto médio de um intervalo

[ver Seção 3.6.5.3]; Apenas a biblioteca desenvolvida neste sistema implementa esta

operação.

Page 98: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

85

Java com strings private static void testePontoMedio(){

Intervalo a = new Intervalo("873264263.12341243","873264263.2948");

System.out.println("=" +

a.getPontoMedio(10).toScientificNotation(10));

}

=8,7326426321E8

4.3.11 VALOR ABSOLUTO Esta seção compara os resultados entre da operação valor absoluto de um

intervalo [ver Seção 3.6.5.4]; O Maple não implementa esta operação.

Java com strings private static void testeValorabsoluto(){

Intervalo a = new Intervalo("873264263.12341243","873264263.2948");

System.out.println("=" +

a.valorAbsoluto().toScientificNotation(15));

}

=8,732642632091062E8

Java-XSC private static void valorabsolutoJavaXSC(){

Interval a = new Interval(873264263.12341243,873264263.2948,15);

System.out.println("=" + IntervalUtil.abs(a));

}

=8.732642632948E8

4.4 COMPARAÇÃO ENTRE RESULTADOS

A Tabela 1 na seqüência apresenta os resultados obtidos na Seção 4.3

usando a biblioteca desenvolvida nesta dissertação, Java-XSC [JAVA-XSC] e o

Maple Intervalar [INTPAKX]. S é o resultado das operações com Strings, J o resultado

com Java-XSC e M o resultado com o Maple Intervalar. Sendo |S - J| e |S - M| o valor

absoluto das distâncias [Ver Seção 2.6.5.1] intervalares entre os resultados.

Page 99: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

Tabela 1 - Comparação entre resultados Obtidos com Java com Strings, Java-XSC e o Maple Intervalar

Operação Java com Strings Java-XSC Maple Intervalar | S – J| |S - M|

Adição [6,5412641815E-1

,1,6495040519E1]

[0.6541264181,

16.4950405187]

[0.6541264180,

16.49504053] 3E-10 11E-9

Subtração [-4E-1 , -2,3E-1] [-0.400000006, -

0.2299999892]

[-.4000000001, -

.1999999999] 108E-10

300000001E-

10

Multiplicação [-1,2126295343E-1,

3,2467179145E-1]

[-0.1212629535,

0.3246717915]

[-.1212629535,

0.3246717915] 7E-11 7E-11

Recíproco [1,016426995E-8,

1,0164271087E-8] [1.01E-8,1.02E-8]

[1.016426994E-8,

1.016427110E-8] 6426995E-17 13E-18

Divisão [-4,0187541031E1 , -

4,0024908762E1]

[-40.1875410308, -

40.0249087621]

[-40.18754107, -

40.02490873] 2E-10 39E-9

União [-1,234434, 1,12122112] [-1,234434, 1,12122112] [-1,234434, 1,12122112] 0 0

Intersecção [-1,2021523E5 ,

8,7686E4] [-120215.23, 87686.0] [-1.2021523E5, 87686] 0 0

Distância 3,6106256162 3.6106256162 - 0 -

Diâmetro 1,7138757E-1 0.1713876724243164 0.2 1024243164E-

16 2861243E-8

Ponto Médio 8,7326426321E8 - - - -

Valor Absoluto 8,732642632091062E8 8.732642632948E8 - 856938E-7 -

Page 100: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

87

4.5 COMPARAÇÃO DO DESEMPENHO A Tabela 2 a seguir apresenta uma comparação entre o desempenho das

operações realizadas processando-se Strings e Java-XSC, através da métrica tempo.

A comparação não envolve o Maple intervalar, uma vez que a ferramenta para

mensurar o tempo não seria a mesma (JBuilder 7, JDK 1.4.2, o cálculo do tempo é

realizado através da medição do tempo do relógio da máquina antes e depois da

operação ser efetuada). Foram realizadas 1000 execuções, comentando-se as

operações de I/O (System.out.println); o maior tempo de execução, t, é o que foi

considerado nesta tabela, assim como o tempo total, T, para realizar 1000 vezes a

operação.

Tabela 2 - Análise do desempenho entre Java com Strings e Java-XSC

Java com Strings (ms) Java-XSC (ms)

Operação t T t T

Adição 40 821 10 70

Subtração 31 121 10 80

Multiplicação 30 120 11 71

Recíproco 20 60 30 60

Divisão 30 130 31 71

União 20 100 10 40

Intersecção 20 80 10 60

Distância 30 130 10 60

Diâmetro 20 100 10 60

Ponto Médio 40 110 - -

Valor Absoluto 30 120 30 110

Page 101: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

88

Com os resultados das Tabelas 1 e 2 pode-se observar que o Maple e a API

Java-XSC apresentam pequenas diferenças em relação a técnica de processamento

de Strings envolvendo operações com baixa precisão. A velocidade das operações

com Strings por serem feitas via software são intrinsecamente mais lentas, porem

estão com um bom desempenho.

Page 102: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

89

5 CONCLUSÕES E TRABALHOS FUTUROS

Este trabalho implementou uma biblioteca intervalar em Java via

processamento de Strings. A principal motivação para o desenvolvimento deste foi a

busca por cálculos numéricos com altas precisão e exatidão. As principais conclusões

foram:

A representação de números racionais processados através de strings permite

que se trabalhe com precisão e exatidão superiores à Java-XSC e o Maple Intervalar

(Tabela 1), sendo o custo desta exatidão refletido no tempo das operações (Tabela

2).

Enfatiza-se que, para qualquer uma das operações, executando 1000

repetições, o tempo de processamento é menor do que 1 segundo (Tabela2).

Quando o fator tempo estiver envolvido, como nos sistemas de tempo real, é

aconselhável um estudo prévio daqueles (sistemas) para assegurar que a biblioteca

desenvolvida nesta dissertação realize os cálculos no limite aceitável de tempo para o

uso seguro do sistema.

Este trabalho propôs uma alternativa ao sistema de ponto-flutuante

implementado em Java o qual possui problemas numéricos, como mostrado nos

Capítulos 1 e 2. A comunidade usuária de Java terá acesso à biblioteca que está

disponível em (www.cin.ufpe.br/~javaxsc) em código aberto, possibilitando o

desenvolvimento de possíveis extensões, como as propostas na Seção 5.1.

5.1 TRABALHOS FUTUROS

Como trabalhos futuros sugere-se:

• Implementação das Funções Potência e Raiz

• Implementação das Funções Exponencial e Logarítmica.

• Implementação das Funções Trigonométricas.

Utilização de Expressões Regulares para a construção do tipo Number. Para

tornar o código mais legível e melhorar a manutenibilidade do sistema, serão usadas

Page 103: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

90

expressões regulares que permitem definir um autômato e casamento de padrões em

Strings.

Desenvolvimento de um tipo CadeiaDeCaractere. Este tipo é uma alternativa

ao tipo String estendendo o tamanho das mantissas ao limite da memória da

máquina. O tamanho máximo de uma String está limitado ao tamanho de um tipo

inteiro (int).

Analisar quais as operações que consomem mais tempo de processamento

quando comparadas com as de Java-XSC.

Page 104: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

91

REFERÊNCIAS BOOCH, G., JACOBSON, I., RUMBAUGH, J. The Unified Modeling Language

Reference Manual. Addison-Wesley, 1999.

CAMPOS, M. A; FIGUEIREDO, P. L., Introdução ao Tratamento da Informação

nos Ensinos Fundamental e Médio, 2005.

CAMPOS M. A., Implementing the Type Interval and Proving its Fundamental

properties in the OBJ3 System. Exame de Qualificação. Departamento de

Informática, UFPE.1995.

CHOUDHARI, P., Java Advantages & Disadvantages, 2001. Disponível em:

http://arizonacommunity.com/articles/java_32001.shtml Acesso em: 12/06/2005.

DIVÉRIO, T. A.; OLIVEIRA, P. W; CLAUDIO, D. M. Fundamentos da Matemática

Intervalar. Porto Alegre: Sagra-Luzzatto, 1997. 93p. (Série Matemática da

Computação e Processamento Paralelo, v.1, Instituto de Informática da UFRGS,

Projeto ArInPar-ProTeM-CNPq) ISBN 85-241-0515-1

DUTRA, Enéas Montenegro Dutra. Java XSC: Uma Biblioteca Java para

Computações Intervalares. Dissertação de Mestrado em Ciências da Computação,

Departamento de Informática e Matemática Aplicada, Universidade Federal do Rio

Grande do Norte, Natal, 2000.

GAJSKI, D.D., Principles of Digital Design, Prentice Hall, 1997

GOSLING, J.; JOY, B., STEELE G., (1996), The Java Language Specification,

Disponível em: http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html.

Acesso em 31/07/2007

HÖLBIG, C.A. Sistema de Ponto-flutuante e Padrão IEEE-754. 2006.

HOPCROFT, E.J., ULLMAN J.D. Introduction to Automata Theory, Languages

and Computation, Addison-Wesley publishing company, 1979. ISBN:020102988

Page 105: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

92

INTPAKX. Disponível em http://www.math.uni-wuppertal.de/~xsc/software/intpakX/.

Acesso em: 31/07/2007.

JAVA COM STRINGS, Uma Biblioteca Intervalar baseada em Processamento de

Strings, Diponível em http://www.cin.ufpe.br/~javaxsc. Acesso em 31/07/2007.

JAVA-XSC. Java para Computação científica com Controle automático do Erro.

Disponível em http://www.cin.ufpe.br/~javaxsc. Acesso em 31/07/2007.

KULISCH, U. W; A new aritmetic for Scientific Computation. Em U. W Kulisch abd

W.L. Miranker, editors. A new Approach to Scientific Computation, pages 1 – 26.

Proceeding of Symposium held at IBM Researche Center, Torktown Heights, N. Y,

Academic Press, 1983. (ok)

MACAULAY INSTITUTE, Float Point Arithmetic and Java, 2004. Disponível em

http://macaulay.ac.uk/fearlus/float-point/javabugs.html acesso em: 31/07/2007

MAPLE, MapleSoft.com. Disponível em: http://www.maplesoft.com/. Acesso em

31/07/2007.

MOORE, R. E. Methods and Aplications of Intervals Analysis. Society for

Industrial and Applied Mathematics, Philadhelpia, PA, USA, 1979.

SUNAGA, Theory of an Interval Algebra and its Application to Numerical

Analysis, RAAG Memoirs, 1958 (ok)

SUN MICROSYSTEMS, Java Language Overview, Java 2 Platform, Standard Edition, White Papers. Disponível em: http://java.sun.com/docs/overviews/java/java-overview-1.html. Acesso em: 31/07/2007. UNICODE, Unicode Home Page, 1991-2007 Disponível em http://unicode.org Acesso em: 31/07/2007. VUIK, K. Some Disasters caused by Numerical Errors. Disponível em http://ta.twi.tudelft.nl/users/vuik/wi211/disasters.html . Acesso 31/07/2007.

Page 106: Pós-Graduação em Ciência da Computação · pessoas que conviveram comigo durante o curso de pós-graduação, interagindo, cooperando, dividindo angustias e incentivando. A todos

93