2017-03-17 66 views
1

使用Python 3.5.2decimal.InvalidOperation,DivisionImpossible非常大的数字

>>> from decimal import Decimal 
>>> Decimal('12') % Decimal('0.01') 
Decimal('0.00') 
>>> Decimal('234567') % Decimal('0.01') 
Decimal('0.00') 

按预期工作。但...

>>> Decimal('7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450') % Decimal('0.01') 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>] 

编辑。这是我发现的最小数量可能会导致这个错误:

>>> Decimal(100000000000000000000000000) % Decimal('0.01') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>] 

为什么Decimal(very_large_int) % Decimal('0.01')给这个错误?我认为Decimal能够处理非常大的数字?

+0

哦,来吧,你会怎么做这么大的数字...... –

+0

我刚发布这个,因为它很好奇。为什么Decimal会以这种方式失败?明确可能的时候,为什么'部门不可能'? (至少对我来说) – Flux

+0

你为什么用这么大的数字,我们必须滚动? 731671765313306249192251196对你不会造成同样的问题吗?或者你是否试图找出导致问题的最小长度? –

回答

1

Decimal基于十进制算术规范。你可以看到here

the integer result of a divide-integer or remainder operation had too many digits (would be longer than precision).

这种精密的东西,你可以调整“司不可能”的意思是:

>>> decimal.getcontext().prec=10000 
>>> Decimal('7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088 
... 0551112540698747158523863050715693290963295227443043557668966489504452445231617318564030987111217223831136222989342338030813533627661428280644448664523874 
... 9303589072962904915604407723907138105158593079608667017242712188399879790879227492190169972088809377665727333001053367881220235421809751254540594752243525 
... 8490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637 
... 0484403199890008895243450658541227588666881164271714799244429282308634656748139191231628245861786645835912456652947654568284891288314260769004224219022671 
... 0556263211111093705442175069416589604080719840385096245544436298123098787992724428490918884580156166097919133875499200524063689912560717606058861164671094 
... 0507754100225698315520005593572972571636269561882670428252483600823257530420752963450') % Decimal('0.01') 
Decimal('0.00') 
+1

但是结果**没有太多的数字。结果为0.与使用较小数字时相同。 –

+0

@StefanPochmann不错,我认为在这种情况下,这些词的实现不是真的。我假设它只是意味着“我不能处理数字大于$ precision的整数除法/余数运算”。 – L3viathan

+1

也不能这样,因为具有'%Decimal(str(10 ** 990))'的巨大数字确实有效(结果为“Decimal”('1.330624919225119674426574742E + 989')')。 –

2

由于L3viathan answered,问题是结果(不结果,这是我在评论中提到的“隐藏部分”)超出了可用的精度。

的隐藏部分更明显,如果我们使用Python2:

Traceback (most recent call last): 
    File "/tmp/d.py", line 24, in <module> 
    print(big % Decimal('0.01')) 
    File "/usr/local/lib/python2.7/decimal.py", line 1460, in __mod__ 
    remainder = self._divide(other, context)[1] 
    File "/usr/local/lib/python2.7/decimal.py", line 1381, in _divide 
    'quotient too large in //, % or divmod') 
    File "/usr/local/lib/python2.7/decimal.py", line 3873, in _raise_error 
    raise error(explanation) 
InvalidOperation: quotient too large in //, % or divmod 

实质上,a % b通过在Knuth的第2卷做既除法和模量一起(一拉算法d实现;对于C实现限制为两个全字,请参阅qdivrem.c code I wrote in the early 2000s)。库代码因此需要两个额外的数字(Decimal('0.01')中小数点右边的位数 - 计算实际需要的位数并不像下面的big那样简单,因为我们必须查看指数)来计算中间商。

十进制库直接在C中为Python3重新实现,它隐藏了细节,但两者的治疗方法相同:扩展精度。下面是捕获异常,并再次尝试,一个示例源程序虽然与魔法不变2.

from __future__ import print_function 
import decimal 
Decimal = decimal.Decimal 
import traceback 
big = Decimal(
    '731671765313306249192251196744265747423553491949349698352031277' 
    '4506326239578318016984801869478851843858615607891129494954595017379' 
    '5833195285320880551112540698747158523863050715693290963295227443043' 
    '5576689664895044524452316173185640309871112172238311362229893423380' 
    '3081353362766142828064444866452387493035890729629049156044077239071' 
    '3810515859307960866701724271218839987979087922749219016997208880937' 
    '7665727333001053367881220235421809751254540594752243525849077116705' 
    '5601360483958644670632441572215539753697817977846174064955149290862' 
    '5693219784686224828397224137565705605749026140797296865241453510047' 
    '4821663704844031998900088952434506585412275886668811642717147992444' 
    '2928230863465674813919123162824586178664583591245665294765456828489' 
    '1288314260769004224219022671055626321111109370544217506941658960408' 
    '0719840385096245544436298123098787992724428490918884580156166097919' 
    '1338754992005240636899125607176060588611646710940507754100225698315' 
    '520005593572972571636269561882670428252483600823257530420752963450') 
try: 
    print(big % Decimal('0.01')) 
except decimal.DecimalException: 
    traceback.print_exc() 
    print('') 
    ctx = decimal.getcontext() 
    print('failed because precision was', ctx.prec, 'and big is', 
     len(big.as_tuple().digits), 'digits long') 
    print('trying again with 2 more digits') 
    with decimal.localcontext() as ctx: 
     ctx.prec = len(big.as_tuple().digits) + 2 
     try: 
      print(big % Decimal('0.01')) 
     except decimal.DecimalException: 
      traceback.print_exc() 

随着Python2:

$ python2 /tmp/d.py 
Traceback (most recent call last): 
    File "/tmp/d.py", line 24, in <module> 
    print(big % Decimal('0.01')) 
    File "/usr/local/lib/python2.7/decimal.py", line 1460, in __mod__ 
    remainder = self._divide(other, context)[1] 
    File "/usr/local/lib/python2.7/decimal.py", line 1381, in _divide 
    'quotient too large in //, % or divmod') 
    File "/usr/local/lib/python2.7/decimal.py", line 3873, in _raise_error 
    raise error(explanation) 
InvalidOperation: quotient too large in //, % or divmod 

failed because precision was 28 and big is 1000 digits long 
trying again with 2 more digits 
0.00 

有了Python3:

$ python3 /tmp/d.py 
Traceback (most recent call last): 
    File "/tmp/d.py", line 24, in <module> 
    print(big % Decimal('0.01')) 
decimal.InvalidOperation: [<class 'decimal.DivisionImpossible'>] 

failed because precision was 28 and big is 1000 digits long 
trying again with 2 more digits 
0.00 

注意,除以一个非常大的数字实际上更容易:这是由0.01划分,这在这里造成问题。如果除数的指数至少为1000 - 28(1e972或更大),我们不会有问题。