js為什么不能正確處理小數(shù)運(yùn)算?
var sum = 0;
for(var i = 0; i < 10; i++) {
sum += 0.1;
}
console.log(sum);
上面的程序會(huì)輸出1嗎?
在 你有必要知道的 25 個(gè) JavaScript 面試題 一文中,第 8 個(gè)題淺顯的說(shuō)了下 js 為什么不能正確處理小數(shù)運(yùn)算的問(wèn)題。今天重拾舊題,更深層次的剖析下這個(gè)問(wèn)題。
但要先說(shuō)明的是,不能正確處理小數(shù)的運(yùn)算并不是 JavaScript 語(yǔ)言本身的設(shè)計(jì)錯(cuò)誤,其它高級(jí)編程語(yǔ)言,如C,Java等,也是不能正確處理小數(shù)運(yùn)算的:
#include <stdio.h>
void main(){
float sum;
int i;
sum = 0;
for(i = 0; i < 100; i++) {
sum += 0.1;
}
printf('%f\n', sum); //10.000002
}
數(shù)在計(jì)算機(jī)內(nèi)部的表示
我們都知道,用高級(jí)編程語(yǔ)言編寫的程序需要經(jīng)過(guò)解釋、編譯等操作轉(zhuǎn)變成 CPU(Central Processing Unit) 可以識(shí)別的機(jī)器語(yǔ)言才能運(yùn)行,而對(duì) CPU 來(lái)說(shuō),它不識(shí)別數(shù)的十進(jìn)制、八進(jìn)制和十六進(jìn)制等,我們?cè)诔绦蛑新暶鞯倪@些進(jìn)制數(shù)都會(huì)被轉(zhuǎn)成二進(jìn)制數(shù)進(jìn)行運(yùn)算。
為什么不是轉(zhuǎn)換成三進(jìn)制數(shù)進(jìn)行運(yùn)算呢?
計(jì)算機(jī)內(nèi)部是由很多的 IC (Integrated Circuit: 集成電路) 這種電子部件構(gòu)成的,它的長(zhǎng)相大概是這樣子:

IC 有很多種形狀,在其兩側(cè)或內(nèi)部并排排列著很多引腳(圖示只畫(huà)出了一側(cè))。IC 的所有引腳,只有直流電壓 0V 或 5V 兩個(gè)狀態(tài),即一個(gè) IC 引腳只能表示兩個(gè)狀態(tài)。IC 的這個(gè)特性就決定了計(jì)算機(jī)內(nèi)部的數(shù)據(jù)只能用二進(jìn)制數(shù)處理。
由于1 位(一個(gè)引腳)只能表示兩個(gè)狀態(tài),所以二進(jìn)制的計(jì)算方式就變成了 0、1、10、11、100….這種形式:

所以,在數(shù)的運(yùn)算中,所有操作數(shù)都會(huì)被轉(zhuǎn)成二進(jìn)制數(shù)參與運(yùn)算,如39,會(huì)被轉(zhuǎn)換成二進(jìn)制 00100111
小數(shù)的二進(jìn)制表示
如前文所說(shuō),程序中的數(shù)據(jù)都會(huì)被轉(zhuǎn)換成二進(jìn)制數(shù),小數(shù)參與運(yùn)算時(shí),也會(huì)被轉(zhuǎn)成二進(jìn)制,如十進(jìn)制的11.1875 會(huì)被轉(zhuǎn)換成1101.0010。
小數(shù)點(diǎn)后 4 位用二進(jìn)制數(shù)表示的數(shù)值范圍是 0.0000~0.1111,因此,這只能表示 0.5、0.25、0.125、0.0625 這四個(gè)十進(jìn)制數(shù)以及小數(shù)點(diǎn)后面的位權(quán)組合(相加)而成的小數(shù):

