美文网首页
不积跬步之二进制的那些事儿

不积跬步之二进制的那些事儿

作者: 雨飞飞雨 | 来源:发表于2021-12-30 18:45 被阅读0次
    易经八卦.jpeg

    我们都知道二进制是0和1组成的,在二进制被 戈特弗里德·莱布尼茨设计出来的时候,据说他学习参考了中国的<易经>,<易经>中的太极生两仪,两仪生四象,思象生八卦,八卦生十六卦,十六生32卦,32卦生64卦.像不像我们的二进制计数呢 1 2 4 8 16 32 64,好了,我们进入正题

    • 二进制在IEEE 754 64 中的存储格式是什么?
    • 位操作都有哪些?
    • 二进制的加减乘除
    • 二进制和十进制,八进制,十六进制的互相转换

    二进制在IEEE 754 64 中的存储格式是什么?

    ECMAScript 中所有的值都以 IEEE 754 64 位格式存储,但是位操作并不直接应用到64位,而是先把值转换为32位再进行位操作,之后再把结果转换为64位.对于开发者而言,就好像只有32位,因为64位整数存储格式是不可见的.既然知道了这个,那么就只需要考虑32位整数的就可以了

    那么32位的整数又是怎么使用的呢?

    有符号整数使用32位的前31位表示整数值,第32位表示数值的符号,如0表示正,1表示负,这个位称为符号位,它的值决定了数值其余部分的格式.

    正值的存储

    正值以真正的二进制格式存储,即31位中的每一位都代表2的幂.第一位(成为0位)表示2**0 ,第二位表示2**1,第三位就是2**2以此类推.如果一个位是空的,则以0填充,相当于忽略不计.比如数值18的二进制格式为0000 0000 0000 0000 0000 0000 0001 0010 或者精简为10010.后者是用5个有效位,来决定实际的值.

    image.png

    负值的存储

    负值以一种成为二补数(或补码)的二进制编码存储.一个数值的二补数通过以下三个步骤来完成.

    1. 确定绝对值的二进制表示,如对于-18就是先确定18的二进制表示
    2. 找到数值的反码(补数),就是把所有的0变成,所有的1变成0
    3. 最后,给结果加1

    就完成了.

    我们实际操作一下:

    第一步:-18的绝对值18的二进制是

    0000 0000 0000 0000 0000 0000 0001 0010
    

    第二步:取反,把每个0变成1,每个1变成0

    1111 1111 1111 1111 1111 1111 1110 1101
    

    第三步:最后给这个值加个1

    1111 1111 1111 1111 1111 1111 1110 1110
    

    这个就是我们的-18

    位操作都有哪些?

    1.按位非 ~

    按位非操作符用波浪符~表示,它的作用是返回数值的一补数,例如:

    let num1 =25 ;    // 0000 0000 0000 0000 0000 0000 0001 1001
    let num2 = ~num1; // 1111 1111 1111 1111 1111 1111 1110 0110
    console.log(num2); // -26
    

    通过上面的结果可以看出 按位非的最终结果是对数值取反并减-
    相当于

    let num1 = 25;
    let num2 = -num1 - 1;
    

    虽然效果一样,但是未运算要快的多.

    2.按位与 &

    按位与操作符使用&表示.

    第一数值的位 第二个数值的位 结果
    1 1 1
    1 0 0
    0 1 0
    0 0 0

    就是 两个1才返回1 任何一位是0 就返回0 这个不就是乘法吗? 1 * 1 = 1 ,1 * 0 = 0 ,0 * 0 = 0

    例如:25 & 3

    25  =  0000 0000 0000 0000 0000 0000 0001 1001
    3   =  0000 0000 0000 0000 0000 0000 0000 0011
    ----------------------------------------------
    AND =  0000 0000 0000 0000 0000 0000 0000 0001
    

    最后得到的值就是1

    3.按位或 |

    用管道符表示 只要1 就返回1 两个是0 就返回0

    第一数值的位 第二个数值的位 结果
    1 1 1
    1 0 1
    0 1 1
    0 0 0

    还是 25 | 3 ,我们看看值是多少呢?

    25  =  0000 0000 0000 0000 0000 0000 0001 1001
    3   =  0000 0000 0000 0000 0000 0000 0000 0011
    ----------------------------------------------
    OR  =  0000 0000 0000 0000 0000 0000 0001 1011
    

    等于 27

    4.按位异或 ^

    按位异或和按位或的区别是 ,它只在一位上是1的时候才返回1 ,两个是1的时候反而是0 ,当然两个1的时候也是0

    ---|---|--
    1 | 1 | 1
    1 | 0 | 1
    0 | 1 | 1
    0 | 0 | 0

    25 ^ 3 = ?

    25  =  0000 0000 0000 0000 0000 0000 0001 1001
    3   =  0000 0000 0000 0000 0000 0000 0000 0011
    ----------------------------------------------
    XOR =  0000 0000 0000 0000 0000 0000 0001 1010
    

    等于 26.

    5.左移 <<

    会按照指定的位数将数值的所有位向左移动.比如,如果数值2 (等于二进制是10),就会得到64(二进制2000000)

    let oldValue = 2; // 10
    let newValue = oldValue << 5; //1000000
    

    注意:在移位后,数值右端会空出5位,左移会以0填充这些空位,让结果是完整的32位数值.

    注意:左移会保留它所操作数值的符号.所以-2 << 5 就是 -64

    6.有符号右移 >>

    会将数值的所有32位都向右移..同时会保留符号位(整数还是负数),有符号右移其实就是左移的逆运算.
    例如:64 >> 5 === 2

    0000 0000 0000 0000 0000 0000 0010 0000
    ---------------------------------------
    0000 0000 0000 0000 0000 0000 0000 0010
    

    如果移位的值超过它自己的值会怎么样,例如2 >> 5 ,它的值是0

    移位出现的空值会补0

    7.无符号右移 >>>

    正数

    无符号右移和有符号右移相同

    负数

    对于负数,有时候差异会非常大,与有符号右移不同,无符号右移会给空位补零,而不管符号位是什么.
    对于正数,当然没有没问题.但是对于负数,就差距很大了.无符号右移操作符将负数的二进制表示当成整数的二进制
    表示处理.因为负数时其绝对值的二补数,所以右移之后结果边得非常大.

    let oldValue = -64;// 1111 1111 1111 1111 1111 1111 1111 1100 0000
    let newValue = oldValue >>> 5;//等于十进制134217726
    -------------------------------------------------------------------
    >>> 0000 1111 1111 1111 1111 1111 1111 1111 1110
    

    就是 134217726

    二进制的加减乘除

    二进制的加法 逢 2 进 1

    还记得小学的时候我们学过的10进制加减乘除法是怎么算的吗?就是他们两个数竖着放在一起,各位个位对着个位,十位对着十位.百位对着百位.
    然后最重要的是,10进制它是逢10进1.而二进制它的区别是逢2进1,例如:我们 3 +1

       0 0 1 1
     + 0 0 0 1
     ---------
       0 1 0 0    
    

    1 + 1 等于 2 逢2 进1 然后 第二位 已经有了一个1 我们两个1 相加又进1 而自己则是 0 ,所以 是 0 1 0 0

    再看一个

       0 1 0 1  
     + 0 0 1 1 
    ----------
       1 0 0 0        
    

    转换成10进制就是 5+3 = 8;

    二进制的减法 不够借 2

    和二进制的加法的算法一样,我们把他们竖排,在10进制里面进行减法运算的时候有一个概念是 不够10的则向上一位借10做减法.
    在二进制中也一样,如果不够则从上一位借2 ,也就是 不够借2

       0 1 0 1  
     + 0 0 1 1 
    ----------
       0 0 1 0
    

    在运算的时候,第一位 1 - 1 = 0 ,第二位 0 -1 不够 ,怎么办,向上一位借1 啊,就是 2 - 1 ,剩下 1
    所以最后就是 0 0 1 0
    转换为10进制就 5 - 3 = 2
    结果也是相同

    二进制的乘法 1 * 1 = 1 , 0 * 1 = 0

    还记得10进制乘法是怎么算的吗?

              7   8
              5   6
    ----------------
             4    8
        4    2
        4    0
    3   5
    ---------------
    4   3    6    8
    

    10进制的关键是 每一位和上一个数的每一位想乘,所有的都遍历完成以后,在相加,逢10进1

       0 1 0 1  
     + 0 0 1 1 
    ----------
       0 1 0 1
     0 1 0 1
    ----------
     0 1 1 1 1
    

    5 * 3 = 15

    二进制的乘法是1 * 1 = 1 1 * 0 = 0,计算完毕以后进行相加. 逢 2 进 1

    二进制的除法

    二进制的除法和十进制的很像,都需要不够借1

              0 0 0 1 1
            -----------
    0 1 0 1 | 0 1 1 1 1
              0 1 0 1    
            -----------
                  1 0 1
                  1 0 1
                  -----
                      0
    

    最后是 0 1 1

    是不是和10进制的除法一样.

    二进制和十进制,八进制,十六进制的互相转换

    二进制转换成十进制

    二进制转换成十进制非常简单,我们知道二进制实际上是表示的2的幂
    我们以八位为例,这里直接用幂计算符号 **来表示

    0 0 0 0 0 0 0 0
    2**7 2**6 2**5 2**4 2**3 2**2 2**1 2**0
    128 64 32 16 8 4 2 1

    比如我们上面的数 0 1 0 1

    对应到上面的值 22 + 20 也就是 4 + 1 = 5

    按照位数对应到它的值,我们就能求出来.

    十进制转换成二进制

    同样可以参考上面的表格来计算.

    比如15 ,15这个值比16小,所以它应该是 8+4+2+1 = 15

    所以它的值就是

    16 8 4 2 1
     0 1 1 1 1
    

    如果是16就直接用16数值那里的1

    16 8 4 2 1
    1  0 0 0 0
    

    是不是很简单

    二进制转八进制

    以三位作为划分,我们按 倒排4 2 1 来标识,三排作为一个数字,因为8进制是逢8进1,所以它的值就是按照 最高位来排下来.

    1 0 0   1 1 0
    -----   -----
    4 2 1   4 2 1
    --------------
    4       6
    -------------
    46
    

    八进制数就是 46
    我们在看一个例子

    0 1 0   0 1 1
    -----   -----
    4 2 1   4 2 1
    ---------------
      2       3
    

    最后得到的值是23(8)八进制数

    那么带小数的是怎么算的呢?

    0 1 1 . 0 1 1
    ----    -----
    4 2 1   4 2 1
    --------------
      3       3
    

    最后得到的值就是 3.3(8)八进制数

    八进制 转 二进制

    实际上和10进制转二进制很像
    我们知道一个八进制数需要三个二进制数来表示

    例如 八进制数 46(8)

      4    6
    ---    ---
    4 2 1  4 2 1
    -----  -----
    1 0 0  1 1 0
    
    

    最后转换出来就是 100 110,带小数点也一样计算.

    二进制 转 十六进制

    十六进制的前9位是正常的1~9,从10 开始
    A --> 10

    B --> 11

    C --> 12

    D --> 13

    E --> 14

    F --> 15

    我们上面的八进制是需要三个二进制数来表示,16进制,就需要4个二进制数来表示.
    例如:

    1 1 1 0  1 1 0 0
    -------  -------
    8 4 2 1  8 4 2 1
    ----------------
    14       12
    ----------------
    E C
    

    最后就是 0xEC

    parseInt(0xEC).toString(2)
    //'11101100'
    

    可以看到是正确的

    同样我们也可以推理出十六进制转二进制的做法

    十六进制 转 二进制

    十六进制参考八进制数据转换

    E        C
    
    14       12
    -------  -------
    8 4 2 1  8 4 2 1 
    -------  -------
    8+4+2    8+4
    -------  -------
    1 1 1 0   1  1  0 0 
    

    最后转换成 二进制就是 1110 1100

    Over...

    相关文章

      网友评论

          本文标题:不积跬步之二进制的那些事儿

          本文链接:https://www.haomeiwen.com/subject/bdfwqrtx.html