2012-04-12 12 views
7

estoy tratando de dividir algunas grandes números en Python, pero estoy consiguiendo algunos resultados extrañosen Python

NStr = "7D5E9B01D4DCF9A4B31D61E62F0B679C79695ACA70BACF184518" \ 
     "8BDF94B0B58FAF4A3E1C744C5F9BAB699ABD47BA842464EE93F4" \ 
     "9B151CC354B21D53DC0C7FADAC44E8F4BDF078F935D9D07A2C07" \ 
     "631D0DFB0B869713A9A83393CEC42D898516A28DDCDBEA13E87B" \ 
     "1F874BC8DC06AF03F219CE2EA4050FA996D30CE351257287" 

N = long(NStr, 16) 
f2 = 476 

fmin = N/float(f2) 

print N - (fmin * float(f2)) 

Este salidas como 0.0 como se esperaba. Sin embargo si, por ejemplo, cambiar el código para

fmin = N/float(f2) 
fmin += 1 

sigo teniendo una salida de 0.0

También he intentado usar el paquete decimal

fmin = Decimal(N)/Decimal(f2) 
print Decimal(N) - (fmin * Decimal(f2)) 

Pero eso me da una salida de -1.481136900397802034028076389E+280

Supongo que no le estoy diciendo a python cómo manejar correctamente los números grandes, pero no sé a dónde ir desde aquí.

También me gustaría añadir que el objetivo final es calcular

fmin = ceil(N/float(f2)) 

como una larga y lo más preciso posible

+0

¿'f2' siempre va a ser un número entero? – huon

+0

@dbaupp sí, será –

Respuesta

3

Ampliando mi comentario, si N y f2 son long s estrictamente mayor que 0,

fmin = (N - 1) // f2 + 1 

es exactamente ceil(N/float(f2)) (pero aún más exactamente que usar flotadores).

(El uso de // en lugar de / para la división entera es para la compatibilidad con Python 3.x para ningún esfuerzo adicional.)

Es porque le da N // f2 (básicamente) floor(N/float(f2)) y así N // f2 + 1 es casi siempre el lo mismo que ceil. Sin embargo, cuando N es un múltiplo de f2, N // f2 + 1 es demasiado grande (el +1 no debería estar allí) pero al usar N - 1 lo soluciona, y no rompe el otro caso.

(Esto no funciona, ya sea para N, f2 menor o igual a 0, pero que puede manejarse por separado)

4

fmin es una float después se divide el entero largo por un flotador. Su valor es 1.84952718165824e+305. Agregar 1 a eso no lo cambia en absoluto, la precisión simplemente no es tan alta.

Si lo hace la división entera en cambio, sigue siendo un fminlong:

>>> fmin = N/f2 
>>> fmin 
18495271816582402193321106509793602189617847415669131056768139225220221855498293 
49983070478076000952025038039460539798061570960870927236986153971731443029057201 
52035339202255934728764897424408896865977423536890485615063959262845076766281553 
766072964756078264853041334880929452289298495368916583660903481130L 
>>> N - (fmin * f2) 
111L 

Por supuesto, usted no está consiguiendo 0 debido a la división entera, donde se descarta la parte decimal del resultado. Pero ahora, la adición de 1 se hacer una diferencia:

>>> N - ((fmin+1) * f2) 
-365L 

Con el módulo de Decimal no cambia el problema:

>>> from decimal import Decimal, getcontext 
>>> fmin = Decimal(N)/Decimal(f2) 
>>> fmin 
Decimal('1.849527181658240219332110651E+305') 

todavía no hay precisión ilimitada, e incluso si se establece Decimal.getcontext().prec = 2000, todavía no obtendría exactamente 0.

+0

Este no es el comportamiento que veo en python 2.7.2 – Marcin

+0

Esto es lo que veo en Python 2.7.2 (Windows 7 x64, versión de 64 bits de Python). ¿Que estas usando? –

+0

Entonces, si quisiera hacer 'fmin = math.ceil (N/f2)' y obtener la respuesta como un tiempo largo pero con el mayor grado de precisión posible, ¿qué haría? –

1

Los flotadores simplemente no tienen suficiente precisión para este tipo de operaciones.

Puede mejorar la precisión del módulo decimal con getcontext(). Por ejemplo, para usar 65536 cifras decimales:

from decimal import Decimal, getcontext 
getcontext().prec = 2**16 

continuación:

>>> print Decimal(N) - (fmin * Decimal(f2)) 
-2E-65228 

Aún no es 0 pero más cerca :)

