国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS魔法堂:徹底理解0.1 + 0.2 === 0.30000000000000004的背后

JerryWangSAP / 628人閱讀

摘要:也就是說不僅是會產生這種問題,只要是采用的浮點數編碼方式來表示浮點數時,則會產生這類問題。到這里我們都理解只要采取的浮點數編碼的語言均會出現上述問題,只是它們的標準類庫已經為我們提供了解決方案而已。

Brief

一天有個朋友問我“JS中計算0.7 * 180怎么會等于125.99999999998,坑也太多了吧!”那時我猜測是二進制表示數值時發生round-off error所導致,但并不清楚具體是如何導致,并且有什么方法去規避。于是用了3周時間靜下心把這個問題搞懂,在學習的過程中還發現不僅0.7 * 180==125.99999999998,還有以下的坑

著名的 0.1 + 0.2 === 0.30000000000000004

1000000000000000128 === 1000000000000000129

IEEE 754 Floating-point

眾所周知JS僅有Number這個數值類型,而Number采用的時IEEE 754 64位雙精度浮點數編碼。而浮點數表示方式具有以下特點:

浮點數可表示的值范圍比同等位數的整數表示方式的值范圍要大得多;

浮點數無法精確表示其值范圍內的所有數值,而有符號和無符號整數則是精確表示其值范圍內的每個數值;

浮點數只能精確表示m*2e的數值;

當biased-exponent為2e-1-1時,浮點數能精確表示該范圍內的各整數值;

當biased-exponent不為2e-1-1時,浮點數不能精確表示該范圍內的各整數值。

由于部分數值無法精確表示(存儲),于是在運算統計后偏差會愈見明顯。

想了解更多浮點數的知識可參考以下文章:

《基礎野:細說原碼、反碼和補碼》

《基礎野:細說無符號整數》

《基礎野:細說有符號整數》

《基礎野:細說浮點數》

Why 0.1 + 0.2 === 0.30000000000000004?

在浮點數運算中產生誤差值的示例中,最出名應該是0.1 + 0.2 === 0.30000000000000004了,到底有多有名?看看這個網站就知道了http://0.30000000000000004.com/。也就是說不僅是JavaScript會產生這種問題,只要是采用IEEE 754 Floating-point的浮點數編碼方式來表示浮點數時,則會產生這類問題。下面我們來分析整個運算過程。

0.1 的二進制表示為 1.1001100110011001100110011001100110011001100110011001 1(0011)+ * 2^-4;

當64bit的存儲空間無法存儲完整的無限循環小數,而IEEE 754 Floating-point采用round to nearest, tie to even的舍入模式,因此0.1實際存儲時的位模式是0-01111111011-1001100110011001100110011001100110011001100110011010;

0.2 的二進制表示為 1.1001100110011001100110011001100110011001100110011001 1(0011)+ * 2^-3;

當64bit的存儲空間無法存儲完整的無限循環小數,而IEEE 754 Floating-point采用round to nearest, tie to even的舍入模式,因此0.2實際存儲時的位模式是0-01111111100-1001100110011001100110011001100110011001100110011010;

實際存儲的位模式作為操作數進行浮點數加法,得到 0-01111111101-0011001100110011001100110011001100110011001100110100。轉換為十進制即為0.30000000000000004。

Why 0.7 * 180===125.99999999998?

0.7實際存儲時的位模式是0-01111111110-0110011001100110011001100110011001100110011001100110;

180實際存儲時的位模式是0-10000000110-0110100000000000000000000000000000000000000000000000;

實際存儲的位模式作為操作數進行浮點數乘法,得到0-10000000101-1111011111111111111111111111111111111111101010000001。轉換為十進制即為125.99999999998。

Why 1000000000000000128 === 1000000000000000129?

1000000000000000128實際存儲時的位模式是0-10000111010-1011110000010110110101100111010011101100100000000001;

1000000000000000129實際存儲時的位模式是0-10000111010-1011110000010110110101100111010011101100100000000001;

因此1000000000000000128和1000000000000000129的實際存儲的位模式是一樣的。

Solution

到這里我們都理解只要采取IEEE 754 FP的浮點數編碼的語言均會出現上述問題,只是它們的標準類庫已經為我們提供了解決方案而已。而JS呢?顯然沒有。壞處自然是掉坑了,而好處恰恰也是掉坑了:)

針對不同的應用需求,我們有不同的實現方式。

Solution 0x00 - Simple implementation

對于小數和小整數的簡單運算可用如下方式

function numAdd(num1/*:String*/, num2/*:String*/) { 
    var baseNum, baseNum1, baseNum2; 
    try { 
        baseNum1 = num1.split(".")[1].length; 
    } catch (e) { 
        baseNum1 = 0; 
    } 
    try { 
        baseNum2 = num2.split(".")[1].length; 
    } catch (e) { 
        baseNum2 = 0;
    } 
    baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)); 
    return (num1 * baseNum + num2 * baseNum) / baseNum; 
};
Solution 0x01 - math.js

若需要復雜且全面的運算功能那必須上math.js,其內部引用了decimal.js和fraction.js。功能異常強大,用于生產環境上妥妥的!

Solution 0x02 - D.js

D.js算是我的練手項目吧,截止本文發表時D.js版本為V0.2.0,僅實現了加、減、乘和整除運算而已,bug是一堆堆的,但至少解決了0.1+0.2的問題了。

