本章為大家介紹關於javascript中計算精確度的一些小知識(總結),讓大家了解十進制之間二進制是如何轉換的、javascript是如何保存數字的、 javascript是如何讀取數字的,最後實例講解javascript如何解決精度問題出現的計算錯誤問題。有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
一. 前置知識點
#1. 十進位如何轉換成二進位?
整數部分除二取餘數, 直到商數為0,逆序排列,小數部分乘2取整,順序排列,直到積中小數部分為0或到達要求精度。
8转为二进制是多少? 8 / 2 = 4...0 取0 4 / 2 = 2...0 取0 2 / 2 = 1...0 取0 1 / 2 = 0...1 取1 二进制结果为:1000 0.25转为二进制是多少? 0.25 * 2 = 0.50 取0 0.50 * 2 = 1.00 取1 二进制结果为:01 于是可得出8.25的二进制表示:1000.01
2. 二進位如何轉為十進位?
注意:二進位轉為十進位不分整數部分與小數部分。
二进制1000.01转为十进制 1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 0 * 2^0 + 0 * 2^-1 + 0 * 2^-2 = 8.25
二. javascript是如何保存數字的
#JavaScript 裡的數字是採用IEEE 754 標準的64 位元double 雙精度浮點數
sign bit(符號): 用來表示正負號,1位元(0表示正,1表示負)
exponent(指數): 用來表示次方數,11位元
mantissa(尾數): 用來表示精確度,52位元
對於沒有接觸的人來說,以上可能理解起來很模糊,沒關係,接下來我們用案例具體說明其流程,先看一下上述的十進制數8.25在JS中是如何保存的。
十進位的8.25會被轉換為二進位的1000.01;
二進位1000.01可用二進位的科學計數法1.00001 * 2^4表示;
00001 * 2^4的小數部分00001(二進位)就是mantissa(尾數)了,4(十進位)加上1023就是exponent(指數)了(這裡後面講解為什麼要加上1023);
接下來指數4要加上1023後轉為二進位10000000011;
我們的十進位8.25是一個正數,所以符號為二進位表示為0
8.25最終的二進位儲存0-10000000011-00001000000000000000000000000
三.javascript是如何讀取數字的
我們還是以8.25的二進位0-10000000011-0000000000000000000000000000000000000000000000000000000000000來講述首先我們取得指數部分的二進位1000000001,轉換為十進位為1027,1027減去1023就是我們實際的指數4了;
#四. 從0.1 0.2來看javascript精度問題
這裡就要進入我們的正題了,看懂了前面的原理說明,這部分將會變得很好理解了。
0.1儲存為64位元二進位浮點數沒有忘記以上步驟吧
先將0.1轉換為二進位的整數部分為0,小數部分為:0001100110011001100110011001100110011...咦,這裡居然進入了無限循環,那怎麼辦呢?暫時先不管;
我們得到的無限循環的二進制數用科學計數表示為1.100110011001100110011001100110011... * 2^-4;
同上,0.2存储为64位二进制浮点数:0-01111111100-1001100110011001100110011001100110011001100110011010
读取到两个浮点数的64为二进制后,再将其转化为可计算的二进制数
0.1转化为1.1001100110011001100110011001100110011001100110011010 * 2^(1019 - 1023)——0.00011001100110011001100110011001100110011001100110011010;
0.2转化为1.1001100110011001100110011001100110011001100110011010 * 2^(1020 - 1023)——0.0011001100110011001100110011001100110011001100110011010;
接着将两个浮点数的二进制数进行加法运算,得出0.0100110011001100110011001100110011001100110011001100111转化为十进制数即为0.30000000000000004
不难看出,精度缺失是在存储这一步就丢失了,后面的计算只是在不精准的值上进行的运算。
五. javascript如何解决精度问题出现的计算错误问题
对于小数或者整数的简单运算可如下解决:
function numAdd(num1, num2) { let baseNum, baseNum1, baseNum2; try { baseNum1 = String(num1).split(".")[1].length; } catch (e) { baseNum1 = 0; } try { baseNum2 = String(num2).split(".")[1].length; } catch (e) { baseNum2 = 0; } baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)); return (num1 * baseNum + num2 * baseNum) / baseNum; };
如:0.1 + 0.2 通过函数处理后,相当于 (0.1 * 10 + 0.2 * 10) / 10
但是如同我们前面所了解的,浮点数在存储的时候就已经丢失精度了,所以浮点数乘以一个基数仍然会存在精度缺失问题,比如2500.01 * 100 = 250001.00000000003, 所以我们可以在以上函数的结果之上使用toFixed(),保留需要的小数位数。
一些复杂的计算,可以引入一些库进行解决
以上是javascript關於計算精度的一些小知識(總結)的詳細內容。更多資訊請關注PHP中文網其他相關文章!