前言
經(jīng)常使用JavaScript用來處理數(shù)字的程序員都知道,JavaScript的Number.toFixed
,這一函數(shù),在格式化數(shù)字時(shí),會(huì)自動(dòng)進(jìn)行四舍五入,例如:
10.125.toFixed(2) ---> "10.13"
但是在某些情況下,其四舍五入的結(jié)果,往往都不盡人意,例如:
10.145.toFixed(2) ---> "10.14"
那為何為出現(xiàn)上述這種情況,需要從進(jìn)制談起。
進(jìn)制之謎
眾所周知,計(jì)算機(jī)在設(shè)計(jì)之初,出于各方面角度考慮,最終采用二進(jìn)制的格式來存儲(chǔ)數(shù)據(jù)。而我們平時(shí)對(duì)于數(shù)字所慣用的進(jìn)制是十進(jìn)制。用二進(jìn)制的格式來存儲(chǔ)十進(jìn)制的數(shù)據(jù),必然會(huì)面臨進(jìn)制的轉(zhuǎn)換,進(jìn)制的轉(zhuǎn)換就會(huì)面臨精度的丟失。
例如,對(duì)于?,對(duì)于三進(jìn)制的數(shù)來說是0.1,而對(duì)于十進(jìn)制來說,是0.333(3循環(huán))。那對(duì)于三個(gè)?相加,對(duì)于三進(jìn)制來說,就是一(逢三進(jìn)一),而對(duì)于十進(jìn)制來說,就是0.999(9循環(huán))。
同樣的情況,也會(huì)出現(xiàn)在十進(jìn)制和二進(jìn)制的轉(zhuǎn)換中。當(dāng)我們?cè)谟?jì)算機(jī)中,聲明一個(gè)變量為10.145,其實(shí)該數(shù)字作為二進(jìn)制保存在計(jì)算機(jī)中,并不真的是10.145??梢酝ㄟ^Number.prototype.toPrecision
方法來一探究竟。
Number.prototype.toPrecision
方法以指定的精度返回該數(shù)值對(duì)象的字符串表示。
10.145.toPrecision(21) ---> "10.1449999999999995737"
因此就可以解釋,為什么toFixed()
方法返回的四舍五入的值,在某些情況不符合預(yù)料。
同樣,也告訴大家,浮點(diǎn)數(shù)的所有計(jì)算,加減乘除無一例外,在某些情況下,都會(huì)出現(xiàn)不符合預(yù)料的情況,最常見的如0.1+0.2
,等等。
解決方案
針對(duì)上述情況,我寫了一個(gè)簡單的庫,可以滿足數(shù)字的四舍五入需求。round-js
探索浮點(diǎn)數(shù)標(biāo)準(zhǔn)
和大部分語言不同,JavaScript目前對(duì)于數(shù)字的表示,只有一種類型,即Number,采用64位雙精度浮點(diǎn)數(shù)來表示一個(gè)數(shù),其標(biāo)準(zhǔn)與大多數(shù)語言一樣,采用IEEE-754標(biāo)準(zhǔn)。IEEE-754下64位雙精度浮點(diǎn)數(shù)的標(biāo)準(zhǔn),可以用一張圖來表示,如下:
- sign:代表符號(hào),用1位來表示,即正數(shù)負(fù)數(shù),1代表正數(shù),0代表負(fù)數(shù);
- exponent:代表指數(shù),底數(shù)為2,用11位來表述;
- fraction:真正的有效數(shù)字,用52位來表示。
最終,任何一個(gè)數(shù),對(duì)于該標(biāo)準(zhǔn),都使用如下公式來表示得出:
其中:
- V:結(jié)果
- S:上面的sign
- M:有效數(shù)字fraction
- E:上面的exponent
可以看出,這個(gè)表示方法,類似十進(jìn)制中的科學(xué)計(jì)數(shù)法。
對(duì)于這些概念,IEEE-754標(biāo)準(zhǔn)中還有許多規(guī)定,例如<1M<2
,等等,更多細(xì)節(jié)就不展開了。
完。