js中如何取精度


js中如何取精度

一、總結

一句話總結:其實round()函數去經度會有誤差,直接用num.toFixed(2)簡單方便。

toFixed()方法會按照指定的小數返回數值的字符串表示。
var num = 10;
alert(num.toFixed(2));//10.00

 

1、Math.round(n * Math.pow(10, m)) / Math.pow(10, m) ;這種方法求精度的問題是什么?

因為后面涉及到了除法,所以會出現19.230000000001的情況,而且也不能保證總是m位的小數,還是用num.toFixed(2)好

 

2、js精度丟失的原因是什么?

計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖

意義

  • 1位用來表示符號位
  • 11位用來表示指數
  • 52位表示尾數

浮點數,比如

0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
0.2 >> 0.0011 0011 0011 0011…(0011無限循環)

此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。

大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。

 

3、js精度丟失的解決方案是什么?

對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。

對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)

 

 

 

 

二、關於JavaScript中.round()函數的運用

.round()函數是Math(算數)對象中的一種算數任務,主要用於解決數學中四舍五入的問題。


用法

Math.round(number)
number為數字類型,該函數執行后會返回number經過四舍五入處理后的返回值。

例如:

document.write(Math.round(3.64) )
document.write(Math.round(0.46) )
document.write(Math.round(-4.64) )

運行結果
4
0
-5

分析:
因為該round函數通過判斷number的小數點后的部分且以0.5為界限。

number為正數時。
如果 number 的小數部分大於等於 0.5,返回值是大於 number 的最小整數(即number的整數部分加一)。否則,round 返回小於等於 number 的最大整數(即number的整數部分)。

同理,當number為負數時
可以看做先忽略負號來處里,最后在結果返回值時補充回來。
如上面的-4.64.可以先忽略負號,經過round()計算完后得5,再在結果返回時補回來即-5

round()拓展

**【通過上述例子我們不難發現round()函數不能對小數點后面的數字進行精確的保留。】
**倘若我們需要對number后的小數點進行有效的保留,我們該怎么做。

例如將4.6545進行四舍五入,且保留小數點后面的三位小數
由於Math.round()函數並沒有提供保留小數點的功能,所以需要通過創建函數來進行改造

function Newround(n, m){//此處的n表示要四舍五入的數,m為要保留的小數位數
    Math.round(n * Math.pow(10, m)) / Math.pow(10, m) ;
}
//此處先將n乘以10的m次方,將要保留的小數位數的小數部分轉成整數部分,然后再用.round()進行四舍五入處理。最后再除以10的m次方還原小數部分

Newround(4.6545, 3); // 得到 4.655


//Math.pow()函數為冪函數
//用法
Math.pow(n,m)//返回的是n的m次方的值
//例子:
Math.pow(5,3)//返回值5*5*5即125

以上是博主對JavaScript中的.round()函數的理解,如有其它經驗,請各位大神補充,謝謝

 

參考:關於JavaScript中.round()函數的運用 - CSDN博客
https://blog.csdn.net/weixin_36059749/article/details/52459066

 

 

 

三、js精度問題

現象

 

原因

計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 規范,采用雙精度存儲(double precision),占用 64 bit。如圖

意義

  • 1位用來表示符號位
  • 11位用來表示指數
  • 52位表示尾數

浮點數,比如

0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
0.2 >> 0.0011 0011 0011 0011…(0011無限循環)

此時只能模仿十進制進行四舍五入了,但是二進制只有 0 和 1 兩個,於是變為 0 舍 1 入。這即是計算機中部分浮點數運算時出現誤差,丟失精度的根本原因。

大整數的精度丟失和浮點數本質上是一樣的,尾數位最大是 52 位,因此 JS 中能精准表示的最大整數是 Math.pow(2, 53),十進制即 9007199254740992。

大於 9007199254740992 的可能會丟失精度

9007199254740992     >> 10000000000000...000  // 共計 53 個 0
9007199254740992 + 1 >> 10000000000000...001  // 中間 52 個 0
9007199254740992 + 2 >> 10000000000000...010  // 中間 51 個 0

實際上

9007199254740992 + 1  // 丟失
9007199254740992 + 2  // 未丟失
9007199254740992 + 3  // 丟失
9007199254740992 + 4  // 未丟失

以上,可以知道看似有窮的數字, 在計算機的二進制表示里卻是無窮的,由於存儲位數限制因此存在“舍去”,精度丟失就發生了。

 

想了解更深入的分析可以看這篇論文(又長又臭):What Every Computer Scientist Should Know About Floating-Point Arithmetic

解決方案

對於整數,前端出現問題的幾率可能比較低,畢竟很少有業務需要需要用到超大整數,只要運算結果不超過 Math.pow(2, 53) 就不會丟失精度。

