js 小數運算出現誤差的原因


問題

在javascript使用小數點 +-*/ 運算會出現誤差,比如:

0.1 + 0.2 == 0.30000000000000004
0.2 + 0.4 == 0.6000000000000001
19.9 * 100 == 1989.9999999999998

其實,不僅僅只有javascript,還有java、python等都會有類似問題,因為浮點數IEEE754是被普遍使用的標准

 


 

浮點數

浮點數是相對於定點數來說的。

計算機中小數的表示法,有定點和浮點兩種。

定點,即小數點固定,比如:302876512411.25 小數點固定在數字的個位數右邊
浮點,即小數點不固定(浮動),3.028e+11 小數點不固定在個位數和小數之間,而是根據指數值進行前后浮動

可以這么理解,科學記數法就是浮點數的表示方式

那么,我們為什么要使用浮點數呢?

我們可以先考慮下,為什么要使用科學記數法?

302876512411.25
3.028e+11

科學記數法的核心就是:通過移動小數點,只在小數點前保留一位數字,其他都算作小數,並使用指數記錄小數點移動的位數

好處是,通過指數就可以很直觀的看出數值的大小(而不用個十百千萬的數)

其實它還有另一個好處,如果省略小數點后幾位的話,它會顯得很廋,不像原值那么臃腫(缺點是精度丟失)

 


 

計算機中的浮點數

因為計算機中,數值的存儲是有大小限制的,比如

單精度浮點數 float -- 4Byte -- 32bit
雙精度浮點數 double -- 8Byte -- 64bit

問題: 如何在有限的存儲空間內容,盡可能的表示更多的數值?

使用定點數(原值),雖然保留了精度,但是能夠表示的數值范圍有限
使用浮點數(科學記數法),能夠表示的數值范圍變廣了,但是精度也丟失了

也就是說,相同位數下

范圍和精度是不可兼得

兩害相權取其輕,微小的精度 沒有 數值表示范圍 顯得更重要

所以計算機中表示小數的方式就是使用了浮點數,也就有了 IEEE754標准

 


 

二進制的定點和浮點

定點數: 以32位存儲為例 (124 + 1*2e-2)

img
圖片來源: https://www.zhihu.com/question/19848808

浮點數:(1.01)2 = 1.25 即 1.25 * 2e-3

img
圖片來源: https://www.zhihu.com/question/19848808

階碼=階碼真值+127。 (127是單精度浮點的偏移量,即 0111 1111)

 


 

IEEE 754

浮點數的存儲格式,一般按照標准IEEE 754

IEEE 754 規定,浮點數的表示方法為:

img

最高的 1 位是符號位 s,(表示正負)

接着的 8 位是階碼真值E,(補碼,計算真值需要加上偏移量)

剩下的 23 位為尾數 M。(原碼)

float: 1 + 8 + 23 = 32

double: 1 + 11 + 52 = 64

例如:0.125 DEC = 1/8 = 0.001 BIN = 1 x 2^-3

= 0 + 0111 1100 + 0000 0000 0000 0000 0000 000 = 00111110000000000000000000000000

IEEE754換算工具: http://www.binaryconvert.com/convert_double.html

 


 

浮點數加減乘除運算出現誤差的原因

0.125 = 1/8

0.0625 = 1/16

0.03125 = 1/32

0.015625 = 1/64

0.0078125 = 1/128

0.00390625 = 1/256

0.1 = 0/2 + 0/4 + 0/8 + 1/16 + 1/32 + 0/64 + 0/128 + 1/256 + 1/512 + ...

0.1 = 0.0001 1001 1001 ...

 

0.2 = 0/2 + 0/4 + 1/8 + 1/16 + 0/32 + 0/64 + 1/128 + 1/256 + 0/512 + ...

0.1 = 0.0011 0011 0011 ...

原因: 一個能准確表示的十進制小數,而二進制卻是循環小數

解決方法

避免使用會清除小數的換算方式

Math.floor
Math.ceil

 

對於整數,一般不會出錯
對於小數,出錯的概率較高,可以先變為整數,再縮為小數 (0.1*10 + 0.2*10) / 10 == 0.3
小數的加減乘除運算可以封裝方法: https://blog.51cto.com/xzllff/831241


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM