Desenvolva um código em Fortran que seja capaz de realizar
a soma do número 0,00001 por 100.000 vezes, empregando precisão simples e também precisão dupla.
a soma do número 0,00001 por 100.000 vezes, empregando precisão simples e também precisão dupla.
Passo 1
O que o enunciado pede e quais dados temos:
- Pede para desenvolver um programa em Fortran que realize a soma de
por vezes. - Deve-se fazer isso usando precisão simples e também precisão dupla.
- Resultado matemático exato esperado: somar
vezes deveria resultar em . - Precisamos apresentar um código que efetue as duas somas (single e double), imprima os resultados e permita comparar o erro em cada caso.
Passo 2
Decisão do método de implementação (por que usar esse método):
- Método escolhido: um loop simples que acumula um valor de incremento em um acumulador. Esse é o método direto que se pedirá na prova: repetir
vezes a operação de soma. - Por que não outro método mais complexo? O enunciado pede explicitamente a operação repetida; além disso, isso evidencia os efeitos de precisão (erro de arredondamento) ao longo de muitas somas, que é o objetivo didático.
- Observação: existe um método para reduzir erro de soma chamado soma de Kahan (ou algoritmo de compensação). Vou mencionar e explicar, mas o código principal será o loop direto (e ao final mostro como incluir Kahan opcionalmente).
Passo 3
Conceitos numéricos importantes (explicação didática, sem pular etapas)
- Representação em ponto flutuante: computadores representam números reais com um número finito de bits para mantissa e expoente. Isso gera arredondamento.
- Precisão simples e dupla:
- Precisão simples guarda aproximadamente 6–9 dígitos decimais significativos (mantissa ~24 bits).
- Precisão dupla guarda aproximadamente 15–17 dígitos decimais significativos (mantissa ~53 bits).
- Efeito prático: quando se soma repetidamente pequenos incrementos, os arredondamentos em cada operação podem acumular um erro.
- Machine epsilon: para um tipo real
em Fortran, a função intrínseca retorna a menor diferença relativa entre e o próximo número representável. Isso dá uma ideia da resolução do formato. - Objetivo prático: implementar o loop em dois tipos (single e double), imprimir soma, diferença em relação a
e os valores de para comparar.
Passo 4
Como representar precisão simples e dupla em Fortran (detalhes e recomendações)
- Usar a função intrínseca
selected_real_kindpara escolher kinds portáveis:selected_real_kind(p=6)normalmente dá um real de precisão simples suficiente.selected_real_kind(p=15)normalmente dá um real de precisão dupla.
- Definir parâmetros:
integer, parameter :: sp = selected_real_kind(p=6)integer, parameter :: dp = selected_real_kind(p=15)
- Declarar variáveis com
real(kind=sp)ereal(kind=dp). - Literais com sufixo de kind: ex.:
1.0e-5_spou1.0e-5_dp(após declararspedp).
Passo 5
Estrutura do algoritmo e cuidados de implementação (passo a passo do que o código fará)
- Declarar constantes:
, sp,dp. - Declarar acumuladores:
s_spinicializado comna precisão simples. s_dpinicializado comna precisão dupla.
- Declarar incrementos:
inc_sp = 1.0e-5_spinc_dp = 1.0e-5_dp
- Loop
do i=1,N:s_sp = s_sp + inc_sps_dp = s_dp + inc_dp
- Após o loop, imprimir:
- Valor de
s_spes_dp. - Diferença
s_sp - 1.0_spes_dp - 1.0_dp. epsilon(1.0_sp)eepsilon(1.0_dp)para ver a precisão de cada tipo.
- Valor de
- Observação sobre portabilidade:
selected_real_kindtorna o código menos dependente dereal*4oureal*8.
Passo 6
Explicação linha a linha (ensinando a ler e entender o código sem pular passos)
program soma_preciso: inicia o programa.implicit none: força declaração explícita de variáveis (boa prática).- Declarações dos
parameter:spedpobtêm os kinds adequados.Né o número de repetições:.
- Variáveis:
icontador inteiro.inc_sp,s_spsãoreal(kind=sp).inc_dp,s_dpsãoreal(kind=dp).
- Inicializações:
inc_sp = 1.0e-5_spsignifica o literalna precisão simples. s_sp = 0.0_spinicializa o acumulador com zero no mesmo kind.
- Loop
doefetua exatamenteadições. Cada adição é uma operação em ponto flutuante com arredondamento. - Impressões: usamos
print *para saída legível; mostramos também o erro relativo/absoluto subtraindo. - Finaliza com
end program.
Passo 7
Comentário sobre melhoria opcional (soma de Kahan)
- Se for necessário reduzir o erro de acumulação sem usar dupla precisão, a soma de Kahan é uma técnica simples:
- Mantém um valor de compensação
c. - A cada soma, corrige
y = inc - c,t = s + y,c = (t - s) - y,s = t.
- Mantém um valor de compensação
- Essa técnica reduz sensivelmente erro de cancelamento em somas de muitos termos.
- Vou deixar um comentário no código indicando como ativar Kahan, caso queira comparar resultados.
Passo 8
Como compilar e executar (passo a passo)
- Salve o código em arquivo, por exemplo
soma_precisao.f90. - Compile com um compilador Fortran moderno, por exemplo
gfortran:gfortran -O2 -o soma_precisao soma_precisao.f90
- Execute:
./soma_precisao
- A saída mostrará os dois somatórios, os erros e as epsilons. Compare
s_spes_dpcom1.0.
Resposta
program soma_precisao
implicit none
integer, parameter :: sp = selected_real_kind(p=6)
integer, parameter :: dp = selected_real_kind(p=15)
integer, parameter :: N = 100000
integer :: i
implicit none
integer, parameter :: sp = selected_real_kind(p=6)
integer, parameter :: dp = selected_real_kind(p=15)
integer, parameter :: N = 100000
integer :: i
real(kind=sp) :: inc_sp, s_sp
real(kind=dp) :: inc_dp, s_dp
real(kind=dp) :: inc_dp, s_dp
! Inicializações
inc_sp = 1.0e-5_sp
inc_dp = 1.0e-5_dp
s_sp = 0.0_sp
s_dp = 0.0_dp
inc_sp = 1.0e-5_sp
inc_dp = 1.0e-5_dp
s_sp = 0.0_sp
s_dp = 0.0_dp
! Loop de acumulação simples
do i = 1, N
s_sp = s_sp + inc_sp
s_dp = s_dp + inc_dp
end do
do i = 1, N
s_sp = s_sp + inc_sp
s_dp = s_dp + inc_dp
end do
! Impressão dos resultados e erros
print *, 'Resultados da soma de 0.00001 repetida ‘, N, ’ vezes:’
print *, ’ Single precision (real(kind=sp)) : ', s_sp
print *, ’ Double precision (real(kind=dp)) : ', s_dp
print *, ‘Erros absolutos (valor - 1.0):’
print *, ’ Erro single : ', s_sp - 1.0_sp
print *, ’ Erro double : ', s_dp - 1.0_dp
print *, ‘Machine epsilon:’
print *, ’ epsilon(1.0_sp) = ', epsilon(1.0_sp)
print *, ’ epsilon(1.0_dp) = ', epsilon(1.0_dp)
print *, 'Resultados da soma de 0.00001 repetida ‘, N, ’ vezes:’
print *, ’ Single precision (real(kind=sp)) : ', s_sp
print *, ’ Double precision (real(kind=dp)) : ', s_dp
print *, ‘Erros absolutos (valor - 1.0):’
print *, ’ Erro single : ', s_sp - 1.0_sp
print *, ’ Erro double : ', s_dp - 1.0_dp
print *, ‘Machine epsilon:’
print *, ’ epsilon(1.0_sp) = ', epsilon(1.0_sp)
print *, ’ epsilon(1.0_dp) = ', epsilon(1.0_dp)
! Observação: para reduzir erro em single sem usar double, pode-se usar
! a soma de Kahan (não implementada por padrão). Exemplo de Kahan:
! real(kind=sp) :: c, y, t
! c = 0.0_sp; s_sp = 0.0_sp
! do i=1,N
! y = inc_sp - c
! t = s_sp + y
! c = (t - s_sp) - y
! s_sp = t
! end do
! a soma de Kahan (não implementada por padrão). Exemplo de Kahan:
! real(kind=sp) :: c, y, t
! c = 0.0_sp; s_sp = 0.0_sp
! do i=1,N
! y = inc_sp - c
! t = s_sp + y
! c = (t - s_sp) - y
! s_sp = t
! end do
end program soma_precisao