[Python-es] El infinito se me queda corto

Chema Cortes pych3m4 en gmail.com
Mie Mar 20 14:02:02 CET 2013


El día 20 de marzo de 2013 13:19, José Sabater Montes <jsm en iaa.es> escribió:
>
> Hola,
>
> El 20/03/2013, a las 11:00, Chema Cortes escribió:
>
>> Supongo que habrá una explicación. Si realizo el siguiente cálculo:
>>
>> In [13]: import math
>>
>> In [14]: math.tan(math.pi/2)
>> Out[14]: 1.633123935319537e+16
>>
>> Aunque no esperaba que me diese la solución correcta de
>> float("+inf")/float("-inf"), me resulta muy corto para el tamaño
>> máximo que podría tener un double:
>>
>> In [16]: sys.float_info.max
>> Out[16]: 1.7976931348623157e+308
>>
>> He comprobado que pasa lo mismo con scala e, incluso, en el buscador
>> de google (tal vez sea que google usa también python):
>>
>> https://www.google.com/search?q=tan(pi%2F2)
>>
>> ¿Existe alguna explicación?
>
>>>> import math
>>>> math.pi/2.
> 1.5707963267948966
>
> Aparentemente la tangente de ese número con esa precisión es exactamente esa calculando con la precisión de un float.
> Para aumentar la precisión creo que hay que usar una librería que ofrezca precisión arbitraria como el módulo "decimal" de la librería estándar o mpmath.
> El problema con "decimal" es que no implementa funciones matemáticas un poco más complejas como las trigonométricas, así que el resultado de la tangente sería el mismo si se usan las mismas funciones que antes.
>
>>>> from decimal import Decimal
>>>> pi_dec = Decimal('3.1415926535897932384626433832795028841971693993751')
>>>> pi_dec
> Decimal('3.1415926535897932384626433832795028841971693993751')
>>>> pi_dec/Decimal('2')
> Decimal('1.570796326794896619231321692')
>>>> math.tan(pi_dec/Decimal('2'))
> 1.633123935319537e+16
>
> Si no me equivoco, las funciones trigonométricas se suelen calcular usando series. Una buena librería de precisión arbitraria (supongo que todas) irá adaptando el número de sumandos a la precisión requerida. Por ejemplo:
>
>>>> from mpmath import *
>>>> tan(1.5707963267948966)
> mpf('16331239353195370.0')
>>>> tan(pi/2)
> mpf('16331239353195370.0')
>
> Aumentamos la precisión
>
>>>> mp.dps = 50
>>>> tan(1.5707963267948966)
> mpf('16331239353195369.755967737041528916530864068104910291')
>>>> tan(pi/2)
> mpf('-1978834901269570871682051952580899049722178117311132.0')
>>>> tan(mpf('1.5707963267948966192313216916397514420985846996875534'))
> mpf('-1978834901269570871682051952580899049722178117311132.0')
>
>
>> ¿Algún modo de ajustar más el resultado a
>> infinito (sin ser la solución trivial de comprobar los parámetros de
>> entrada)?
>
> Creo que la única opción es usar un módulo matemático de precisión arbitraria como mpmath o bigfloat.


Por lo que parece, la tendencia a infinito está en relación con la
precisión del cálculo. Cuanto más digitos de precisión, tanto más
grande será el número

Comprobando con mpmath:

dps    resultado aprox.
------ | -----------------------
10      390115388672.0
15      1.63312393531954e+16
50     -1.97883490126957e+51
100     6.97133053829442e+101
300     1.59802604184178e+301
1000    2.84594257232622e+1001
5000   -1.04829403956079e+5001
10000  -7.59194855601038e+10001
12000   8.19055339712727e+12001
15000   1.18530955294349e+15001
20000   1.34274677646898e+20001
30000  -1.14011942791359e+30001

El que alternen los signos supongo que será cosa del algorítmo de aproximación.


--
Hyperreals *R: http://ch3m4.org/blog
Quarks, bits y otras criaturas infinitesimales


Más información sobre la lista de distribución Python-es