Diferença surpreendentemente grande ao avaliar a identidade trigonométrica com o NumPy

8

De acordo com Wolfram Alpha e o sistema de álgebra computacional Sage , a seguinte identidade é válida:

porque(arctan(eu1-eu2d))=11+(eu1-eu2)2d2

No entanto, quando tentei verificar isso com um exemplo arbitrário no NumPy, notei uma grande diferença nos valores reais calculados pelos dois lados da identidade. Eu usei o seguinte código:

    l1 = 10; l2 = 8; d = 17
    from numpy import arctan2, cos, sin, sqrt

    alpha = arctan2((l1-l2),d)
    left = cos(alpha)

    right = sqrt(1 + ((l1-l2)**2)/(d**2))

Avaliando os resultados lefte rightproduzindo o seguinte:

    left = 0.99315060432287616
    right = 1.0

É tentador escrever isso simplesmente como um erro numérico, mas como tenho pouca experiência em como podem ocorrer erros numéricos grandes, não tenho tanta certeza. Isso é possível ou estou faltando alguma coisa (óbvio)?

Daniel Eberts
fonte
2
o seu rightfoi inserido incorretamente. deve ser right = 1/sqrt() Quando eu entro as fórmulas em meu Ti-89 eu recebo uma correspondência para 12 dígitos em 0,99315 ...
Godric Seer
Sim, acho que ele entenderia se a expressão da raiz quadrada tivesse sido avaliada corretamente.
Michael Grant
Sim, quando entrei na expressão dele, recebi 1.007, o que definitivamente seria visível.
21813 Godric Seer
1
Sério. Isso é embaraçoso. Obrigado por perceber.
precisa saber é o seguinte
Cometi esse erro antes de mim.
Michael Grant

Respostas:

10

Suspeito que sua expressão Python para o lado direito esteja fazendo divisão inteira, não divisão de ponto flutuante. Como resultado, ((l1-l2)**2)/(d**2)está sendo avaliado como zero, e o termo dentro da raiz quadrada é um.

Na verdade, você esqueceu o recíproco na sua expressão direita também, mas esse não é o primeiro problema ...

No MATLAB:

>> l1 = 10; l2 = 8; d = 17;
>> alpha = atan2((l1-l2),d)
alpha =
    0.1171
>> left = cos(alpha)
left =
    0.9932
>> right = 1/sqrt(1 + ((l1-l2)^2)/(d^2))
right =
    0.9932
Michael Grant
fonte
6
Adquiri o hábito de usar from __future__ import divisionno topo de todos os meus scripts python para esse fim.
Geoffrey Irving
@GeoffreyIrving, transforme isso em uma resposta e eu aposto que você recebe um representante sério por isso. Bom conselho.
Michael Grant
7

Como Michael C. Grant apontou, o problema é que a operação de divisão é divisão inteira, não divisão de ponto flutuante. Nas versões do Python anteriores a 3, divida dois números inteiros usando /arredondamentos para baixo para um número inteiro. Esse comportamento pode ser alterado adicionando

from __future__ import division

no topo do seu script. Observe que esta declaração altera o comportamento de /apenas para o arquivo atual. Se desejar ocasionalmente o comportamento original, use //(barra dupla). Isso é discutido em mais detalhes aqui:

PEP 238 - Mudando de Operador da Divisão

Observe que, embora a maioria das linguagens, incluindo C, tenha semântica semelhante para divisão, a semântica é mais confusa no Python, porque os números inteiros geralmente não são promovidos a flutuadores quando passados ​​para as funções. Assim, uma função escrita como se estivesse operando em flutuadores pode de fato estar operando em números inteiros.

Geoffrey Irving
fonte