Python數值型別

駿馬金龍發表於2018-12-17

數值型別

python的數值型別包括常規的型別:整數(沒有小數部分的數字)、浮點數(通俗地說,就是有小數部分的數字)以及其它數值型別(複數、分數、有理數、無理數、集合、進位制數等)。除了十進位制整數,還有二進位制數、八進位制數、十六進位制數。

   型別           示例
---------------------------------------
   整數          1234, -24, 0
   浮點數        1.23, 1., .2, 3.14e-10
   八進位制        0o177, 0O177
   十六進位制      0x9ff, 0X9ff
   二進位制        0b1010, 0B1010

需要說明的幾點事項:

  • python 3.x中的整數不區分一般整數和長整型整數,3.x版本中的整數支援無窮精度
  • 任何時候浮點數都是不精確的。當帶有小數點或科學計數的標記符號e或E,就表示這是浮點數
    • 當浮點數參與表示式的運算時,會以浮點數的規則進行運算,也就是整數會轉換成浮點數型別
    • python中的浮點數精度和C語言的雙精度浮點數精度相同
  • 整數除了十進位制整數外,還可以寫成二進位制、八進位制、十六進位制甚至是其它進位制的整數,它們的轉換方式見後文
    • 當一個整數以0b0B開頭,其後都是0、1時,預設識別為二進位制整數
    • 當一個整數以0o0O開頭(數值零和大、小寫的字母o),其後都是0-7之間的數值時,預設識別為8進位制整數
    • 當一個整數以0x0X開始,其後都是[0-9a-fA-F]之間的字元時,預設識別為十六進位制

python中的數值型別是不可變物件,不可變意味著不可原處修改。假如a = 3333,那麼現在記憶體中會有一個記憶體塊儲存數值物件3333,如果修改它,比如對它加上1操作a += 1,python將建立一個新的記憶體塊用來儲存新的數值物件3334,而不是在3333那個記憶體塊中直接修改為3334,所以那個原始的數值3333就被丟棄了,它會等待垃圾回收器去回收。關於可變、不可變物件,後面的文章將會經常提到,請先留意這兩個詞。

數值基本運算

支援最基本的數學運算子號:+ - * / % **、取正負+x -x,地板除法//,除法和取模divmod(x, y)

>>> 123 + 345
468
>>> 345 - 123
222
>>> 1.5 * 4
6.0
>>> 2/5
0.4
>>> 2 % 3
2
>>> 3 ** 2
9
>>> 3.00 ** 2
9.0
>>> 3 ** 100
515377520732011331036461129765621272702107522001

>>> a = 3; b = -3
>>> -a, -b
(-3, 3)
>>> divmod(5, 2)
(2, 1)

可見,python的數值計算方式非常直接,且python 3.x中會自動為整數提供無窮精度。正如上面最後一個計算表示式(3**100),它將所有數字都顯示出來了。就算是計算3**100003**1000000,python也不會報錯,不過3的100萬次方,顯然需要花上一段時間來計算。這和其它程式語言有所區別,例如java中計算Math.pow(3,10000)將返回Infinity,表示無窮大。

又是幾個注意事項:

  • python中的除法運算/得到的結果總是浮點數(例如9/3=3.0),後面還有一種地板除法(floor)不一樣。
  • 當數值部分有小數時,會自動轉換為浮點數型別進行運算,而且會自動忽略參與運算的小數尾部的0
  • 加號+和乘號*也能處理字串:
    • +可以連線字串,例如"abc" + "def"得到abcdef
    • *可以重複字串次數,例如"a"*3得到"aaa""ab"*3得到"ababab"

其它數學運算方法

除了上面的基礎算術運算子,還支援很多數值型別的運算子,例如:取反(~)、位移(>>)、位與(&)、位異或(^)、邏輯與(and)、邏輯或(or)

除此之外,還有幾個python的內建數學函式:

pow():求冪,如pow(2,3)=8
abs():求絕對值,如abs(-3)=3
round():四捨五入,如round(3.5)=4
int():取整(截去小數部分),如int(3.5)=3
float():轉換成浮點數,如float(3)=3.0
oct():十進位制整數轉換成八進位制
hex():十進位制整數轉換成十六進位制整數
bin():十進位制整數轉換成二進位制
...等等...

還有專門的數學模組math、取隨機數的模組random等。

浮點數

由於硬體的原因,使得計算機對於浮點數的處理總是不精確的。

例如,按照數學運算時,1.1-0.9=0.2,但實際得到的結果為:

>>> 1.1-0.9
0.20000000000000007

它以高精度的極限趨近的值來顯示。上面的趨近結果大於按照數學運算結果,但並不總是如此,例如下面的運算則是小於數學運算的結果:

>>> 3.3-3.2
0.09999999999999964

由於浮點數不精確,所以儘量不要對兩個浮點數數進行等值==和不等值!=比較.如果非要比較,應該通過它們的減法求絕對值,再與一個足夠小(不會影響結果)的值做不等比較。

例如:

>>> (3.2-2.8) == 0.4
False

>>> abs((3.2-2.8)-0.4) < 0.0002
True

最後,浮點數並非總是輸出很長精度的值。正如前面的運算:

>>> 3.2+3.2
6.4
>>> 3/10
0.3

浮點數有兩個特殊方法,一個是is_integer(),用來測試這個浮點數是否是整數,另一個是as_integer_ratio(),可以將浮點數轉換成分子分母組成的元組,不過這個方法並非總是如你所想的那樣友好。例如:

>>> (3.0).is_integer()
True
>>> (3.2).is_integer()
False

>>> (2.5).as_integer_ratio()
(5, 2)
>>> (2.6).as_integer_ratio()
(5854679515581645, 2251799813685248)

浮點數總是不精確的,而且不能指定小數位數。但在python中,有一個專門的小數模組decimal,它可以提供精確的小數運算,還有一個分數模組fractions,也能提供精確的小數運算。

真除法、Floor除法和小數位截斷

  • /:實現的是真除法。在python中,它總是返回浮點數值
  • //:實現的是floor地板除法,它會去掉除法運算後的小數位,以便得到小於運算結果的最大整數。如果參與運算的有小數,則返回浮點數,否則返回整數
  • 在math模組中,有地板函式math.floor()和天花板函式math.ceil()。它們的意義可以根據現實中地板、空氣、天花板的高低位置來考慮。地板位於空氣之下,地板運算的返回值是比空氣小的最大整數,天花板位於空氣之上,天花板運算的的返回值是比空氣大的最小整數
  • round(x, N)是四捨五入,可以指定四捨五入到哪個小數位
  • math.trunc()是直接截斷小數
  • 實際上int()函式自身就是位元組截斷小數的

看下面的示例。

真除法總是返回浮點數。

>>> 9/3
3.0
>>> 10/4
2.5
>>> 10/4.0
2.5
>>> -9/2
-4.5
>>> -9/2.0
-4.5

floor除法返回浮點數還是整數取決於參與運算的數是否包含浮點數。

>>> 9 // 3
3
>>> 10 // 4
2
>>> 10 // 4.0
2.0

對於正數的運算結果,floor除法是直接去除小數位的。對於負數結果,它是取比運算結果更小的負整數。。

例如,負數結果的floor除法:

>>> -9 // 3
-3
>>> -10 // 4
-3
>>> -10 // 3
-4

-10 / 4的結果是-2.5,floor要取比它小的最大整數,也就是-3。-10 / 3的結果是-3.3,floor要取比它小的最大整數,也就是-4。

除了真除法和floor除法,還有四捨五入round()和math.trunc()兩種截斷小數的方式。例如:

>>> round(10/4)
2
>>> round(-5.2/2)
-3

>>> import math      # import表示匯入某個模組
>>> math.trunc(5/2)
2
>>> math.trunc(-5.2/2)
-2

int()也可以直接截斷小數。

>>> int(3.6)
3
>>> int(-3.6)
-3

數值型別的轉換

  • int()可以將字串或浮點數轉換成整數,也可以用於進位制數轉換
  • float()可以將字串或整數轉換成浮點數

實際上它們表示根據給定引數在記憶體中構造一個整數、浮點數物件,所以可以用來作為型別轉換工具。而且,前面已經說過,int()可以用來截斷小數位。

>>> int(3.5)   # 浮點數 -> 整數
3
>>> int(-3.6)  # 浮點數 -> 整數
-3
>>> int(`3`)   # 字串 -> 整數
3

>>> float(3)   # 整數 -> 浮點數
3.0
>>> float(`3`) # 字串 -> 浮點數
3.0

int()還可用於進位制數轉換,見下文。

小數型別(Decimal)

小數模組decimal,它有一個函式Decimal(),它是精確的,是可以指定小數位數的。

如果沒有python基礎,這裡能看懂多少算多少,反正小數用的也不多。

例如,使用浮點數計算

>>> 0.1 * 3 - 0.3
5.551115123125783e-17

它本該等於0,但結果卻是無限接近於0,因為計算機硬體用於儲存數值位數的空間有限。

使用decimal模組的Decimal()可以構造精確的小數。例如:

>>> import decimal
>>> decimal.Decimal(`0.1`) * 3 - decimal.Decimal(`0.3`)
Decimal(`0.0`)

注意,Decimal()的引數都是字串,如果不加引號,它還是會解釋成浮點數。

>>> decimal.Decimal(0.1)
Decimal(`0.1000000000000000055511151231257827021181583404541015625`)

Decimal()的運算的結果會取最長的小數位數。

>>> decimal.Decimal(`0.1`) * 3 - decimal.Decimal(`0.300`)
Decimal(`0.000`)

可以設定decimal的精度,也就是小數位數。有兩種範圍的精度:全域性範圍、區域性範圍。

例如,沒有設定精度時,會保留很多位數的小數。

>>> import decimal
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal(`0.1428571428571428571428571429`)

設定全域性範圍的精度為4,即保留4位小數:

>>> import decimal
>>> decimal.getcontext().prec = 4
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal(`0.1429`)

全域性範圍的精度表示整個執行緒執行時,這個模組的精度都是4。

還可以設定區域性範圍的精度,區域性表示退出了這個範圍就失效了。使用with/as語句可以設定區域性精度,所以退出with/as語句塊精度的設定就失效了。

>>> with decimal.localcontext() as ctx:
...     ctx.prec = 2
...     decimal.Decimal(1) / decimal.Decimal(7)
...
Decimal(`0.14`)

>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal(`0.1429`)     # 因為前面設定了全域性精度為4

分數(Fraction)

分數模組fractions,它有一個函式Fraction(),它可以構建分數。有了分數之後,可以參與運算。分數和浮點數不同,分數是精確的。

同樣地,如果沒有python基礎,這裡能看懂多少算多少,反正用的也不多。

例如,構建分數三分之一。

>>> import fractions
>>> fractions.Fraction(1,3)
Fraction(1, 3)

還可以根據浮點數的字串格式構建分數。

>>> fractions.Fraction(`0.3`)
Fraction(3, 10)

然後可以用分數進行運算。

分數加整數:

>>> fractions.Fraction(1,3) + 1
Fraction(4, 3)

分數加、減、乘、除分數:

>>> fractions.Fraction(1,3) + fractions.Fraction(2,3)
Fraction(1, 1)
>>> fractions.Fraction(1,3) - fractions.Fraction(2,3)
Fraction(-1, 3)
>>> fractions.Fraction(1,3) * fractions.Fraction(2,3)
Fraction(2, 9)
>>> fractions.Fraction(1,3) / fractions.Fraction(2,3)
Fraction(1, 2)

實際上,float物件有一個as_integer_ratio()函式,可以將浮點數轉換成整數的元組表示形式(元組後面的文章會介紹),然後根據這個元組就可以構造出分數來。

例如,將2.5轉換成元組,並進而轉換成分數。

>>> (2.5).as_integer_ratio()
(5, 2)      # 得到元組

>>> fractions.Fraction(*(2.5).as_integer_ratio())
Fraction(5, 2)

進位制整數的轉換

  • oct():十進位制整數轉換成八進位制
  • hex():十進位制整數轉換成十六進位制整數
  • bin():十進位制整數轉換成二進位制

例如,將十進位制的64轉換成二進位制、八進位制、十六進位制整數。

>>> bin(64),oct(64),hex(64)
(`0b1000000`, `0o100`, `0x40`)

int()函式也能進行進位制轉換,它的用法格式為:

int(x, base=10)

base指定要將x解釋成哪個進位制位的數,然後轉換成十進位制數,也就是前面說的構造一個整數物件。不指定base時,預設解釋成10進位制。

base的值可以是0或2-36之間的任意一個數,base=0也表示解釋成10進位制。

例如,將二進位制的數轉換為十進位制整數。

>>> int(`0b11`,base=2)
3
>>> int(`11`,base=2)
3

既然x要解釋成某個進位制的數,那麼超出這個進位制的數自然不能出現。例如:

  • 將x解釋成二進位制數的時候,x裡就不能包含除0、1之外的數(當然,字首0b除外);
  • 解釋成7進位制,就不能出現7、8、9;
  • 解釋成8進位制,就不能出現8、9;
  • 解釋成11進位制,就只能出現0-9、a/A這些字元;
  • 12進位制就只能出現0-9、aAbB這幾個字元;
  • 36進位制就只能出現0-9、a-zA-Z這幾個字元。

例如,將一個字串解釋為15進位制,並轉換成整數。15進位制只能出現0-9、a-eA-E這幾個字元。

>>> int(`93E`, base=15)
2084

相關文章