Aritmética de Alta Precisão

Documentos relacionados
Aritmética e Álgebra COM10615-Tópicos Especiais em Programação I edmar.kampke@ufes.br

PARTE I I: ARITMÉTICA COMPUTACIONAL ARQUITETURA DE COMPUTADORES ANTONIO RAMOS DE CARVALHO JÚNIOR

3. Linguagem de Programação C

MC102 Algoritmos e Programação de Computadores

3. Linguagem de Programação C

Sistemas de Numeração

Arquitetura de Computadores I

Programação Orientada a Objetos

Cursos: Análise, Ciência da Computação e Sistemas de Informação Laboratório I - Prof. Aníbal Notas de aula 2 SISTEMAS NUMÉRICOS

Programação Estruturada

LINGUAGEM C: VARIÁVEIS E EXPRESSÕES

Nós estamos acostumados com operações aritméticas: soma, subtração, multiplicação e divisão.

Métodos Computacionais. Operadores, Expressões Aritméticas e Entrada/Saída de Dados

Introdução a Programação de Jogos

Introdução à Programação. Operadores, Expressões Aritméticas e Entrada/Saída de Dados

Exemplos: -5+7=2; 12-5=7; -4-3=-7; -9+5=-4; -8+9=1; -4-2=-6; -6+10=4

Introdução à Computação

Introdução à Programação

Introdução à Computação MAC0110

Estruturas da linguagem C. 1. Identificadores, tipos primitivos, variáveis e constantes, operadores e expressões.

Utilização da Linguagem C

Princípios de Desenvolvimento de Algoritmos MAC122

Arquitetura e Organização de Computadores. Sistemas Numéricos

Introdução à Computação

Alex Maycon da Silva

Capítulo 04 : Sistemas Numéricos

Aula 03: Introdução a C

Computação Eletrônica. Tipos de dados, constantes, variáveis, operadores e expressões. Prof: Luciano Barbosa

Algoritmos e Estruturas de Dados I (DCC/003) Estruturas Básicas. Aula Tópico 2

Universidade Federal de Uberlândia Faculdade de Computação. Representação e aritmética binária

MANUTENÇÃO DE COMPUTADORES SISTEMAS NUMÉRICOS

Computação I (MAB120) DCC/UFRJ

Aritmética Binária e Complemento a Base. Introdução ao Computador 2010/1 Renan Manola

Sistemas de Numeração. Tiago Alves de Oliveira

Aula 08: Repetição (Parte 3)

Algoritmos e Estruturas de Dados I (DCC/003) 2013/1. Estruturas Básicas. Aula Tópico 4

Sistemas de Computação

Algoritmos e Estruturas de dados

SCC-210. Algoritmos Avançados Capítulo 5

Programação Imperativa. Lição n.º 3 Operações aritméticas

Complexidade de Algoritmos

Introdução a Linguagem C (Parte I) UFPA Sistemas de Informação. Roberto Araujo 2013

Introdução à Linguagem C Variáveis e Expressões

Computação eletrônica: Operadores, expressões e funções

Introdução a Computação

Bits e operações. Sistemas de Computação

Estrutura de Dados Conceitos Iniciais

Programação 1. Atribuição, operadores aritméticos, entrada de dados. Técnico em Eletrônica Semestre 5 02

Circuitos Lógicos. Capítulo 9 Aritmérica Digital: Operações e Circuitos

Multiplicação Divisão

1 bases numéricas. capítulo

Capítulo V Sistemas Numéricos

Sistemas de Numeração

Linguagem C. IF61A/IF71A - Computação 1 Prof. Leonelo Almeida. Universidade Tecnológica Federal do Paraná

Aula de hoje. Códigos numéricos. Códigos binários. Armazenamento de dados. Armazenamento de dados. Armazenamento de dados

Declarações. C diferencia letras maiúsculas de minúsculas! int n, N; n é diferente de N!

Aula 11: Laços e exercícios

PROGRAMAÇÃO I E N T R A DA E S A Í DA D E DA D O S

Aula 16: Laços aninhados e desvios

Arquitetura de Computadores

PCS 3115 (PCS2215) Sistemas Digitais I. Módulo 03a Aritmética Binária. Prof. Dr. Marcos A. Simplicio Jr. versão: 3.0 (Jan/2016) Conteúdo

PROGRAMAÇÃO ESTRUTURADA E ORIENTADA A OBJETOS

Programação I Aula 19 Aritmética com racionais Pedro Vasconcelos DCC/FCUP

Material Didático Unificado.

Introdução à Computação - Linguagem C: aulas 03, 04 e 05. Mauro Cesar Bernardes 03/Junho/2014

Números são números, letras são números e sinais de pontuação, símbolos e até mesmo as instruções do próprio computador são números.

Expressões lógicas, expressões condicionais, prioridades e operadores, base binária, operadores de bits

4 Variáveis. Unesp Campus de Guaratinguetá

Introdução à Programação Estruturada Parte 3. Material da Prof. Ana Eliza

Estruturas de Repetição

LÓGICA DE PROGRAMAÇÃO. PROFª. M.Sc. JULIANA H Q BENACCHIO

DECIMAIS. Definições e operações

Cálculo Numérico Conceitos Básicos

Disciplina: Introdução à Engenharia da Computação

Conceitos Básicos Linguagem C

Disciplina de Algoritmos e Programação

Sistemas Digitais Representação Digital de Informação

Organização de Computadores I

ARQUITETURA DE COMPUTADORES

Representação de Dados

X. B Y Base do sistema de numeração Dígito do número em questão

Computação para Informática - Prof. Adriano Joaquim de Oliveira Cruz Segunda Aula Prática - 29 de agosto de 2008

Cálculo Numérico IPRJ/UERJ. Sílvia Mara da Costa Campos Victer ÍNDICE. Aula 1- Introdução. Representação de números. Conversão de números

Prof. Leonardo Augusto Casillo

Aula 2 - Sistemas de Numeração

Aritmética em Bases Não Decimais

Sistemas de Numeração.

3/14/2012. Programação de Computadores O Computador. Memória Principal representação binária

Programação Estruturada

Material Didático Unificado.

Representação de Dados (inteiros não negativos)

Definição: Uma função de uma variável x é uma função polinomial complexa se pudermos escrevê-la na forma n

Revisão C++ - Parte 1

Introdução à Ciência da Computação. Aula 04 Carlos André Guerra Fonseca

Paradigmas de Linguagens

Tipos Abstratos de Dados - Exercício

Criando seu próprio tipo de dado. Prof. Fabrício Olivetti de França

Arquitetura e Organização de Computadores

Universidade Federal de Ouro Preto - UFOP Departamento de Computação - DECOM Programação de Computadores I - BCC701

Transcrição:

Aritmética de Alta Precisão Túlio Toffolo www.toffolo.com.br Marco Antônio Carvalho marco.opt@gmail.com BCC402 Aula 06 Algoritmos e Programação Avançada

Na aula de hoje Inteiros de Alta Precisão. Arrendondamento x Truncamento. Aritmética de Alta Precisão. 2

Prova: Próxima Terça, dia 19

Inteiros de Alta Precisão Para representarmos números enormes, torna-se necessário transformar os dígitos em caracteres; Existem duas maneiras básicas: Vetores de dígitos: A representação mais fácil, em que o elemento inicial do vetor representa o bit menos significativo (o número é armazenado invertido) Pode-se usar um contador com o número de dígitos Listas encadeadas de dígitos: Quando não há informação sobre o limite para a quantidade de dígitos Se houver informação, poderíamos ainda utilizar alocação dinâmica para criarmos vetores adequados. 4

Inteiros de Alta Precisão Nos problemas que veremos, existe informação sobre o comprimento dos números, em bits Desta forma, poderemos nos basear em estruturas de vetores. A seguir, definiremos uma estrutura para inteiros de alta precisão em C, cujo código está disponível no Moodle bignum.c; Por sinal, bignum é um termo utilizado para inteiros de alta precisão. 5

Comentário Sobre Listas e Vetores Quem oferece melhor aproveitamento de memória, listas encadeadas ou vetores? Listas? Podem desperdiçar memória! Útil quando desconhecemos o tamanho final Vetores? Economizam memória quando o comprimento é conhecido a priori 6

