Universidade Federal de Pernambuco Centro de Informática CALCULADORA INTERVALAR EM JAVA Renato Viana Ferreira Recife, 2005 Universidade Federal de Pernambuco Centro de Informática CALCULADORA INTERVALAR EM JAVA Renato Viana Ferreira Orientadora: Marcília Andrade Campos Monografia apresentada à disciplina Iniciação Científica 1 do curso de graduação em Ciência da Computação da UFPE Recife, 2005 Identificação Universidade Federal de Pernambuco Aluno: Renato Viana Ferreira CPF: 057.347.140-58 Curso: Bacharelado em Ciência da Computação Título: Calculadora Intervalar em Java Orientadora: Marcília Andrade Campos Resumo Sistemas computacionais atuais são incapazes de representar todos os números reais, pois estes são contínuos e densos e a representação digital é discreta. A representação destes números é realizada em computadores por meio de ponto flutuante, que provê uma representação binária capaz de armazenar uma quantidade finita de valores em sua forma exata e todos os outros valores devem ser aproximados para o valor representável mais próximo. Java se tornou a linguagem mais utilizada no mundo devido ao seu paradigma de programação, portabilidade, simplicidade e robustez. Por isso, foi desenvolvida uma extensão para computação científica para Java, chamada Java-XSC (eXtension for Scientific Computation), no intuito de solucionar o problema de erro numérico em Java. Com relação à extensão intervalar para Maple, o intpakX, a biblioteca Java-XSC apresentou ótimos resultados após testes de validação, apresentando saídas semelhantes na grande maioria dos casos e também de melhor precisão em algumas operações. Uma calculadora intervalar para Java foi implementada como uma aplicação para a biblioteca, não apenas contendo as operações aritméticas, mas também lógicas. Índice 1. 2. a. b. c. 3. 4. 6. 7. Introdução ................................................................................................................. 6 Revisão Bibliográfica ............................................................................................... 7 Ponto-flutuante em Java ........................................................................................... 7 Erros de Ponto-Flutuante .......................................................................................... 7 Implementação de Ponto-Flutuante em Java ............................................................ 8 Objetivos................................................................................................................... 9 Metodologia ............................................................................................................ 10 Discussões e Conclusões ........................................................................................ 14 Referências ............................................................................................................. 15 1. Introdução Muitos avanços científicos das últimas décadas não seriam possíveis sem a computação de ponto-flutuante dos computadores digitais [Bush, 1996]. Porém, alguns resultados de computação utilizando ponto-flutuante parecem estranhos mesmo para pessoas com vários anos de experiência em computação científica e computacional. Um grande problema enfrentado pelo modelo computacional atual é a representação numérica, devido à continuidade e densidade dos números reais. Estes números não podem ser representados de maneira discreta em sua totalidade, então são representados através de números de ponto-flutuante, que representam uma parcela de valores de maneira exata. A computação de ponto-flutuante trabalha internamente com base 2, binária. Por exemplo, a fração decimal 0.1 não pode ser representada de maneira exata na base 2. Ela é uma dízima periódica 0.00011001100110... Assim como 1/3 = 0.33333… na base 10. Quando somamos 0.333333... com 0.666666... temos 0.999999... ao invés de 1.0, apesar de termos somado 1/3 + 2/3 para obter 1. Então, na base 2, quando somamos 0.1 + 0.1 obtemos algo diferente de 0.2. Uma conclusão crucial é que ponto-flutuante é inexato por natureza [Green, R.,2005], por que nenhuma representação digital é capaz de lidar com todos os números reais É importante notar que como sistemas digitais conseguem representar uma parcela finita dos números reais , todos os outros valores são aproximados para o valor representável mais próximo [Microsoft, 2003] [IEEE, 1985]. A seção 2 desta dissertação apresenta uma revisão bibliográfica com exemplos de erros de ponto-flutuante que ocorrem na linguagem Java. A terceira seção apresenta os objetivos deste projeto. A quarta seção descreve a metodologia de desenvolvimento do projeto seguida da seção de resultados. As conclusões e discussões são apresentadas na seção 6, finalizando o documento com as referências. 2. Revisão Bibliográfica a. Ponto-flutuante em Java Java ganhou enorme popularidade desde que foi criada. Sua rápida ascensão e larga aceitação remetem às possibilidades fornecidas para design e programação, principalmente à promessa de compilar o código uma única vez e rodar em qualquer plataforma. Como descrito nos White papers de Java: “Java é simples, orientada a objetos, distribuída, interpretada, robusta, segura, adaptável a arquiteturas, portável, multithreaded e dinâmica” [Choudhari, P., 2001] [Sun Microsystems, 2005]. Java oferece herança de forma simples, com o uso de interfaces e eliminou o uso de ponteiros, quando comparado com C++. Outra vantagem para os programadores Java é que a JVM (Java Virtual Machine) automaticamente gerencia a alocação de memória e coleta de lixo. Devido a tudo isso, Java, atualmente é a linguagem de programação mais popular do mundo. b. Erros de Ponto-Flutuante Para representar números de ponto-flutuante Java oferece dois tipos primitivos, float e double e duas classes wrapper Float e Double do pacote java.lang [Sun Microsystems, 2005]. O tipo primitive double dá suporte a 16 dígitos decimais, porém apenas precisão para os 14 mais significativos [Gosling, J., Joy, B., Steele G., 1996]. Exemplos de erros de ponto-flutuante são mostrados abaixo: Exemplo 1: Esse exemplo mostra uma simples subtração entre dois números de pontoflutuante, depois compara o resultado obtido com o resultado esperado e imprime o resultado da comparação. double d = 3.9-3.8; if(d==0.1) System.out.println("equals"); else System.out.println("not equals"); A saída deveria ser claramente equals na stdout (standard output), porém imprime not equals, pois d é igual a 0.10000000000000009, mostrando que a subtração retornou um valor incorreto. Exemplo 2: Este exemplo executa dez adições seqüenciais a uma variável e imprime o resultado final. double d = 0.0; for(int i = 0; i < 10; i++){ d += 0.4; } System.out.println(d); Fazemos um loop que adiciona 0.4 dez vezes à variável d, então ela deveria assumir o valor de 10 x 0.4, que é obviamente 4.0, mas o número impresso na saída foi 3.9999999999999996. Para computação científica, que exige alta precisão, esta margem de erro nem sempre aceitável, pois causa resultados errados, o erro deve ser controlado [Ferreira, R. V., Fernandes, B. T., Campos, M. A., 2004]. c. Implementação de Ponto-Flutuante em Java A implementação de ponto-flutuante em Java segue parcialmente o padrão IEEE 754 (1985) para aritmética de ponto-flutuante [The Macaulay Institute, 2004]. Como dito previamente, os tipos primitivos de Java para ponto-flutuante são float e double, representando a precisão simples de 32 bits e precisão dupla de 64 bits do formato IEEE 754 como especificados no IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York). A representação binária para o tipo float é: 1 bit 8 bits 23 bits sign exponent significand e para o tipo double é: 1 bit 11 bits 52 bits sign exponent significand A representação float dá precisão de 6 a 9 dígitos, enquanto double dá de 15 a 17 dígitos de precisão. O problema da implementação de ponto-flutuante em Java é que Java não cumpre todos os requisitos do padrão IEEE 754. Os três principais problemas são: 1. Não suporta os bits de exceção do IEEE 754. 2. Não provê os arredondamentos direcionados. 3. Não provê um tipo intervalar de máquina. Os bits de exceção são: Invalid Operation, Overflow, Division-by-Zero, Underflow, Inexact Result. Sem estes bits é impossível se adequar ao padrão IEEE 754 [The Macaulay Institute, 2004]. 3. Objetivos O objetivo deste projeto é desenvolver uma API (Application Programming Interface) chamada Java-XSC (eXtension for Scientific Computation) que provê controle automático dos erros de ponto flutuante. Implementando: Arredondamentos direcionados; O tipo intervalo; Operações da matemática intervalar; Uma calculadora intervalar; Como Java, atualmente, é linguagem mais popular do mundo, devido a um grande número de vantagens oferecidas, como: simplicidade, portabilidade e robustez, tende a ser mais utilizada em aplicações de computação científica. Visa-se suprir essa necessidade, com a implementação de uma biblioteca chamada Java-XSC (eXtension for Scientific Computation) para controlar erros numéricos de ponto-flutuante utilizando métodos da matemática intervalar [Moore, R. E. (1996)]. 4. Metodologia Este projeto tem a coordenação da professora Marcília Andrade Campos (www.cin.ufpe.br/~mac) no Centro de Informática da Universidade Federal de Pernambuco e conta com a participação de quatro alunos, sendo dois de mestrado e dois da graduação em Ciência da Computação. O primeiro passo para o desenvolvimento de uma biblioteca intervalar para Java (Java-XSC) foi o estudo de arredondamentos direcionados, tanto para cima, como para baixo. Em seguida, uma classe Rounding foi modelada e implementada contendo os arredondamentos e foi validada utilizando a biblioteca intervalar para Maple, intpakX, desenvolvida no Departamento de Matemática da Universidade de Wuppertal na Alemanha. Após a documentação da classe Rounding, foi discutida a criação do tipo intervalo, sobre duas possibilidades: a inserção de um tipo primitivo na linguagem Java ou a criação de uma classe. Verificando-se a impossibilidade de inserção de tipos primitivos na linguagem devido a restrições técnicas e legais, foi modelada e implementada a classe Interval para representar o tipo intervalo contendo seus limites superior e inferior e a precisão desejada. Posteriormente foi feito um estudo sobre operações intervalares, tanto unárias, como lógicas e aritméticas, estes estudos foram realizados através de revisão bibliográfica de teses e artigos relacionados ao tópico e utilização da biblioteca intpakX. As operações unárias foram implementadas na própria classe Interval, pois se referem unicamente a um intervalo. Para a implementação das operações aritméticas foi criada a classe IntervalMath e para as operações lógicas, a classe IntervalSet. Toda a modelagem das classes foi realizada utilizando o Rational Rose Entreprise Edition da Rational/IBM. O desenvolvimento da biblioteca utilizou as versões 1.4.2 e 5.0 de Java e a IDE (Ambiente Integrado de Desenvolvimento) Eclipse, nas versões 2.1, 3.0 e 3.1. 5. Resultados Durante este projeto foi modelada, implementada, documentada e validada uma extensão para computação científica para a linguagem de programação Java. Por fim, também foi implementada uma aplicação para esta biblioteca que foi uma calculadora intervalar. Os arredondamentos direcionados implementados na classe Rounding, quando comparados às funções de arredondamento do intpakX, apresentaram resultados extremamente satisfatórios, obtendo saídas idênticas em 70% dos casos de teste. A diferença nos resultados, quando ocorreu, foi sempre apresentada na última casa decimal, onde em relação à qualidade do arredondamento, ou seja, a menor distância entre o número inicial a ser arredondado e o resultado da função de arredondamento, ocorreu um relativo empate entre o intpakX e a biblioteca Java-XSC. A definição do tipo intervalo foi feita considerando o limite inferior e superior e também a precisão desejada para os arredondamentos, no que Java-XSC difere do intpakX, pois a precisão deste último é invariável. Os limites foram representados pelo tipo primitivo double (ponto-flutuante) e a precisão pelo tipo primitivo int (inteiro). Sobre o tipo intervalo, modelado como uma classe chamada Interval, foram implementadas as seguintes operações unárias: (i) Comprimento; (ii) Simétrico; (iii) Recíproco; (iv) Verificação do vazio. As operações aritméticas intervalares, implementadas na classe IntervalMath, foram as seguintes: (i) Igualdade (implementada na classe Interval); (ii) Adição; (iii) Subtração; (iv) Multiplicação; (v) Divisão. As operações lógicas intervalares, implementadas na classe IntervalSet, foram: (i) Pertence (implementada na classe Interval); (ii) Interseção, (iii) União, (iv) Absoluto, (v) Distância. A comparação dos resultados de Java-XSC com o intpakX, para as mesmas entradas, foi extremamente satisfatória, tendo a biblioteca Java-XSC apresentado um controle do erro bastante similar e se mostrando mais precisa em alguns casos, principalmente na operação de divisão. As operações de distância e absoluto não foram encontradas na biblioteca intpakX, impossibilitando a comparação. A calculadora intervalar para Java foi implementada como uma aplicação para a biblioteca e será disponibilizada na web, não apenas contendo as operações aritméticas, mas também lógicas. Aqui estão os resultados gerados pela biblioteca Java-XSC, comparados com o MapleInt: Java - XSC MapleInt Arredondamento para baixo: 2.0 com precisão de 10-9 1.999999999 1.999999999 Arredondamento para baixo: 3.88888888899 com precisão de 10-9 3.888888888 3.888888888 Arredondamento para baixo: -1.6564564876648 com precisão de 10-9 -1.656456488 -1.656456489 Arredondamento para cima: 2.0 com precisão de 10-9 2.000000001 2.000000001 Arredondamento para cima: 3.88888888899 com precisão de 10-9 3.888888890 3.888888890 Arredondamento para baixo: -1.6564564876648 com precisão de 10-9 -1.656456486 -1.656456487 Comprimento de [1.0, 2.5] 1.5 1.5 Comprimento de [0.0, 8.9] 8.9 8.9 Comprimento de [-5.6, 4.9] 10.5 10.5 Recíproco de [-3.0, 8.4] com precisão de 10-9 [-Infinity, Infinity] [-Infinity, Infinity] Recíproco de [-5.0, -2.0] com precisão de 10-9 [-0.500000001, -0.199999999] [-0.500000001, -0.199999999] Recíproco de [1.0, 4.0] com precisão de 10-9 [0.249999999, 1.000000001] [0.249999999, 1.000000001] 0.0 pertence a [-0.1, 0.1] true true 0.2 pertence a [-0.1, 0.1] false false 0.1 pertence a [-0.1, 0.1] true true Adição: [1.0, 2.5] + [0.0, 8.9] com precisão de 10-9 [0.999999999, 11.400000001] [0.999999999, 11.400000001] Adição: [-8.9, 0.0] + [0.0, 8.9] com precisão de 10-9 [-8.900000001, 8.900000001] [-8.900000001, 8.900000001] Subtração: [-8.9, 0.0] - [0.0, 8.9] com precisão de 10-9 [-0.000000001, 17.800000001] [0.000000000, 17.800000001] Subtração: [1.0, 3.0] - [4.0, 5.0] com precisão de 10-9 [-4.000000001, -0.999999999] [-4.000000001, -0.999999999] Multiplicação: [1.0, 3.0] * [4.0, 5.0] com precisão de 10-9 [3.999999999, 15.000000001] [3.999999999, 15.000000001] Multiplicação: [-8.9, 0.0] * [1.0, 2.5] com precisão de 10-9 [-22.250000001, 0.000000001] [-22.250000001, 0.000000000] Divisão: [-8.9, 0.0] / [1.0, 2.5] com precisão de 10-9 [-Infinity, Infinity] [-Infinity, Infinity] Divisão: [4.0, 5.0] / [1.0, 2.5] com precisão de 10-9 [1.599999999, 5.000000001] [1.599999999, 5.000000006] Tabela 1: Resultados da biblioteca Java-XSC confrontados aos do MapleInt Diagrama UML de relação entre as classes da biblioteca: Diagrama 1: Diagrama de classes atual da biblioteca Java-XSC 6. Discussões e Conclusões Sistemas computacionais atuais são incapazes de representar todos os números reais, pois estes são contínuos e densos e a representação digital é discreta. Java, sendo uma linguagem de programação, conseqüentemente, sofre esse problema, além disso, a implementação de ponto flutuante para Java desprezou fatores importantes como: (i) não suportar flags de exceções, definidos pelo padrão IEEE 754, (ii) não prover os arredondamentos direcionados também definidos pelo IEEE 754 e (iii) não prover intervalos como tipo primitivo. Portanto, Java apresenta vários erros em operações com ponto-flutuante. Como Java, atualmente, é linguagem mais popular do mundo, devido a um grande número de vantagens oferecidas, como: simplicidade, portabilidade e robustez, tende a ser mais utilizada em aplicações de computação científica. Visando suprir essa necessidade, foi implementada uma biblioteca chamada Java-XSC (eXtension for Scientific Computation) para controlar erros numéricos de ponto-flutuante utilizando métodos da matemática intervalar. Com relação à extensão intervalar para Maple, o intpakX, a biblioteca Java-XSC apresentou ótimos resultados após testes de validação, apresentando saídas semelhantes na grande maioria dos casos e também de melhor precisão em algumas operações. A aplicação implementada para uso da biblioteca foi uma calculadora intervalar para operações aritméticas e lógicas que será disponibilizada na internet através do site do projeto, http://www.cin.ufpe.br/~javaxsc/. A biblioteca Java-XSC será estendida para conter, além das operações aritméticas e lógicas, as operações logarítmicas, trigonométricas e estatísticas da matemática intervalar, além das relações de ordem. 7. Referências Bush, B.M., (1996), “The Perils of Floating Point”, http://www.lahey.com/float.htm, visitado em Novembro, 2005. Green, R., (2005), “Java Glossary: Floating Point”, http://mindprod.com/jgloss/floatingpoint.html, visitado em Novembro, 2005. Microsoft (2003), “Tutorial to Understand IEEE Floating-Point Errors”, http://support.microsoft.com/kb/q42980/, visitado em Novembro, 2005. IEEE Computer Society (1985), "IEEE Standard for Binary Floating-Point Arithmetic", IEEE Std 754-1985. Choudhari, P., (2001), “Java Advantages & Disadvantages”, http://arizonacommunity.com/articles/java_32001.shtml, visitado em Novembro, 2005. Sun Microsystems, “Java Language Overview”, Java 2 Platform, Standard Edition, White Papers, http://java.sun.com/docs/overviews/java/java-overview-1.html, visitado em Novembro, 2005. Sun Microsystems, “JavaTM 2 Platform, Standard Edition, v 1.4.2 API Specification”, http://java.sun.com/j2se/1.4.2/docs/api/, visitado em Novembro, 2005. Gosling, J., Joy, B., Steele G., (1996), “The Java Language Specification”, Addison Wesley. Ferreira, R. V., Fernandes, B. T., Campos, M. A., (2004), “Evaluating the floating-point in Java Virtual Machine”, Anais ENNEMAC 2004. The Macaulay Institute, (2004), “Floating Point Arithmetic and Java”, http://www.macaulay.ac.uk/fearlus/floating-point/javabugs.html, visitado em Novembro, 2005. Knuth, D., (1997) “The Art of Computer Programming”, Volume 2. Moore, R. E. (1996), “Interval Analysis” Englewood Cliffs: Prentice-Hall Campos, M. A., (1997), “Uma Extensão Intervalar para a Probabilidade Real” Departamento de Informática – UFPE IBM Rational Software, (2002) – Rational Rose Enterprise Edition – http://www306.ibm.com/software/rational/offerings/reqanalysis.html, visitado em Novembro, 2005 Eclipse.org, (2005) – http://www.eclipse.org/, visitado em Novembro, 2005. MapleSoft.com, http://www.maplesoft.com/, visitado em Novembro, 2005.