對於小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)

 // 0.1 + 0.2

(0.1*10 + 0.2*10) / 10 == 0.3 // true

以下是我寫了一個對象,對小數的加減乘除運算丟失精度做了屏蔽。當然轉換后的整數依然不能超過 9007199254740992。

/**
 * floatTool 包含加減乘除四個方法,能確保浮點數運算不丟失精度
 *
 * 我們知道計算機編程語言里浮點數計算會存在精度丟失問題(或稱舍入誤差),其根本原因是二進制和實現位數限制有些數無法有限表示
 * 以下是十進制小數對應的二進制表示
 *      0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
 *      0.2 >> 0.0011 0011 0011 0011…(0011無限循環)
 * 計算機里每種數據類型的存儲是一個有限寬度,比如 JavaScript 使用 64 位存儲數字類型,因此超出的會舍去。舍去的部分就是精度丟失的部分。
 *
 * ** method **
 *  add / subtract / multiply /divide
 *
 * ** explame **
 *  0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004)
 *  0.2 + 0.4 == 0.6000000000000001  (多了 0.0000000000001)
 *  19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002)
 *
 * floatObj.add(0.1, 0.2) >> 0.3
 * floatObj.multiply(19.9, 100) >> 1990
 *
 */
var floatTool = function() {
    
    /*
     * 判斷obj是否為一個整數
     */
    function isInteger(obj) {
        return Math.floor(obj) === obj
    }
    
    /*
     * 將一個浮點數轉成整數,返回整數和倍數。如 3.14 >> 314,倍數是 100
     * @param floatNum {number} 小數
     * @return {object}
     *   {times:100, num: 314}
     */
    function toInteger(floatNum) {
        var ret = {times: 1, num: 0}
        if (isInteger(floatNum)) {
            ret.num = floatNum
            return ret
        }
        var strfi  = floatNum + ''
        var dotPos = strfi.indexOf('.')
        var len    = strfi.substr(dotPos+1).length
        var times  = Math.pow(10, len)
        var intNum = parseInt(floatNum * times + 0.5, 10)
        ret.times  = times
        ret.num    = intNum
        return ret
    }
    
    /*
     * 核心方法,實現加減乘除運算,確保不丟失精度
     * 思路:把小數放大為整數(乘),進行算術運算,再縮小為小數(除)
     *
     * @param a {number} 運算數1
     * @param b {number} 運算數2
     * @param digits {number} 精度,保留的小數點數,比如 2, 即保留為兩位小數
     * @param op {string} 運算類型,有加減乘除(add/subtract/multiply/divide)
     *
     */
    function operation(a, b, op) {
        var o1 = toInteger(a)
        var o2 = toInteger(b)
        var n1 = o1.num
        var n2 = o2.num
        var t1 = o1.times
        var t2 = o2.times
        var max = t1 > t2 ? t1 : t2
        var result = null
        switch (op) {
            case 'add':
                if (t1 === t2) { // 兩個小數位數相同
                    result = n1 + n2
                } else if (t1 > t2) { // o1 小數位 大於 o2
                    result = n1 + n2 * (t1 / t2)
                } else { // o1 小數位 小於 o2
                    result = n1 * (t2 / t1) + n2
                }
                return result / max
            case 'subtract':
                if (t1 === t2) {
                    result = n1 - n2
                } else if (t1 > t2) {
                    result = n1 - n2 * (t1 / t2)
                } else {
                    result = n1 * (t2 / t1) - n2
                }
                return result / max
            case 'multiply':
                result = (n1 * n2) / (t1 * t2)
                return result
            case 'divide':
                return result = function() {
                    var r1 = n1 / n2
                    var r2 = t2 / t1
                    return operation(r1, r2, 'multiply')
                }()
        }
    }
    
    // 加減乘除的四個接口
    function add(a, b) {
        return operation(a, b, 'add')
    }
    function subtract(a, b) {
        return operation(a, b, 'subtract')
    }
    function multiply(a, b) {
        return operation(a, b, 'multiply')
    }
    function divide(a, b) {
        return operation(a, b, 'divide')
    }
    
    // exports
    return {
        add: add,
        subtract: subtract,
        multiply: multiply,
        divide: divide
    }
}();

// toFixed 修復
function toFixed(num, s) {
    var times = Math.pow(10, s)
    var des = num * times + 0.5
    des = parseInt(des, 10) / times
    return des + ''
}

 

相關:

http://0.30000000000000004.com

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

 
 
 
參考:js精度問題 - 晴天彩虹 - 博客園
https://www.cnblogs.com/panpanwelcome/p/7109718.html
 
 
 
 


免責聲明!

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



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