前端二进制

137 阅读6分钟

计算机中的数值表示

进位计数值:基数R+位权w

基数R

  • R代表基本数码的个数,二进制就是0和1这2个数。
  • R进制的主要特点是逢R进1.
  • 如2进制,逢2进1,十进制,逢10进1

位权Wi

  • 位权是指第i位上的数码的权重值,位权与数码所处的位置i有关。
  • 例如R=10(十进制数),各个数码的权位10^i,i表示数码所处的位置。
  • 十进制 11 = 1*10^1 + 1*10^0 = 11; 
    二进制 11 = 1 * 2 ^1 + 1 * 2 ^0 = 3
    

进制转换

二进制转10进制

  • 按权展开,加权求和,如2进制111.11转为10进制
111.11     = 1 * 2 ^ 2 + 1 * 2 ^1 + 1 * 2 ^ 0 + 1 * 2 ^(-1) + 1 * 2 ^(-2)
		   =  4  + 2 + 1 + 0.5 + 0.25 = 7.75

十进制转2进制

  • 整数部分:除2取余,直到商为0,最先得到的余数是最低为,最后得到的余数是最高位。
  • 小数部分: 乘2取整, 直到积数为0或者达到精度要求为止,最先得到的整数是高位。
十进制 7.75
整数7,小数0.75;
7/2 = 3....1
3/2 = 1....1
1/2 = 0 ....1
整数部分的二进制就是111
小数:
0.75 * 2 =  1.5  => 1
0.5 * 2 =  1  => 1
小数部分就是11
所以7.75转为二进制就是111.11

计算机的数据

计算中的数据分为数值数据和非数值数据,数值数据又分为无符号数据和有符号数据。

数值数据

bit!!

  • bit比特是表示信息的最小单位

  • 比特是二进制单位的缩写

  • 比特只有两种状态 0和1

  • 一般来说n比特的信息量可以表示出2的n次方种选择

    // 一般来说n比特的信息量可以表示出2的n次方种选择
    let a = 0b1000  // 8
    // 3个bit,2^3 = 8种选择  0~7
    console.log(a);
    

无符号数据!!

原码: 3个bit能表示8个数。 0,1,2,3,4,5,6,7

有符号数据!!

原码!!

​ 1 符号: 用 0,1表示正负号,放在数值的最高位,0表示正数,1表示负数。

​ 2 原码:3个bit能表示8个数字。如 +0 +1 +2 +3 -0 -1 -2 -3

000 001 010 011 100 101 110 111
+0  +1  +2  +3  -0  -1  -2  -3

这样有个缺陷: (+1)+(-2) = 001 + 110 = 111 = (-3)? 答案是错的。

反码:!!

​ 正数不变,负数的除了符号位,其他位置全部取反。

如:
+0 +1 +2 +3        -0  -1  -2  -3
000	001 010 011    111 110 101 100 

可以看到,除了符号位,其他位置跟正数全部是反着来的。那么(+1)+(-2) = 001 + 101 = 110 = (-1)正确。但是0有两个值,即000和111

补码:最高位溢出舍弃。!!

正数不变,负数在反码的基础上加1。

如:
0    +1 +2 +3            -1  -2  -3  -4
000	001 010 011          111 110 101 100

这样解决了可以带符号位进行运算不需要单独标识。

如 +1 + -2 = 001 + 110 = 111 = -1;

+1 + -1 = 001 + 111 = 1000(舍弃) = 000 = 0

解决了自然码正负0的表示方法。

实现了减法变加法。

  • 计算机只有补码。原码和反码只是为了让你快速计算得到补码。

小数点表示

  • 在计算机中,小数点及其位置是隐含规定的。小数点并不占用存储空间。
  • 定点数: 小数点的位置是固定不变的,分为定点整数定点小数
  • 浮点数:小数点的位置是会变化的。

定点小数

小数点隐含固定在最高数据位的左边,整数位则用于表示符号位,用于表示纯小数

X0(符号位) .(小数点隐含位置)   X1X2...Xn(数值位)

如0.35, 0.45。 如二进制的0.110011 = 十进制的0.796875

定点整数

小数点位置隐含固定在最低为之后,最高位为符号位,用于表示纯整数。

X0(符号位)  X1X2...Xn(数值位).(小数点隐含位置)

如(0110011.)2 = 51(10)

