sexta-feira, 31 de julho de 2009

ERROS COMUNS: Exponenciação - número negativo elevado a um potência real

Sabe-se que a expressão:
y = x ** n,
onde y e x são reais, e n é um inteiro, equivale a simplesmente multiplicar x por ele mesmo n vezes, e é isso exatamente que o computador faz ao encontrar uma expressão desse tipo.

No entanto, quando se tem algo do tipo:
y = x ** z,
onde y, x e z são reais, z pode ser igual a, por exemplo, 2.5. É fisicamente impossível multiplicar um número por ele mesmo 2.5 vezes, e portanto é necessário recorrer a métodos indiretos para calcular x ** z. O método mais comum para este caso é o seguinte:
x ** z = exp(z*ln(x)).
Portanto, como não existe logaritmo natural de número negativo, o seguinte "ERRO" pode ocorrer:
(-2.0)**(2.5) = NaN (Not a number)
( 2.0)**(2.5) = 5.656854.

ERROS COMUNS: Divisão em "modo misto"

Um erro bastante comum, do qual eu e alguns colegas já fomos vítimas, é a divisão envolvendo reais e inteiros. De uma forma geral, as expressões em "modo misto" devem ser evitadas, visto que são relativamente difíceis de entender e podem levar a resultados diferentes dos desejados.

Em operações aritméticas entre um número real e um inteiro, o inteiro é convertido para real pelo computador e o resultado é do tipo real.
Isso explica porque:
(1 + 1/2) = 1
(1. + 1/2) = 1.0
(1 + 1./2) = 1.5
(1 + 1/2.) = 1.5
No primeiro e no segundo caso ocorre que 1/2 = 0, por se tratar de uma divisão entre dois números inteiros. No terceiro e no quarto casos, 1./2 = 1/2. = 0.5, o inteiro é convertido para real e a aritmética para números reais é utilizada.

Portanto, é necessário ter atenção redobrada para casos tais como:
(2**(1/2)), que resulta igual 1, diferentemente de
(2**(1./2)) ou (2**(1/2.)), cujo resultado é 1.414214...

segunda-feira, 6 de julho de 2009

Comparação - ACESSANDO MATRIZES... (RANK 2 ARRAYS)

Acho que muitos já devem saber (ou ter ouvido falar) que o fortran guarda matrizes na memória "coluna por coluna". Dessa forma, o acesso às mesmas deve ser feito também coluna por coluna, para maior velocidade de execução. O conceito de coluna varia um pouco quando se trabalha com matrizes de mais de duas dimensões, mas de uma forma geral os loops mais internos devem se referir à dimensão mais à esquerda da matriz.
Por exemplo, com uma matriz de três dimensões se teria:

!LOOP mais rápido
DO K=1,N3
DO J=1,N2
DO I=1,N1
A(I,J,K) = ...
END DO
END DO
END DO

Qualquer alteração da ordem desses LOOPs resulta num código mais lento.

Tomando-se o caso de duas dimensões apenas, qual seria a diferença de velocidade do loop mais rápido para o mais lento?

Na busca por respostas para essa pergunta, foi desenvolvido um pequeno projeto no Compaq Visual Fortran 6.6 ( 288Kb). Para uma matriz A de dimensão NxN, onde N=5000, foram rodados 20 vezes os códigos que permitiram colocar o valor de cada elemento da matriz como sendo
A(I,J) = A(I,J) + I**2 + J**2,
acessando linha por linha (mais lento) e coluna por coluna (mais rápido).
O tempo médio e o tempo médio relativo foram obtidos para comparação.
Os resultados foram os seguintes:

Ou seja, para este caso específico a diferença já mostrou ser muito grande. O acesso coluna por coluna chega a ser quase 5 vezes mais rápido que o acesso linha por linha. Um bom motivo para varrer seus códigos-fonte e excluir os loops coluna por coluna deles sempre que possível.