從上表可以看出,十進(jìn)制數(shù) 0 的下一位是 0.0625,所以,0~0.0625 之間的小數(shù),就無(wú)法用小數(shù)點(diǎn)后 4 位數(shù)的二進(jìn)制數(shù)表示;如果增加二進(jìn)制數(shù)小數(shù)點(diǎn)后面的位數(shù),與其相對(duì)應(yīng)的十進(jìn)制數(shù)的個(gè)數(shù)也會(huì)增加,但無(wú)論增加多少位,都無(wú)法得到 0.1 這個(gè)結(jié)果。實(shí)際上,0.1 轉(zhuǎn)換成二進(jìn)制是 0.00110011001100110011…… 注意 0011 是無(wú)限重復(fù)的:
console.log(0.2+0.1); //操作數(shù)的二進(jìn)制表示 0.1 => 0.0001 1001 1001 1001…(無(wú)限循環(huán)) 0.2 => 0.0011 0011 0011 0011…(無(wú)限循環(huán))
js 的 Number 類型并沒(méi)有像 C / Java 等分整型、單精度、雙精度等,而是統(tǒng)一表現(xiàn)為雙精度浮點(diǎn)型。按照 IEEE 的規(guī)定,單精度浮點(diǎn)數(shù)用 32 位表示全體小數(shù),而雙精度浮點(diǎn)數(shù)用 64 位表示全體小數(shù),而浮點(diǎn)數(shù)由符號(hào)、尾數(shù)、指數(shù)和基數(shù)組成,所以并不是所有的位數(shù)都用來(lái)表示小數(shù),符號(hào)、指數(shù)等也要占據(jù)位數(shù),基數(shù)不占據(jù)位數(shù):

雙精度浮點(diǎn)數(shù)的小數(shù)部分最多支持 52 位,所以兩者相加之后得到這么一串 0.0100110011001100110011001100110011001100…因浮點(diǎn)數(shù)小數(shù)位的限制而截?cái)嗟亩M(jìn)制數(shù)字,這時(shí)候,再把它轉(zhuǎn)換為十進(jìn)制,就成了 0.30000000000000004。
總結(jié)
js 不能正確處理小數(shù)運(yùn)算,包括其它高級(jí)編程語(yǔ)言一樣,這不是語(yǔ)言本身的設(shè)計(jì)錯(cuò)誤,而是計(jì)算機(jī)內(nèi)部本身就不能正確處理小數(shù)的運(yùn)算,對(duì)小數(shù)的運(yùn)算往往會(huì)得到意想不到的結(jié)果,因?yàn)椴⒉皇撬械氖M(jìn)制小數(shù)能被二進(jìn)制表示。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
- JavaScript 正則表達(dá)式 驗(yàn)證整數(shù)、小數(shù)、實(shí)數(shù)、有效位小數(shù)最簡(jiǎn)單
- js 小數(shù)取整的函數(shù)
- JS保留小數(shù)點(diǎn)(四舍五入、四舍六入)實(shí)現(xiàn)思路及實(shí)例
- JS格式化數(shù)字金額用逗號(hào)隔開(kāi)保留兩位小數(shù)
- js取float型小數(shù)點(diǎn)后兩位數(shù)的方法
- js中小數(shù)轉(zhuǎn)換整數(shù)的方法
- js數(shù)字轉(zhuǎn)換為float,取N位小數(shù)
- Javascript浮點(diǎn)數(shù)乘積運(yùn)算出現(xiàn)多位小數(shù)的解決方法
- js小數(shù)運(yùn)算出現(xiàn)多位小數(shù)如何解決
相關(guān)文章
javascript 回到頂部效果的實(shí)現(xiàn)代碼
本篇文章主要是對(duì)javascript 回到頂部效果的實(shí)現(xiàn)代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-02-02
JavaScript實(shí)現(xiàn)公歷轉(zhuǎn)農(nóng)歷功能示例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)公歷轉(zhuǎn)農(nóng)歷功能,涉及javascript日期與時(shí)間相關(guān)操作及運(yùn)算操作技巧,需要的朋友可以參考下2017-02-02
Ajax異步文件上傳與NodeJS express服務(wù)端處理
本文主要介紹了Ajax異步文件上傳與NodeJS express服務(wù)端處理的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
JavaScript為內(nèi)置對(duì)象添加原型方法實(shí)現(xiàn)
這篇文章主要介紹了JavaScript為內(nèi)置對(duì)象添加原型方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
JavaScript表單通過(guò)正則表達(dá)式驗(yàn)證電話號(hào)碼
通過(guò)正則表達(dá)式實(shí)現(xiàn)判斷一個(gè)輸入量是否為電話號(hào)碼,需要的朋友可以參考下2014-03-03

