為什么說(shuō)浮點(diǎn)數(shù)缺乏精確性?
在開始本文之前,讓我們先來(lái)談?wù)劯↑c(diǎn)數(shù)為什么缺乏精確性的問(wèn)題,其實(shí)這不是Python的問(wèn)題,而是實(shí)數(shù)的無(wú)限精度跟計(jì)算機(jī)的有限內(nèi)存之間的矛盾。
舉個(gè)例子,假如說(shuō)我只能使用整數(shù)(即只精確到個(gè)位,計(jì)算機(jī)內(nèi)的浮點(diǎn)數(shù)也只有有限精度,以C語(yǔ)言中的雙精度浮點(diǎn)數(shù)double為例,精度為52個(gè)二進(jìn)制位),要表示任意實(shí)數(shù)(無(wú)限精度)的時(shí)候我就只能通過(guò)舍入(rounding)來(lái)近似表示。
比如1.2我會(huì)表示成1,2.4表示成2,3.6表示成4.
所以呢?
在算1.2 - 1.2的時(shí)候,由于計(jì)算機(jī)表示的問(wèn)題,我算的實(shí)際上是1 - 1,結(jié)果是0,碰巧蒙對(duì)了;
在算1.2 + 1.2 - 2.4的時(shí)候,由于計(jì)算機(jī)表示的問(wèn)題,我算的實(shí)際上是1 + 1 - 2,結(jié)果是0,再次蒙對(duì)了;
但是在算1.2 + 1.2 + 1.2 - 3.6的時(shí)候,由于計(jì)算機(jī)表示的問(wèn)題,我算的實(shí)際上是1 + 1 + 1 - 4,結(jié)果是-1,運(yùn)氣沒那么好啦!
這里的1.2, 2.4, 3.6就相當(dāng)于你問(wèn)題里的0.1, 0.2和0.3,1, 2, 4則是真正在計(jì)算機(jī)內(nèi)部進(jìn)行運(yùn)算的數(shù)值,我說(shuō)清楚了嗎?
其他請(qǐng)看IEEE 754浮點(diǎn)數(shù)標(biāo)準(zhǔn),比如CSAPP第二章啥的(雖然估計(jì)你沒興趣看)。
另:不僅僅是浮點(diǎn)數(shù)的在計(jì)算機(jī)內(nèi)部的表示有誤差,運(yùn)算本身也可能會(huì)有誤差。比如整數(shù)2可以在計(jì)算機(jī)內(nèi)準(zhǔn)確表示,但是要算根號(hào)2就有誤差了;再比如兩個(gè)浮點(diǎn)數(shù)相除,本來(lái)兩個(gè)數(shù)都是精確表示的,但除的結(jié)果精度卻超出了計(jì)算機(jī)內(nèi)實(shí)數(shù)的表示范圍,然后就有誤差了。
好了,下面話不多說(shuō)了,開始本文的正文:
起步
浮點(diǎn)數(shù)的一個(gè)普遍的問(wèn)題是它們不能精確的表示十進(jìn)制數(shù)。
>>> a = 4.2>>> b = 2.1>>> a + b6.300000000000001>>> (a + b) == 6.3False>>>
這是由于底層 CPU 和IEEE 754 標(biāo)準(zhǔn)通過(guò)自己的浮點(diǎn)單位去執(zhí)行算術(shù)時(shí)的特征。看似有窮的小數(shù), 在計(jì)算機(jī)的二進(jìn)制表示里卻是無(wú)窮的。
一般情況下,這一點(diǎn)點(diǎn)的小誤差是允許存在的。如果不能容忍這種誤差(比如金融領(lǐng)域),那么就要考慮用一些途徑來(lái)解決這個(gè)問(wèn)題了。
Decimal
使用這個(gè)模塊不會(huì)出現(xiàn)任何小誤差。
>>> from decimal import Decimal>>> a = Decimal('4.2')>>> b = Decimal('2.1')>>> a + bDecimal('6.3')>>> print(a + b)6.3>>> (a + b) == Decimal('6.3')True盡管代碼看起來(lái)比較奇怪,使用字符串來(lái)表示數(shù)字,但是 Decimal 支持所有常用的數(shù)學(xué)運(yùn)算。 decimal 模塊允許你控制計(jì)算的每一方面,包括數(shù)字位數(shù)和四舍五入。在這樣做之前,需要?jiǎng)?chuàng)建一個(gè)臨時(shí)上下文環(huán)境來(lái)改變這種設(shè)定:
>>> from decimal import Decimal, localcontext>>> a = Decimal('1.3')>>> b = Decimal('1.7')>>> print(a / b)0.7647058823529411764705882353>>> with localcontext() as ctx:... ctx.prec = 3... print(a / b)...0.765>>> with localcontext() as ctx:... ctx.prec = 50... print(a / b)...0.76470588235294117647058823529411764705882352941176>>>由于 Decimal 的高精度數(shù)字自然也就用字符串來(lái)做展示和中轉(zhuǎn)。
總結(jié)
總的來(lái)說(shuō),當(dāng)涉及金融領(lǐng)域時(shí),哪怕是一點(diǎn)小小的誤差在計(jì)算過(guò)程中都是不允許的。因此 decimal 模塊為解決這類問(wèn)題提供了方法。
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。
新聞熱點(diǎn)
疑難解答
圖片精選