2017-03-17 410 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或更大),我們不會有問題。