Ver this answer para hacer un ceil() en un objeto Decimal.

1

También encontré el comportamiento en python 2.7.2:

In [17]: N 
Out[17]: 8803749384693223444020846698661754642258095369858506383021634271204825603217187705919415475641764531639181067832169438773077773745613648054092905441668 
8183122792368821460273824930892091174018634908205253603559871152770444609114256540750019592650731223893254070047675403322419289706083795604293822590057017991L 

In [18]: 

In [18]: (fmin * float(f2)) 
Out[18]: 8.803749384693223e+307 
In [19]: N - (fmin * float(f2)) 
Out[19]: 0.0 

In [20]: (fmin * float(f2)) 
Out[20]: 8.803749384693223e+307 

In [21]: N == (fmin * float(f2)) 
Out[21]: False 

In [22]: N < (fmin * float(f2)) 
Out[22]: False 

In [23]: N > (fmin * float(f2)) 
Out[23]: True 

Por razones que no entiendo, parece que restar un flotador de un largo rendimientos La solución parece 0.

ser para convertir tanto a un decimal.Decimal:

In [32]: decimal.Decimal(N) - decimal.Decimal(fmin * float(f2)) 
Out[32]: Decimal('4.099850360284731589507226352E+291') 
+0

Pero si convierte ambos a decimal obtiene la respuesta incorrecta, seguramente debería ser '0.0' –

+0

@NathanBaggs ¿Por qué debería ser 0? Ellos no son iguales. – Marcin

+0

si 'fmin = N/f2' luego' fmin * f2 = N' –

1

Fraction del módulo fractions podría ser útil:

> : N = Fraction(N) 
> : f2 = Fraction(f2) 
> : fmin = N/f2 
> : print N-f2*fmin 
0 
> : fmin += 1 
> : print N-f2*fmin 
-476 

Pero si su único objetivo es calcular ceil(N/float(f2)) puede utilizar:

> : fmin = N/f2 + int(ceil((N % f2)/float(f2))) 
> : print fmin 
184952718165824021933211065097936021896178474156691310567681392252202218554982934998307047807600095202503803946053979806157096087092723698615397173144302905720152035339202255934728764897424408896865977423536890485615063959262845076766281553766072964756078264853041334880929452289298495368916583660903481131 
+0

¿Math.ceil (N/float (f2)) da el mismo resultado, si no es así? –

+0

@NathanBaggs: No, no lo hará. Como han señalado otros, cuando conviertes para flotar, estás perdiendo información. Eso es antes de la llamada 'ceil'. En cualquier caso, 'ceil' devolverá un flotador, por lo que allí de nuevo. El mismo problema. – Avaris

+0

'int (ceil ((N% f2)/float (f2)))' es demasiado complicado para la tarea que está haciendo: ¿qué tal '(1 si N% f2 else 0)'? – huon

3

Si necesita precisión, no aritmética de punto flotante por completo. Dado que python tiene enteros de precisión arbitraria, puede calcular el límite máximo de la división utilizando la aritmética de enteros básicos. Suponiendo que el dividendo y el divisor son positivos, la forma de hacerlo es agregar divisor - 1 al dividendo antes de dividir. En su caso:

fmin = (N + f2 - 1)/f2 

En Python 3.x uso, el usuario // en lugar de / para conseguir la división entera.