浮点数!!

  • 对于既不是定点整数也不是定点小数的数,用浮点数表示。
  • 在计算机中,通常把浮点数N分成阶码和尾数两部分来表示。
  • 小数点位是由阶码规定的,因此是浮动的。
    1.00101
    尾数就是0.100101
    阶码就是小数点的位置。
    阶码如果为2,小数点就要往右移动两位,10.0101
    如果为-2,就要往左移两位,0.00100101
    
  • N=尾数*基数^阶码,尾数是一个规格化的纯小数。 如 (11.1)2 = 尾数0.111 * 基数2 ^10, 二进制,所以基数是2,阶码也是2,转为二进制就是10.
  • 科学计数法,把一个数表示成a(1<= |a| <10)与10的n次幂相乘,可以节约时间和空间,如1100 = 1.1 * 10 ^ 3

IEEE754标准

  • js采用的是双精度(64位)8个字节
  • 符号位决定了一个数的正负指数部分决定了数值的大小小数有效部分决定了数值的精度
符号位(1)   指数位(2~1211位),小数有效位(13~6452位)
如(3.5)10 = (11.1)2 = 1.11 * 2^ 1
0 			000000000001 (对应指数1)		110...00(默认变成1.xxx,如1.11...00)(对应1.11)
符号位		指数位(11)				有效位(52)
  • 一个数在js内部实际的表示形式: (-1)^符号位 * 1.有效位 * 2^指数位,
  • 如上面:(3.5)10 = (11.1)2 = 0.111 * 2^ 10 = -1 ^ 0 + 1.11 * 2 ^ 1 = 3.5
  • 精度最多53个二进制位, -(2^53 -1)到2^53 - 1
  • 指数部分的最大值是2017,分一半表示负数,所以js能够表示的数值范围是2^1024 ~ 2 ^-1023

0.1 + 0.2 != 0.3

十进制0.1 => 二进制 => 0.0001100110011..... 十进制0.2 => 二进制 => 0.001101100110011.... 十进制0.3=> 二进制 => 0.01001100110011....

0.1 * 2= 0.2  // 0
0.2 * 2 = 0.4 //0
0.4 * 2 = 0.8  //0
0.8 * 2 = 1.6 //1
0.6 * 2 = 1.2 //1
0.2 * 2 = 0.4 //0
0.4 * 2= 0.8 //0 
0.8 * 2 = 1.6 //1
0.6 * 2 = 1.2 //1
...开始循环了

0.2 => 10进制
0.2 * 2 = 0.4 // 0

...开始循环
0.3 => 10进制
0.3 * 2 = 0.6 //0
0.6 * 2 = 1.2 //1
0.2 * 2 = 0.4 // 0
开始循环

他们三个是无限循环的小数。分别取出前10位相加

	0.0001100110  (0.1)
+   0.0011001100 (0.2)
=   0.0100110010
    0.0100110011 (0.3)

可以明显看到,到第八位开始就不一样了。(10进制转2进制的精度问题)

js大数相加

列竖式方法,从低位向高位计算。

  • 将原始数字进行倒序,从个位开始依次相加,满10进1。如下
	123123123123...
+	123123123123...
=   246246246246....一个一个加。

具体实现:

let num1 = "1234567890";
let num2 = "12345678";


// 将大数的每个数拆开,并且反转
let num1Array = num1
  .split("")
  .map((item) => parseInt(item))
  .reverse();
let num2Array = num2
  .split("")
  .map((item) => parseInt(item))
  .reverse();

// +1是因为两个数相加可能会进位。
const totalLength =
  num1Array.length > num2Array.length
    ? num1Array.length + 1
    : num2Array.length + 1;

//所得和
let sumArray = [].fill(0, 0, totalLength);

//先拷贝第一个数字
num1Array.forEach((item, index) => {
  sumArray[index] = item;
});

//相加
let up = 0; //保存进位
num2Array.forEach((item, index) => {
  sumArray[index] = sumArray[index] + item + up;
  if (sumArray[index] > 9) {
    //需要进位
    sumArray[index] = sumArray[index] % 10;
    up = 1;
  } else {
    up = 0; //不需要进位
  }
});


if(sumArray[sumArray.length -1] === 0){
    //最后一位为0,表示不用进位,直接抛弃
    sumArray.pop()
}
console.log(sumArray.reverse().join(''));
console.log(parseInt(num1)+ parseInt(num2));

// 结果
1246913568
1246913568