Diferencia asombrosamente grande al evaluar la identidad trigonométrica con NumPy

8

Según Wolfram Alpha y el sistema de álgebra computacional Sage , la siguiente identidad se mantiene:

cos(arctan(l1l2d))=11+(l1l2)2d2

Sin embargo, cuando traté de verificarlo con un ejemplo arbitrario en NumPy, noté una diferencia bastante grande en los valores reales calculados por ambos lados de la identidad. He usado el siguiente 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))

La evaluación de los resultados lefty rightarrojó lo siguiente:

    left = 0.99315060432287616
    right = 1.0

Es tentador descartar esto simplemente como un error numérico, pero dado que tengo muy poca experiencia en la magnitud de los errores numéricos, no estoy tan seguro. ¿Es esto posible o me falta algo (obvio)?

Daniel Eberts
fuente
2
tu rightse introduce de forma incorrecta. debería ser right = 1/sqrt() Cuando ingreso las fórmulas en mi Ti-89 obtengo una coincidencia de 12 dígitos a 0.99315 ...
Godric Seer
Sí, creo que lo habría captado si la expresión de raíz cuadrada se hubiera evaluado correctamente.
Michael Grant
Sí, cuando entré en su expresión obtuve 1.007, que definitivamente habría sido visible.
Godric Seer
1
Oh enserio. Eso es vergonzoso. Gracias por notarlo.
Daniel Eberts
Cometí este error antes que yo.
Michael Grant

Respuestas:

10

Sospecho que su expresión de Python para el lado derecho está haciendo una división entera, no una división de punto flotante. Como resultado, ((l1-l2)**2)/(d**2)se evalúa como cero, y el término dentro de la raíz cuadrada es uno.

De hecho, también olvidó el recíproco en su expresión de la mano derecha, pero ese no es el primer problema ...

En 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
fuente
66
Me he acostumbrado a usar from __future__ import divisionen la parte superior de todos mis scripts de Python para este propósito.
Geoffrey Irving
@GeoffreyIrving, convierta eso en una respuesta y apuesto a que obtendrá un representante serio para eso. Buen consejo.
Michael Grant
7

Como señaló Michael C. Grant, el problema es que la operación de división es división entera, no división de coma flotante. En versiones de Python anteriores a 3, dividir dos enteros usando /rondas hacia abajo a un entero. Este comportamiento se puede cambiar agregando

from __future__ import division

en la parte superior de tu guión. Tenga en cuenta que esta declaración solo cambia el comportamiento del /archivo actual. Si desea ocasionalmente el comportamiento original, use //(doble barra). Esto se discute con más detalle aquí:

PEP 238 - Cambio del operador de división

Tenga en cuenta que si bien la mayoría de los lenguajes, incluido C, tienen una semántica similar para la división, la semántica es más confusa en Python porque los enteros generalmente no se promueven a flotantes cuando se pasan a las funciones. Por lo tanto, una función escrita como si estuviera operando en flotadores puede de hecho estar operando en enteros.

Geoffrey Irving
fuente