Деление больших чисел в Python
Я пытаюсь разделить несколько больших чисел в Python, но получаю странные результаты
NStr = "7D5E9B01D4DCF9A4B31D61E62F0B679C79695ACA70BACF184518" \
"8BDF94B0B58FAF4A3E1C744C5F9BAB699ABD47BA842464EE93F4" \
"9B151CC354B21D53DC0C7FADAC44E8F4BDF078F935D9D07A2C07" \
"631D0DFB0B869713A9A83393CEC42D898516A28DDCDBEA13E87B" \
"1F874BC8DC06AF03F219CE2EA4050FA996D30CE351257287"
N = long(NStr, 16)
f2 = 476
fmin = N / float(f2)
print N - (fmin * float(f2))
Это выводит как 0.0
как и ожидалось. Однако, если я, например, изменить код на
fmin = N / float(f2)
fmin += 1
Я все еще получаю вывод 0.0
Я также попытался использовать десятичный пакет
fmin = Decimal(N) / Decimal(f2)
print Decimal(N) - (fmin * Decimal(f2))
Но это дает мне вывод -1.481136900397802034028076389E+280
Я предполагаю, что я не говорю Python, как правильно обрабатывать большие числа, но я не знаю, куда идти дальше.
Я также должен добавить, что конечной целью является расчет
fmin = ceil(N / float(f2))
максимально длинный и точный
6 ответов
Расширяю свой комментарий, если N
а также f2
являются long
строго больше 0, то
fmin = (N - 1) // f2 + 1
это точно ceil(N / float(f2))
(но даже более точно, чем использование поплавков).
(Использование //
скорее, чем /
для целочисленного деления - для совместимости с Python 3.x без лишних усилий.)
Это потому что N // f2
дает вам (в основном) floor(N / float(f2))
так что N // f2 + 1
почти всегда так же, как ceil
, Однако когда N
это кратное f2
, N // f2 + 1
слишком большой (+1
не должно быть там), но с помощью N - 1
исправляет это и не нарушает другой случай.
(Это не работает ни для N
, f2
меньше или равно 0, но это может быть обработано отдельно)
fmin
это float
после того, как вы разделите длинное целое число на число с плавающей точкой. Его ценность 1.84952718165824e+305
, Добавление 1 к этому ничего не меняет, точность просто не так высока.
Если вы вместо этого сделаете целочисленное деление, fmin
остается long
:
>>> fmin = N / f2
>>> fmin
18495271816582402193321106509793602189617847415669131056768139225220221855498293
49983070478076000952025038039460539798061570960870927236986153971731443029057201
52035339202255934728764897424408896865977423536890485615063959262845076766281553
766072964756078264853041334880929452289298495368916583660903481130L
>>> N - (fmin * f2)
111L
Конечно, вы не получаете 0
из-за целочисленного деления, где отбрасывается десятичная часть результата. Но теперь добавление 1 будет иметь значение:
>>> N - ((fmin+1) * f2)
-365L
С использованием Decimal
модуль не меняет проблему:
>>> from decimal import Decimal, getcontext
>>> fmin = Decimal(N) / Decimal(f2)
>>> fmin
Decimal('1.849527181658240219332110651E+305')
Там все еще нет безграничной точности, и даже если вы установите Decimal.getcontext().prec = 2000
, вы все равно не получите точно 0.
Если вам нужна точность, вообще избегайте арифметики с плавающей точкой. Поскольку в python есть целые числа произвольной точности, вы можете вычислить потолок деления, используя базовую целочисленную арифметику. Предполагая, что дивиденд и делитель являются положительными, способ сделать это состоит в том, чтобы добавить делитель- 1 к дивиденду перед делением. В твоем случае:
fmin = (N + f2 - 1) / f2
На Python 3.x используйте //
оператор вместо /
получить целочисленное деление.
Fraction
от fractions
модуль может быть полезен:
> : N = Fraction(N)
> : f2 = Fraction(f2)
> : fmin = N / f2
> : print N-f2*fmin
0
> : fmin += 1
> : print N-f2*fmin
-476
Но если ваша единственная цель состоит в том, чтобы рассчитать ceil(N / float(f2))
ты можешь использовать:
> : fmin = N/f2 + int(ceil((N % f2) / float(f2)))
> : print fmin
184952718165824021933211065097936021896178474156691310567681392252202218554982934998307047807600095202503803946053979806157096087092723698615397173144302905720152035339202255934728764897424408896865977423536890485615063959262845076766281553766072964756078264853041334880929452289298495368916583660903481131
Поплавкам просто не хватает точности для такого рода операций.
Вы можете улучшить точность decimal
модуль с getcontext()
, Например, чтобы использовать 65536 десятичных знаков:
from decimal import Decimal, getcontext
getcontext().prec = 2**16
Затем:
>>> print Decimal(N) - (fmin * Decimal(f2))
-2E-65228
Все еще не 0, но ближе:)
Смотрите этот ответ, чтобы сделать ceil()
на Decimal
объект.
Я также обнаружил подобное поведение в 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
По причинам, которые я не понимаю, кажется, что вычитание float из long дает 0.
Казалось бы, решение состоит в том, чтобы преобразовать оба в decimal.Decimal
:
In [32]: decimal.Decimal(N) - decimal.Decimal(fmin * float(f2))
Out[32]: Decimal('4.099850360284731589507226352E+291')