(上古遗产)聊聊二进制运算

好久没写资料了,一方面是现在要写的东西太多,思考的事情也太多,都没什么时间来坐下来整理一下有趣的资料出来(其实还是因为自己太懒。)
今晚偶然间看到有人问起了不用内置+运算符怎么实现+法,这个问题让我想起了不用比较运算符(<、>、=)怎么实现比较,其实都不是问题,用我的理解方式去理解其实就是一个小学的问题= -=。
那按我前面所说的就设置一个最终目标吧。

如何从零开始实现一个比较运算符。

( 显然今天这篇是讲不到最终目标了,也许以后也讲不到了。)

首先我们先在数学上讲得通道理再拿到计算机里面看着实现,比较这事情呢,主要就看与0的结果。
比如:a > b => a - b > 0,那我们只需要知道 (a-b) 的结果的正负号就可以做出比较的结果了。
所以我们得有正负数的定义吧,然后还得有(a-b)的减法实现吧。
那么如何实现减法呢?一个正数加上一个负数是不是就有了减法呢?确实是,但是很可惜,这很多余。
实际上,你写出了加法就一定写得出减法,这是我们过去九年义务教育里面教的一种运算方式告诉我们的。

小时候,我们最早计算9+11的时候是这样算的。
     11
   +  9
-------
     20
没错吧,这个我认为中国人都应该知道的吧,这个在我这里叫竖式计算,虽然我并不确定它是不是就叫这名字,反正百度是这么说的,废话真多。
其实,这个在二进制数中也是可行的,而上面那个是十进制的,二进制的就叫逢二进一。

     11    =>        1011
   +  9    =>    +  1001
-------              --------
     20              10100
这个结果你能接受的话,那我们就进入到代码实现部分吧。
对于二进制的竖式计算中存在两个逻辑,分别是进位和合并。
进位指对列同为1的执行逢二进一,合并指对列不同数值的执行为1。
以上逻辑将分别用二进制运算来取代,前者可以视为按位与(&)和左移一位,后者可以视为两数执行按位或。
我附加一点二进制运算说明吧。

在位运算中^和&分别产生以下结果。
  1011
^ 1001
---------     
  0010

数学意义为,保留未进位的结果。
(因为同为1则进位,同为0无结果,最终结果都是要将其变为0,则可以理解为进位后的结果。)

  1011

& 1001
---------
1001
数学意义为,标记所有进位位置,对其执行 << 1 即可得到进位的结果。

a 1011
b 1001
根据竖式运算可知:
1011
+ 1001
---------
00010 // 保留未进位的结果
10010 // 得到进位后的结果
---------
10000 // 保留未进位的结果
00100 // 得到进位后的结果
---------
10100 // 保留未进位的结果
00000 // 得到进位后的结果
合并结果后检查是否仍然可进位,直到最终不存在可进位的数,则说明加法结束。

= -= 我累了,不想写了就这样吧,最后贴个代码。

unsigned add(unsigned a, unsigned b)
{
    unsigned sum, carry;
    while(1)
    {
        sum = a ^ b, carry = a & b;
        if(0 == carry) 
        {
            break;
        }
        a = sum, b = carry << 1;
    }
    return sum;
}
unsigned Add(unsigned a, unsigned b)
{
    unsigned sum = a ^ b, carry = a & b;
    return (0 != carry) ? Add(sum, carry << 1) : sum;
}
上一篇:PTA 1011 A+B 和 C (15 分)(Java)


下一篇:二进制转十进制和十六进制