2014-10-03 99 views
2

在Python中,我應該使用負指數還是分數?例如:Python負指數與分區

>>> num = 2.0**-1 

>>> num = 1/2.0 

我跑一個小的測試,它看起來像的差異歸結爲BINARY_POWER與BINARY_DIVIDE:

import dis 

def exp_test(x): 
    return x**-1 

def div_test(x): 
    return 1/x 

print "Exp Test:" 
dis.dis(exp_test) 
print "Div Test:" 
dis.dis(div_test) 

輸出:

Exp Test: 
2   0 LOAD_FAST    0 (x) 
      3 LOAD_CONST    1 (-1) 
      6 BINARY_POWER   
      7 RETURN_VALUE 
Div Test: 
2   0 LOAD_CONST    1 (1) 
      3 LOAD_FAST    0 (x) 
      6 BINARY_DIVIDE  
      7 RETURN_VALUE 

我只想着漂浮在這裏的價值點。我想我只需要擔心浮點運算中出現的常見差異。

+4

你在擔心什麼?安全?性能?精確? – CoryKramer 2014-10-03 19:09:23

+0

@Cyber​​與性能或速度相比,更關心安全性和精度。 – 2014-10-03 19:19:32

+0

您希望使用此輸入值的範圍是多少?只有'1/2'? – 2014-10-03 19:35:37

回答

3

如果你計算的一個Python floatx倒數,我想不出任何理由,更喜歡x**-1.01.0/x。主觀上,我發現後者更易於閱讀。客觀地說,它可能更快(因爲它包裝了一個簡單的CPU指令,而不是對C庫的pow函數的調用),並且更準確(基於相同的原因)。

請注意,我故意用1.0代替1在兩個表達式,一方面是因爲避免在x是浮動的情況了額外的int-漂浮的轉換,因爲它會保證我得到一個在x碰巧是一個整數的情況下,在Python 2.7中使用適當的float分區而不是分區分區。

要備份「更快」的聲明,以下是我的機器上的一些定時,包括11.0的變體。這是在Python 2.7下,但在Python 3.4下的結果類似。

>>> import timeit 
>>> timeit.timeit('x**-1', 'x=12.34') 
0.08957314491271973 
>>> timeit.timeit('x**-1.0', 'x=12.34') 
0.08102011680603027 
>>> timeit.timeit('1/x', 'x=12.34') 
0.06166410446166992 
>>> timeit.timeit('1.0/x', 'x=12.34') 
0.04489898681640625 

有一個相當明顯的優勢在這裏劃分形式,以及使用1.0代替1明顯加速。你的結果可能有所不同

注意:請勿直接插入時間表達式陷阱,如2**-11.0/2.0。這些表達式可以在編譯時通過Python的窺視優化器優化爲一個常量,這樣所有結束計時的時間就是檢索常量0.5的時間。爲此,可以使用標準庫中的dis模塊看到:

>>> def half_via_pow(): return 2.0**-1.0 
... 
>>> def half_via_div(): return 1.0/2.0 
... 
>>> import dis 
>>> dis.dis(half_via_pow) 
    1   0 LOAD_CONST    3 (0.5) 
       3 RETURN_VALUE   
>>> dis.dis(half_via_div) 
    1   0 LOAD_CONST    1 (1.0) 
       3 LOAD_CONST    2 (2.0) 
       6 BINARY_DIVIDE  
       7 RETURN_VALUE   

上面顯示出關於Python 2.7,計算2.0**-1.0被優化以恆定的,而分工1.0/2.0不是。在Python 3.4中,兩者都被優化爲常量。我懷疑Python 2。7窺視孔優化器避免了優化分區,因此它不必擔心需要提升的情況。無論哪種方式,時間2.0**-1.01.0/2.0並沒有給出1.0/xx**-1.0的速度的準確反映。時間1.0/x而不是x**-1.0,並在設置步驟中爲x提供值。

對於「更準確」的聲明:1.0/x將解析爲單個機器指令,這幾乎肯定會給你正確舍入的結果。相比之下,pow的庫實現非常糟糕,並且經常會給出不正確舍入的結果。即使在最好的情況下,你的數學庫的pow操作正確舍入,它仍然不會比除法結果更準確。

作爲測試,我嘗試了下面的循環,用x ** -1.0比較1.0/x隨機x值:

>>> import random 
>>> while True: 
...  x = random.random() 
...  print(repr(x)) 
...  assert 1.0/x == x**-1.0 

果然,經過約200次迭代,我得到這個:

<around 200 lines of output omitted> 
0.16606458447273365 
0.6466363135038045 
0.8650060330740814 
Traceback (most recent call last): 
    File "<stdin>", line 4, in <module> 
AssertionError 

服用看看最後x的值,我得到:

>>> x = 0.8650060330740814 
>>> 1.0/x 
1.1560613010364489 
>>> x ** -1.0 
1.1560613010364487 

作爲一個粗略的檢查,它是1.0/x在這裏正確舍入的結果,我們可以將浮點數轉換爲Fraction(它進行確切轉換),將倒數作爲Fraction,然後轉換回floatFractionfloat轉換是正確舍入的,並且不使用浮點運算來計算結果,所以我們不依賴於此檢查中浮點除法的精度。

>>> from fractions import Fraction 
>>> float(1/Fraction(x)) 
1.1560613010364489 

(的x確切的倒數計算,以25個顯著數字,是1.156061301036448774438694。我們從1.0/x,計算出相同的精度後面的值,是1.156061301036448885071195,而從x ** -1.01.156061301036448663026590,所以1.0/x只是比x ** -1.0更接近真實值,但它更接近。)

最後,在我看來最重要的是,1.0/x更容易閱讀,並且需要較少的精力來分析。

總而言之,幾乎沒有理由選擇**表單。

+0

謝謝!我今天早上看到了一些代碼,其中指數形式,我認爲它看起來_strange_,但我沒有一個不使用它的實際原因。 – 2014-10-03 21:23:49

0

如果你正在考慮時間,它似乎是Python版本依賴(至少)。

的Python 2.7

import timeit 

>>> timeit.timeit('2.0**-1') 
0.017730650260812 

>>> timeit.timeit('1/2.0') 
0.07249812618659046 

的Python 3.4

import timeit 

>>> timeit.timeit('2.0**-1') 
0.03804316809533148 

>>> timeit.timeit('1/2.0') 
0.01787598969687565 
+0

我們不要忘記,這個部門更加清晰。 – SethMMorton 2014-10-03 19:12:42

+1

難道你不是說它快了一半?似乎更大的時間將意味着更慢;-) – mgilson 2014-10-03 19:14:41

+0

您的時間似乎與我得到的很不一樣 – 2014-10-03 19:16:21