A precisão da máquina é definida como sendo o menor número positivo em aritmética de ponto flutuante \epsilon, tal que (1+\epsilon)>1[?, ?, ?]. Este número, o menor número representável, depende totalmente do sistema de representação da máquina: base numérica, total de dígitos na mantissa, da forma como são realizadas as operações e do compilador utilizado. É importante conhecermos a precisão da máquina porque em vários algoritmos precisamos fornecer como dado de entrada um valor positivo, próximo de zero, para ser usado em testes de comparação com zero. Escreva um código em Fotran e em Python que tenha por objetivo estimar a precisão do computador e compilador que você está usando.

Passo 1

O que o enunciado pede e quais informações temos:
  • Pede: escrever um código em Fortran e um em Python para estimar a precisão da máquina/compilador que está sendo usado.
  • Informação-chave dada: a precisão da máquina é o menor número positivo \epsilon tal que 1+\epsilon>1 em aritmética de ponto flutuante.
  • Resultado esperado: dois programas (Fortran e Python) que calculam esse \epsilon para as diferentes precisões (pelo menos “float” de máquina e, se possível, precisão simples e dupla).

Passo 2

O que é exatamente essa precisão e por que o algoritmo funciona (aula curta e prática).
  • Definição usada aqui: \epsilon_{\text{mach}}=\min\{\,\delta>0:\;1+\delta>1\,\}. Em palavras: o menor incremento positivo que, somado a 1, produz um valor maior que 1 na aritmética da máquina.
  • Por que existe um tal \epsilon: números em ponto flutuante têm mantissa com número finito de dígitos; após uma certa ordem de magnitude, incrementos muito pequenos são “arredondados” de volta para 1.
  • Relação com “unit roundoff”: muitas vezes se chama de unit roundoff u o valor tal que u=\epsilon_{\text{mach}}/2 dependendo da definição adotada; aqui vamos estimar a \epsilon_{\text{mach}} definida acima (diferença entre 1 e o próximo número representável maior que 1).
  • Estratégia prática fácil de implementar: começar com \epsilon=1 e ir dividindo por 2 enquanto 1+\epsilon/2>1. Quando o teste falhar, o último \epsilon válido é a estimativa de \epsilon_{\text{mach}}. Motivo: se 1+\epsilon/2>1 então \epsilon/2 ainda é um incremento detectável; quando diminuir mais e a soma for igual a 1 por arredondamento, então ultrapassou a resolução.

Passo 3

Pseudocódigo e advertências (para usar na prova sem biblioteca):
  • Pseudocódigo simples:
    • \epsilon \leftarrow 1
    • Enquanto 1 + \epsilon/2 > 1 faça \epsilon \leftarrow \epsilon/2
    • Ao sair do laço, \epsilon é a estimativa de \epsilon_{\text{mach}}.
  • Observações práticas:
    • Use aritmética no tipo que quer testar (ex.: precisão simples vs dupla).
    • Em linguagens com tipos explícitos (Fortran), declare a variável com o kind correto.
    • Em Python, o tipo float é normalmente double (64 bits); para testar single (32 bits) use numpy.float32.
    • Não confunda o critério: testar 1+\epsilon>1 diretamente pode falhar se começar com \epsilon pequeno demais; por isso testamos com \epsilon/2 e dividimos sucessivamente.

Passo 4

Código Fortran (explicação linha-a-linha antes de mostrar o código):
  • Ideia: usar duas variantes: precisão simples (single) e precisão dupla (double) com selected_real_kind para portabilidade.
  • Estrutura:
    • declarar kinds: sp e dp com precisão típica (p~6 para single, p~15 para double);
    • inicializar eps = 1.0 do respectivo kind;
    • laço do…end do que divide por 2 até o teste falhar;
    • imprimir o resultado e comparar com a função intrínseca epsilon(x) (se disponível) para checagem.
  • Como compilar e executar (exemplo): use gfortran
    • Compilar: gfortran -o eps_machine eps_machine.f90
    • Executar: ./eps_machine
Código Fortran:
program machine_epsilon
  implicit none
  integer, parameter :: sp = selected_real_kind(p=6)    ! precisão simples típica
  integer, parameter :: dp = selected_real_kind(p=15)   ! precisão dupla típica

  real(kind=sp) :: one_sp, eps_sp, half_sp
  real(kind=dp) :: one_dp, eps_dp, half_dp

  one_sp = 1.0_sp
  one_dp = 1.0_dp

  ! estimativa para precisão simples
  eps_sp = 1.0_sp
  do
    half_sp = eps_sp / 2.0_sp
    if (one_sp + half_sp <= one_sp) exit
    eps_sp = half_sp
  end do

  ! estimativa para precisão dupla
  eps_dp = 1.0_dp
  do
    half_dp = eps_dp / 2.0_dp
    if (one_dp + half_dp <= one_dp) exit
    eps_dp = half_dp
  end do

  print *, 'Estimated machine epsilon (single)  =', eps_sp
  print *, 'Fortran intrinsic epsilon(one_sp) =', epsilon(one_sp)
  print *, 'Estimated machine epsilon (double)  =', eps_dp
  print *, 'Fortran intrinsic epsilon(one_dp)   =', epsilon(one_dp)