Estrutura #define MAXDIGITS 100 /* comprimento máximo do bignum */ #define PLUS 1 /* bit de sinal positivo */ #define MINUS -1 /* bit de sinal negativo */ typedef struct { char digits[maxdigits]; /* representa o número */ int signbit; /* 1 se positivo, -1 se negativo */ int lastdigit; /* índice do dígito mais significativo */ bignum; 7

Estrutura Note que cada dígito (0-9) é representado utilizando um caractere de 1 byte; Embora exija um pouco mais de cuidado para manipular tais números, a economia de espaço e tempo compensa; Armazenar os sinais separadamente é conveniente, ao invés de armazená-los junto com o valor dos números, simplesmente realizamos suas operações separadamente. 8

Impressão Como os bignums são armazenados invertidos, imprimimos do final para o início; Utilizamos aritmética da tabela ASCII para evitar caracteres não imprimíveis devido ao resultado de operações. print_bignum(bignum *n) { int i; if (n->signbit == MINUS) printf("- "); for (i=n->lastdigit; i>=0; i--) printf("%c",'0'+ n->digits[i]); printf("\n"); 9

Operações Adição Realizada da direita para a esquerda, com qualquer sobra sendo enviada para a próxima posição, como dígito de transporte; Caso um dos bignums seja negativo, temporariamente seu sinal é feito positivo e é feita a subtração do outro bignum; Cuida-se para armazenar somente um dígito do resultado da operação por posição. 10

add_bignum(bignum *a, bignum *b, bignum *c) { int carry, i; /* dígito de transporte e contador */ initialize_bignum(c); if (a->signbit == b->signbit) c->signbit = a->signbit; else if (a->signbit == MINUS) { a->signbit = PLUS; subtract_bignum(b,a,c); a->signbit = MINUS; return; else { b->signbit = PLUS; subtract_bignum(a,b,c); b->signbit = MINUS; return; c->lastdigit = max(a->lastdigit,b->lastdigit)+1; carry = 0; for (i=0; i<=(c->lastdigit); i++) { c->digits[i] = (char) (carry + a->digits[i] + b->digits[i]) % 10; carry = (carry + a->digits[i] + b->digits[i]) / 10; zero_justify(c);

Operações O procedimento zero_justify é utilizado para ajustar o membro lastdigit, para evitar indicações para sequências iniciais de zeros e também para evitar casos de zero negativo. zero_justify(bignum *n) { while ((n->lastdigit > 0) && (n->digits[ n->lastdigit ] == 0)) n->lastdigit --; if ((n->lastdigit == 0) && (n->digits[0] == 0)) n->signbit = PLUS; /* hack para evitar -0 */ 12

Operações Subtração O cuidado a ser tomando na subtração é o empréstimo (quando tentamos subtrair um número maior de um menor) Para isso, faz-se com que o maior número seja o primeiro, e depois ajusta-se o sinal. 13

subtract_bignum(bignum *a, bignum *b, bignum *c) { int borrow; /* algo a ser emprestado? */ int v, i; /* dígito placeholder e contador */ initialize_bignum(c); if ((a->signbit == MINUS) (b->signbit == MINUS)) { b->signbit = -1 * b->signbit; add_bignum(a,b,c); b->signbit = -1 * b->signbit; return; if (compare_bignum(a,b) == PLUS) { subtract_bignum(b,a,c); c->signbit = MINUS; return; c->lastdigit = max(a->lastdigit,b->lastdigit); borrow = 0; for (i=0; i<=(c->lastdigit); i++) { v = (a->digits[i] - borrow - b->digits[i]); if (a->digits[i] > 0) borrow = 0; if (v < 0) { v = v + 10; borrow = 1; c->digits[i] = (char) v % 10; zero_justify(c);

Operações Comparação Para determinar qual o maior entre dois números, é importante que a comparação seja realizada a partir do dígito mais significativo; Antes, tenta-se cortar caminho: Comparando os sinais. Comparando o número de dígitos. 15

Operações compare_bignum(bignum *a, bignum *b) { int i; /* contador */ if ((a->signbit == MINUS) && (b->signbit == PLUS)) return(plus); if ((a->signbit == PLUS) && (b->signbit == MINUS)) return(minus); if (b->lastdigit > a->lastdigit) return (PLUS * a->signbit); if (a->lastdigit > b->lastdigit) return (MINUS * a->signbit); for (i = a->lastdigit; i>=0; i--) { if (a->digits[i] > b->digits[i]) return(minus * a->signbit); if (b->digits[i] > a->digits[i]) return(plus * a->signbit); return 0; 16

Operações Multiplicação A multiplicação poderia se feita através de adições sucessivas Qual seria o problema desta abordagem? Levaria muito tempo para números muito grandes! Utiliza-se então o método linha-por-linha Em cada operação, faz-se o shift dos números uma posição para a direita; Então soma-se o número shiftado d vezes ao total, em que d é o dígito apropriado para o segundo número. 17

Operações multiply_bignum(bignum *a, bignum *b, bignum *c) { bignum row; /* representa a linha "shiftada" */ bignum tmp; /* bignum placeholder (para preencher o comprimento) */ int i,j; /* contadores */ initialize_bignum(c); row = *a; for (i=0; i<=b->lastdigit; i++) { for (j=1; j<=b->digits[i]; j++) { add_bignum(c,&row,&tmp); *c = tmp; digit_shift(&row,1); c->signbit = a->signbit * b->signbit; zero_justify(c); 18

Operações /* multiplica n por 10^d */ digit_shift(bignum *n, int d) { int i; /* contador */ if ((n->lastdigit == 0) && (n->digits[0] == 0)) return; for (i=n->lastdigit; i>=0; i--) n->digits[i+d] = n->digits[i]; for (i=0; i<d; i++) n->digits[i] = 0; n->lastdigit = n->lastdigit + d; 19

Operações Divisão A divisão é feita de um modo semelhante a subtrações sucessivas; Em um loop: O resto é shiftado para a esquerda; O próximo dígito é incluído; O divisor é subtraído. 20

divide_bignum(bignum *a, bignum *b, bignum *c) { bignum row; /* representa a linha "shiftada" */ bignum tmp; /* bignum placeholder (para preencher o comprimento) */ int asign, bsign, i, j; /* sinais temporários e contadores */ initialize_bignum(c); c->signbit = a->signbit * b->signbit; asign = a->signbit; bsign = b->signbit; a->signbit = PLUS; b->signbit = PLUS; initialize_bignum(&row); initialize_bignum(&tmp); c->lastdigit = a->lastdigit; for (i=a->lastdigit; i>=0; i--) { digit_shift(&row,1); row.digits[0] = a->digits[i]; c->digits[i] = 0; while (compare_bignum(&row,b)!= PLUS) { c->digits[i] ++; subtract_bignum(&row,b,&tmp); row = tmp; zero_justify(c); a->signbit = asign; b->signbit = bsign;

Operações A divisão é inteira, e o resto da divisão é ignorado; Uma forma de computador o resto da divisão de a por b seria calcular: Ø a -b (a/b) 22

Operações Exponenciação A exponenciação é feita por multiplicações sucessivas, mas com um truque: Quantas multiplicações? Ø O(log n) a n = a n/2 a n/2 a n%2 Logo, requer apenas um número logarítmico de multiplicações. 23

Diferentes bases e conversões Utilizamos diferentes bases para diferentes situações: Binário, octal, decimal, hexadecimal e alfanumérico (base 36); Porquê os programadores americanos pensam que o natal é igual ao halloween? Porque 25 dec == 31 oct. 24

Diferentes bases e conversões Uma das formas de converter um número x de base a em um número y na base b é descartar o dígito menos significativo de y Ou seja, o resto da divisão de x por b; Utiliza-se aritmética modular. A seguir, um código que transforma um int em um bignum Para converter outras bases, basta mudar o 10 pela base desejada. 25

Diferentes bases e conversões int_to_bignum(int s, bignum *n) { int i, t; /* contador e o int a ser trabalhado */ if (s >= 0) n->signbit = PLUS; else n->signbit = MINUS; for (i=0; i<maxdigits; i++) n->digits[i] = (char) 0; n->lastdigit = -1; t = abs(s); while (t > 0) { n->lastdigit ++; n->digits[ n->lastdigit ] = (t % 10); t = t / 10; if (s == 0) n->lastdigit = 0; 26

Números reais Trabalhar com números reais em computação é desafiador, por causa da precisão limitada; A propriedade da continuidade dos números reais não é válida: Sempre há um número c entre a e b caso a < b A associativadade da adição nem sempre será válida, devido a erros de arrendamento por diferentes operações. 27

Arrendondar ou Truncar? Vários problemas pedirão que um número real seja apresentado com determinada quantidade de casas decimais; Truncar consiste em cortar casas decimais; Arredondar consiste em cortar casas decimais e ajustar o restante do número. O formatador de casas decimais do printf arredonda o número; Outra forma de arredondar um número x em k casas decimais é: piso(10 k x + (1/2))/10 k 28

Exemplo: double e int

Frações Números racionais exatos x/y são melhor representados por pares de inteiros x, y, em que x é o numerador e y é o denominador; As operações aritméticas básicas em números racionais c=x 1 /y 1 e d=x 2 /y 2 são fáceis de serem programadas. 30

Operações em Frações c+d = (x 1 y 2 + x 2 y 1 ) / y 1 y 2 ; c-d = (x 1 y 2 - x 2 y 1 ) / y 1 y 2 ; c d = x 1 x 2 / y 1 y 2 ; c/d = x 1 /y 1 y 2 /x 2 = x 1 y 2 / x 2 y 1. 31

Operações em Frações Implementar estas operações cegamente nos expõe ao risco de overflow Para evitá-lo, reduzimos as frações à sua menor representação; Ex.: 16/64 = 1/4; O segredo é cancelar o MDC entre o numerador e o denominador O algoritmo de Euclides, além de elegante, é eficiente na maioria dos casos. 32

Algoritmo de Euclides Cálculo do MDC (Máximo Divisor Comum) void f(int a, int b) { // considere a > b if (b == 0) return a; else return f(b, a % b); 33

Manipulando Polinômios A representação intuitiva de um polinômio univariado (uma única variável) de grau n é um vetor de n+1 coeficientes, de c 0 a c n ; Para algumas operacões em polinômios univariados, existem alguns atalhos. 34

Manipulando Polinômios Avaliação Computar P(x) para um dado x pode exigir O(n 2 ), quando basta O(n) Um segredo é começar pelos termos de menor grau e reaproveitá-los, ja que x n = x n-1 x; Eis o princípio da programação dinâmica! 35

Manipulando Polinômios Adição/Subtração Mais fácil do que com bignums, uma vez que não existe empréstimo nem transporte. 36

Manipulando Polinômios Multiplicação O produto de dois polinômios P(x) e Q(x) é a soma do produto de todos os pares de termos Este tipo de todos contra todos é chamado de convolução; Força bruta exige tempo O(n 2 ) A transformada rápida de Fourier computa convoluções em O(n log n) Mas está além do escopo da disciplina. 37

Manipulando Polinômios Divisão A divisão de polinômios é capciosa, pois os polinômios não são fechados sob divisão; Por exemplo, 1/x pode ou não ser visto como um polinômio, pois é igual a x -1 Por outro lado, 2x/(x 2 +1) não é um polinômio 2x/(x 2 +1) é uma função racional. 38

Determinação de Raízes Dado um polinômio P(x) e um número t, determinar as raízes de P(x) é identificar algum ou todos os x tal que P(x) = t; Se o polinômio possuir grau dois, aplica-se a regra de Bhaskara; Para graus maiores, existem diferentes métodos numéricos (método de Newton, Newton-Rhapson, etc.); A idéia básica de busca binária pode ser utilizada. 39

Determinação de Raízes Suponha uma função f(x) monotonicamente crescente entre l e u Ou seja, f(i) <= f(j) para todos l<=i<=j<=u. Suponha agora que queremos encontrar x tal que f(x)=t Podemos comparar f((l+u)/2) com t Se t < f((l+u)/2), então a raiz está entre l e (l+u)/2; Caso contrário, está entre (l+u)/2 e u; Seguimos estreitando a busca até encontrar a raiz. 40

Bibliotecas de Números Reais Em Java, temos java.lang.math; Em C/C++ temos math.h e cmath: double floor(double x): função piso; double ceil (double x): função teto; double fabs(double x): valor absoluto; double sqrt(double x): radiciação; double exp(double x): potenciação de e; double log(double x): logaritmo na base e; double log10(double x): logaritmo na base 10; double pow(double x, double y): potenciação; 41

Perguntas?