var sum = D.add(0.1, 0.2)
console.log(sum + "") // 0.3

var product = D.mul("1e-2", "2e-4")
console.log(product + "") // 0.000002

var quotient = D.div(-3, 2)
console.log(quotient + "") // -(1+1/2)

解題思路:

由于僅位于Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER間的整數才能被精準地表示,也就是只要保證運算過程的操作數和結果均落在這個閥值內,那么運算結果就是精準無誤的;

問題的關鍵落在如何將小數和極大數轉換或拆分為Number.MIN_SAFE_INTEGER至Number.MAX_SAFE_INTEGER閥值間的數了;

小數轉換為整數,自然就是通過科學計數法表示,并通過右移小數點,減小冪的方式處理;(如0.000123 等價于 123 * 10-6)

而極大數則需要拆分,拆分的規則是多樣的。

按因式拆分:假設對12345進行拆分得到 5 * 2469;

按位拆分:假設以3個數值為一組對12345進行拆分得到345和12,而實際值為12*1000 + 345。
就我而言,1 的拆分規則結構不穩定,而且不直觀;而 2 的規則直觀,且拆分和恢復的公式固定。

余數由符號位、分子和分母組成,而符號與整數部分一致,因此只需考慮如何表示分子和分母即可。

無限循環數則僅需考慮如何表示循環數段即可。(如10.2343434則分成10.23 和循環數34和34的權重即可)

得到編碼規則后,那就剩下基于指定編碼如何實現各種運算的問題了。

基于上述的數值編碼規則如何實現加、減運算呢?

基于上述的數值編碼規則如何實現乘、除運算呢?(其實只要加、減運算解決了,乘除必然可解,就是效率問題而已)

基于上述的數值編碼規則如何實現其它如sin、tan、%等數學運算呢?

另外由于涉及數學運算,那么將作為add、sub、mul和div等入參的變量保持如同數學公式運算數般純凈(Persistent/Immutable Data Structure)是必須的,那是否還要引入immutable.js呢?(D.js現在采用按需生成副本的方式,可預見隨著代碼量的增加,這種方式會導致整體代碼無法維護)

Conclusion

依照我的尿性,D.js將采取不定期持續更新的策略(待我理解Persistent/Immutable Data Structure后吧:))。歡迎各位指教!

尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/5115672.html ^_^肥子John

Thanks

http://es5.github.io
https://github.com/MikeMcl/decimal.js/
http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html
http://demon.tw/copy-paste/javascript-precision.html

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79328.html

相關文章

  • 0.1 + 0.2 != 0.3背后原理

    摘要:標準是浮點數算術標準的標準編號,等同于國際標準。標準規定了計算機程序設計環境中的二進制和十進制的浮點數之間的交換算術格式以及方法。 初學JavaScript,在進行小數(浮點數)運算時,經常會碰到這樣的情況:0.1 + 0.2=0.30000000000000004,記得當時,教程告訴我們說,0.1 + 0.2在JavaScript運算中,它的值是不固定的,可以在后面學習和試驗中,漸漸...

    阿羅 評論0 收藏0
  • 如何解決0.1 +0.2===0.30000000000000004類問題

    摘要:方法使用定點表示法來格式化一個數,會對結果進行四舍五入。該數值在必要時進行四舍五入,另外在必要時會用來填充小數部分,以便小數部分有指定的位數。如果數值大于,該方法會簡單調用并返回一個指數記數法格式的字符串。在環境中,只能是之間,測試版本為。 showImg(https://segmentfault.com/img/remote/1460000011913134?w=768&h=521)...

    yuanzhanghu 評論0 收藏0
  • 深度剖析0.1 +0.2===0.30000000000000004原因

    摘要:吐槽一句,大二的專業課數字邏輯電路終于用在工作上了。,整數位為,且精度只到十分位,因此是。如果是不限精度的話,轉換后的二進制數應該是無限循環。再看一下百科給出的標準因此,的類型,最高的位是符號位,接著的位是指數,剩下的位為有效數字。 showImg(https://segmentfault.com/img/remote/1460000011902479?w=600&h=600); 用一...

    haobowd 評論0 收藏0
  • CSS魔法:你真理解z-index嗎?

    摘要:與的映射關系為。與根對應的對應的層疊上下文,是其他的祖先,的范圍覆蓋整條。注意的默認值為,自動賦值為。對于,它會將賦予給對應的,而則不會。 一、前言                               ?假如只是開發簡單的彈窗效果,懂得通過z-index來調整元素間的層疊關系就夠了。但要將多個彈窗間層疊關系給處理好,那么充分理解z-index背后的原理及兼容性問題就是必要的知識...

    andycall 評論0 收藏0
  • 探尋 JavaScript 精度問題以及解決方案

    摘要:推導為何等于在中所有數值都以標準的雙精度浮點數進行存儲的。先來了解下標準下的雙精度浮點數。精度位總共是,因為用科學計數法表示,所以首位固定的就沒有占用空間。驗證完成的最大安全數是如何來的根據雙精度浮點數的構成,精度位數是。 閱讀完本文可以了解到 0.1 + 0.2 為什么等于 0.30000000000000004 以及 JavaScript 中最大安全數是如何來的。 十進制小數轉為二...

    YanceyOfficial 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<