首頁 >web前端 >js教程 >javascript關於計算精度的一些小知識(總結)

javascript關於計算精度的一些小知識(總結)

青灯夜游
青灯夜游原創
2018-09-17 15:07:042607瀏覽

本章為大家介紹關於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位元

javascript關於計算精度的一些小知識(總結)

對於沒有接觸的人來說,以上可能理解起來很模糊,沒關係,接下來我們用案例具體說明其流程,先看一下上述的十進制數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

  • #不夠位的我們都用0補充;

    步驟2得出的科學計數中的整數本分1我們好像忘記,這裡因為Javascript為了更最大限度的提高精確度,而省略了這個1, 這樣在我們我們本來只能保存(二進位)52位的尾數,實際上是有(二進位)53位的;
  • 指數部分是11位,表示的範圍是[0 , 2047],由於科學計數中的指數可正可負,所以,中間數為1023,[0,1022] 表示為負,[1024,2047] 表示為正, 這也解釋了為什麼我們科學計數中的指數要加上1023來儲存了。
  • 三.javascript是如何讀取數字的

我們還是以8.25的二進位0-10000000011-0000000000000000000000000000000000000000000000000000000000000來講述首先我們取得指數部分的二進位1000000001,轉換為十進位為1027,1027減去1023就是我們實際的指數4了;

    取得尾數部分000010000000000000000000000000000000000000000000000我們的數為正數,得到二進制的科學數為1.00001 * 2^4,接著再轉為十進制數,就得到了我們的8.25;
  1. #四. 從0.1 0.2來看javascript精度問題

  2. 這裡就要進入我們的正題了,看懂了前面的原理說明,這部分將會變得很好理解了。

  3. 要計算0.1 0.2,先計算要先讀取到這兩個浮點數

0.1儲存為64位元二進位浮點數沒有忘記以上步驟吧

先將0.1轉換為二進位的整數部分為0,小數部分為:0001100110011001100110011001100110011...咦,這裡居然進入了無限循環,那怎麼辦呢?暫時先不管;

我們得到的無限循環的二進制數用科學計數表示為1.100110011001100110011001100110011... * 2^-4;

    指數位即是-4 1023 = 1019,轉化位11位二進制數01111111011;
  1. 尾數位是無限循環的,但是雙精度浮點數規定尾數位52位,於是超出52位的將被略去,保留1001100110011001100110011001100110011001100110011010
  2. #最後得出0.1的64位二進位浮點數10101010101010101 0110011001100110011001100110011010

同上,0.2存储为64位二进制浮点数:0-01111111100-1001100110011001100110011001100110011001100110011010

读取到两个浮点数的64为二进制后,再将其转化为可计算的二进制数

  1. 0.1转化为1.1001100110011001100110011001100110011001100110011010 * 2^(1019 - 1023)——0.00011001100110011001100110011001100110011001100110011010;

  2. 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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn