¿Tiene un significado especial 16331239353195370.0?

88

Usando import numpy as nphe notado que

np.tan(np.pi/2)

da el número en el título y no np.inf

16331239353195370.0

Tengo curiosidad por este número. ¿Está relacionado con algún parámetro de precisión de la máquina del sistema? ¿Podría haberlo calculado a partir de algo? (Estoy pensando en algo similar a sys.float_info)

EDITAR: El mismo resultado es de hecho reproducible en otros entornos como Java, octace, matlab ... Sin embargo, el engaño sugerido no explica por qué.

Un chico
fuente
10
No me gusta esa respuesta, es completamente ondulada a mano, no explica realmente la causa. "Bueno, tan (pi / 2) en radianes es esencialmente infinito, ¿no es así?" no explica nada sobre por qué, como preguntó el OP aquí, la respuesta no es de hecho np.inf. Pero es sencillo no solo explicar por qué no lo es, sino también explicar por qué la respuesta es exactamente lo que se vio, y así lo hice ;-)
Tim Peters

Respuestas:

119

pino es exactamente representable como Python float (igual que el doubletipo de la plataforma C ). Se utiliza la aproximación representable más cercana.

Aquí está la aproximación exacta en uso en mi caja (probablemente la misma que en su caja):

>>> import math
>>> (math.pi / 2).as_integer_ratio()
(884279719003555, 562949953421312)

Para encontrar la tangente de esa relación, ahora voy a cambiar a wxMaxima:

(%i1) fpprec: 32;
(%o1) 32
(%i2) tan(bfloat(884279719003555) / 562949953421312);
(%o2) 1.6331239353195369755967737041529b16

Esencialmente idéntico a lo que tienes. La aproximación binaria a pi/2usada es un poco menor que el valor matemático ("precisión infinita") de pi/2. Entonces obtienes una tangente muy grande en lugar de infinity. ¡El calculado tan()es apropiado para la entrada real!

Exactamente por el mismo tipo de razones, por ejemplo,

>>> math.sin(math.pi)
1.2246467991473532e-16

no devuelve 0. La aproximación math.pies un poco menor que pi, y el resultado mostrado es correcto dada esa verdad.

OTRAS FORMAS DE VER math.pi

Hay varias formas de ver la aproximación exacta en uso:

>>> import math
>>> math.pi.as_integer_ratio()
(884279719003555, 281474976710656)

math.pi es exactamente igual al valor matemático ("precisión infinita") de esa relación.

O como un flotador exacto en notación hexadecimal:

>>> math.pi.hex()
'0x1.921fb54442d18p+1'

O de la manera más fácil de entender para casi todos:

>>> import decimal
>>> decimal.Decimal(math.pi)
Decimal('3.141592653589793115997963468544185161590576171875')

Si bien puede no ser inmediatamente obvio, cada flotante binario finito es exactamente representable como un flotante decimal finito (lo contrario no es cierto; por ejemplo, el decimal 0.1no es exactamente representable como un flotante binario finito), y el Decimal(some_float)constructor produce el equivalente exacto.

Aquí está el valor real de piseguido del valor decimal exacto de math.pi, y un signo de intercalación en la tercera línea apunta al primer dígito donde difieren:

true    3.14159265358979323846264338327950288419716939937510...
math.pi 3.141592653589793115997963468544185161590576171875
                         ^

math.pies el mismo en "casi todos" los cuadros ahora, porque casi todos los cuadros ahora usan el mismo formato de punto flotante binario (IEEE 754 doble precisión). Puede utilizar cualquiera de las formas anteriores para confirmar eso en su caja, o para encontrar la aproximación precisa en uso si su caja es una excepción.

Tim Peters
fuente
@Tim Peters - Esto está perfectamente claro. Para completar, supongo que esta representación de ¿ np.pies la representación racional más cercana a la épsilon del sistema?
Aguy
3
Suponiendo que np.pitiene el mismo valor que Python math.pi(no lo comprobé, pero puedes ;-)), es el valor más cercano al pi matemático representable en el C doubleformato de punto flotante nativo de la plataforma . Lo que significa IEEE 754 de doble precisión en casi todas las cajas ahora, y por lo tanto, el flotador binario más cercano con 53 bits de precisión (mantisa). Por lo tanto, el conjunto de racionales está restringido a la forma +/- I * 2**Jdonde el entero Ies 0 o 2**52 <= I < 2**53, y el rango de números enteros Jes lo suficientemente amplio como para cubrir todos los racionales de esta forma en cualquier lugar cercano pi.
Tim Peters
2
Y es por eso que me encantaría que las funciones trigonométricas "binarias" se implementaran más comúnmente. Dado que pi nunca se puede representar de forma racional, sería útil con un conjunto de funciones que operan en ángulos de 0 a 1.
pipe
Bueno, importaron np.pi, no math.pi.
EKons
2
@ Έρικ Κωνσταντόπουλος math.pi, np.piy scipy.pison todos iguales; están duplicados solo para facilitar el nombre; stackoverflow.com/questions/12645547/…
Tim Peters