end program machine_epsilon

Passo 5

Código Python (explicação e variantes):
  • Python float normal costuma ser double (64-bit). O código abaixo calcula \epsilon para float64.
  • Para testar float32 (precisão simples) usar numpy.float32 e fazer as operações com esse tipo.
  • Também mostramos como comparar com numpy.finfo se numpy estiver disponível.
  • Como executar: salvar em um arquivo .py e rodar python3 arquivo.py; se usar numpy instale com pip install numpy.
Código Python:
# Versão simples para float (double precision, Python float)
eps = 1.0
while 1.0 + eps/2.0 > 1.0:
    eps /= 2.0
print("Estimated machine epsilon (Python float, double) =", eps)

# Versão usando numpy para testar float32 (single precision)
try:
    import numpy as np
    eps32 = np.float32(1.0)
    one32 = np.float32(1.0)
    two32 = np.float32(2.0)
    while np.float32(one32 + eps32/two32) > one32:
        eps32 = np.float32(eps32 / two32)
    print("Estimated machine epsilon (numpy.float32, single) =", eps32)

    # comparação com numpy.finfo
    print("numpy.finfo(np.float64).eps =", np.finfo(np.float64).eps)
    print("numpy.finfo(np.float32).eps =", np.finfo(np.float32).eps)
except ImportError:
    print("numpy não está disponível. Para testar float32 instale numpy (pip install numpy).")

Passo 6

Exemplo de saída esperada e interpretação (para você entender o que verá):
  • Em muitas máquinas modernas:
    • \epsilon para double (float64) costuma ser aproximadamente 2{,}220446049250313 \times 10^{-16}.
    • \epsilon para single (float32) costuma ser aproximadamente 1{,}1920929 \times 10^{-7}.
  • Interpretação: se a sua execução der esses valores (ou muito próximos), seu compilador/implementação segue o padrão IEEE-754 típico. Pequenas diferenças são possíveis em arquiteturas exóticas ou compiladores com opções de otimização agressivas.

Passo 7

Dicas finais e verificações extras que ajudam na prova e no uso prático:
  • Fortran tem a função intrínseca \text{epsilon}(x) que retorna precisamente esse valor para o tipo de x; o programa acima imprime para checar.
  • Em Python com numpy, use \text{numpy.finfo(dtype).eps} para obter diretamente.
  • Para testes de comparação com zero na prova/algoritmo, escolha um tolerância tol baseada em \epsilon_{\text{mach}}, por exemplo \text{tol} = 10\cdot\epsilon_{\text{mach}} ou \text{tol} = \sqrt{\epsilon_{\text{mach}}} dependendo da sensibilidade exigida.
  • Incentivo: rode os códigos agora mesmo — é rápido — e veja os valores na sua máquina. Se der problema, me manda o erro que eu te ajudo a corrigir.

Resposta

Fortran:
program machine_epsilon
  implicit none
  integer, parameter :: sp = selected_real_kind(p=6)    ! precisão simples típica
  integer, parameter :: dp = selected_real_kind(p=15)   ! precisão dupla típica

  real(kind=sp) :: one_sp, eps_sp, half_sp
  real(kind=dp) :: one_dp, eps_dp, half_dp

  one_sp = 1.0_sp
  one_dp = 1.0_dp

  ! estimativa para precisão simples
  eps_sp = 1.0_sp
  do
    half_sp = eps_sp / 2.0_sp
    if (one_sp + half_sp <= one_sp) exit
    eps_sp = half_sp
  end do

  ! estimativa para precisão dupla
  eps_dp = 1.0_dp
  do
    half_dp = eps_dp / 2.0_dp
    if (one_dp + half_dp <= one_dp) exit
    eps_dp = half_dp
  end do

  print *, 'Estimated machine epsilon (single)  =', eps_sp
  print *, 'Fortran intrinsic epsilon(one_sp) =', epsilon(one_sp)
  print *, 'Estimated machine epsilon (double)  =', eps_dp
  print *, 'Fortran intrinsic epsilon(one_dp)   =', epsilon(one_dp)
end program machine_epsilon
Python:
# Versão simples para float (double precision, Python float)
eps = 1.0
while 1.0 + eps/2.0 > 1.0:
    eps /= 2.0
print("Estimated machine epsilon (Python float, double) =", eps)

# Versão usando numpy para testar float32 (single precision)
try:
    import numpy as np
    eps32 = np.float32(1.0)
    one32 = np.float32(1.0)
    two32 = np.float32(2.0)
    while np.float32(one32 + eps32/two32) > one32:
        eps32 = np.float32(eps32 / two32)
    print("Estimated machine epsilon (numpy.float32, single) =", eps32)

    # comparação com numpy.finfo
    print("numpy.finfo(np.float64).eps =", np.finfo(np.float64).eps)
    print("numpy.finfo(np.float32).eps =", np.finfo(np.float32).eps)
except ImportError:
    print("numpy não está disponível. Para testar float32 instale numpy (pip install numpy).")

Exercícios relacionados