欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

二进制运算以及源码、补码、反码概念

程序员文章站 2023-08-15 08:51:16
前言在学习框架源码底层时,有非常多的二进制运算,由于大学学习计算机基础时抓梦脚(jio),没有学习牢固,所以在看底层源码的算法逻辑时遇到二进制运算比较吃力,遂通过一篇博文来总结下二进制运算,记录一下。正文1. 二进制基础因为计算机底层是通过二进制来进行计算的,所以在计算机底层会将十进制转换为二进制。十进制就是逢10进1,二进制就是逢2进1。就十进制来说,比如一百可以分为三位,个位、十位、百位, 用位数在下列表示 101这个十进制数。百位十位十位101因此对于十进...

前言

在学习框架源码底层时,有非常多的二进制运算,由于大学学习计算机基础时抓梦脚(jio)也就是开小差,没有学习牢固,所以在看底层源码的算法逻辑时遇到二进制
运算比较吃力,遂通过一篇博文来总结下二进制运算,记录一下。

正文

1. 二进制基础

因为计算机底层是通过二进制来进行计算的,所以在计算机底层会将十进制转换为二进制。十进制就是逢10进1,二进制就是逢2进1。

就十进制来说,比如一百可以分为三位,个位、十位、百位, 用位数在下列表示 101这个十进制数。

百位 十位 十位
1 0 1

因此对于十进制来说,越往上进制位越大,比如千位、万位。

同理,对于二进制来说也有对应位数, 如果表示二进制的4。

4(2^2) 2(2^1) 1(2^0)
1 0 0

1 * 4 + 2 * 0 + 1 * 0 = 4

如果要表示二进制的5,则如下图

4(2^2) 2(2^1) 1(2^0)
1 0 1

4 * 1 + 2 * 0 + 1 * 1 = 5

如果要表示二进制的11,则如下图

8(2^3) 4(2^2) 2(2^1) 1(2^0)
1 0 1 1

8 * 1 + 4 * 0 + 2 * 1 + 1 * 1 = 11

在计算机中,1字节有8位二进制位。

2. 二进制运算

对于二进制运算,记住一个口诀:

  1. 与(&)运算

    运算规则:
    0&0=0, 0&1=0, 1&0=0, 1&1=1

    二者为1则为1,否则都为0。

  2. 或(|)运算

    运算规则:
    0|0=0,0|1=1,1|0=1,1|1=1

    遇1则1,否则为0。

  3. 异(^)或运算

    运算规则:
    0^0=0, 1^0=1, 0^1=1, 1^1=0

    同为0,异为1。

3. 二进制的源码、补码以及反码

二进制源码是什么?

由于数字有正负之分,所以在计算机中通过在一个数的二进制的最高位存放符号(0为正,1为负),而其他数值位存放着就是数值的二进制位, 这就是二进制源码。

源码有缺点,就是不能直接进行运算,因为运算会出错。源码是有符号数的最简单的编码方式,便于输入输出,但作为代码加减运算时较为复杂。

二进制反码是什么?

反码通常是用来由源码求补码或者由补码求源码的过度码,根据定义可以根据补码的整数和小数中"0"的表示形式各有2中,+0和-0不一样。以8位机器数为例,
整数的"+0"源码为:0,0000000,反码为:0,0000000。整数的"-0"源码为:1,0000000,反码为:1,1111111。

反码跟源码是正数时一样,为负数是,除符号位外,其他为所有数值取反。

二进制补码是什么?

由于数字有正负之分,所以在计算机中通过在一个数的二进制的最高位存放符号(0为正,1为负),而这就是机器数的补码。

总结起来补码的作用就是:

  1. 使符号位能与有效值部分一起参加运算,从而简化运算规则.
  2. 使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计 所有这些转换都是在计算机的最底层进行的,而在我们使用的汇编、C等其他高级语言中使用的都是原码。

补码和反码之间怎么转换?

补码是反码加一。

小结

对于源码、补码和反码,三者均有符号位和数值位两部分。符号位用0表示正,1表示负,而数值位三者表示方法都不同。在计算机系统中,数值一律用补码来
表示和存储,原因在于补码可以将符号位和数值域统一处理,同时加法和减法也可以同一处理,此外源码和补码互相转换,其运算过程是相同的,不需要额外的硬件电路。
一个负整数和其补数相加和为模,对一个整数的补码再求补码等于该整数自身,补码的正零与负零表示方法相同。

4. 示例

比如变量 a & (-a) 用二进制怎么运算:

&是按位与,首先在计算机中数字都是以补码的形式存在的,比如:

a = 10

以8位二进制位为例,

+10,它的源码为:

0000 1010

由于正数的源码和补码相同,所以它的补码为:

0000 1010

对于-10,

它的源码为:

1000 1010

它的反码是源码的数值域取反,即:

1111 0101

它的补码是反码+1,即:

1111 0110

由于二进制运算都是用补码来计算的,所以 a & (-a) ,就是:

0000 1010 & 1111 0110 = 0000 0010 换算为十进制为2。

所以如果a=10,则 10 & (-10) = 2

在开源框架底层中算法会用到大量的二进制运算, 例如:在最近学习的Netty底层源码中,DefaultEventExecutorChooserFactory的底层源码有一个方法, 就是通过 a & (-a)来运算的。

@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
    if (isPowerOfTwo(executors.length)) {
        return new PowerOfTowEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}
/*
 * 用于计算val是否是2的幂,例如2、4、8、16
 */
private static boolean isPowerOfTwo(int val) {
   return (val & -val) == val;
}

private static final class PowerOfTowEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;

    PowerOfTowEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

    /*
     * 通过二进制与运算计算出下标索引值,原理如下:
     * 假设:idx = 2 , 二进制表示为:0000 0010
     * executors.length = 16, 则executors.length - 1 = 15, 二进制表示为:0000 1111
     * 
     * 由于是与运算,且executors.length高四位为0000,则只需要注意低四位的运算。
     * 则: 0010 & 1111 = 0010 ,十进制表示为2,
     * 
     * 所以如果idx = 3, 则二进制为:0011 & 1111 = 0011 也为3,
     * 等idx = 16时,二进制为:0001 0000 & 1111 = 0000 0000 ,即十进制的1,
     * 这样就实现了在0 ~ 15 范围内循环获取下标索引的目的。
     */
    @Override
    public EventExecutor next() {
        return executors[idx.getAndIncrement() & executors.length - 1];
    }
}

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;

    GenericEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

    /*
     * 通过取余运算,计算出下标索引
     */
    @Override
    public EventExecutor next() {
        return executors[Math.abs(idx.getAndIncrement() % executors.length)];
    }
}

总结

在计算机中,二进制的运算是比较重要的,可以看到在Java的许多开源框架底层就运用到了大量的二进制与或非运算,所以学好二进制的基础概念是非常重要的。

本文地址:https://blog.csdn.net/CoderBruis/article/details/107062587