很久没写资料了,一方面是如今要写的东西太多,思考的事情也太多,都没什么时间来坐下来整理一下有趣的资料出来(其实仍是由于本身太懒。)ui
今晚偶然间看到有人问起了不用内置 + 运算符怎么实现 + 法,这个问题让我想起了不用比较运算符( < 、 > 、 = )怎么实现比较,其实都不是问题,用个人理解方式去理解其实就是一个小学的问题 = -= 。code
那按我前面所说的就设置一个最终目标吧。数学
如何从零开始实现一个比较运算符。class
(显然今天这篇是讲不到最终目标了,也许之后也讲不到了,不过看完这篇文章后,你应该就知道怎么作了。)百度
首先咱们先在数学上讲得通道理,再拿到计算机里面看着实现,比较这事情呢,主要就看与 0 的结果。 好比:a > b => a - b > 0,那咱们只须要知道 (a - b) 的结果的正负号就能够作出比较的结果了。 因此咱们得有正负数的定义吧,而后还得有(a - b)的减法实现吧。 那么如何实现减法呢?一个正数加上一个负数是否是就有了减法呢?确实是。 因此今天的主题就回到标题上,只要有了加法,那就会有了减法,一样也会有乘法,甚至就会有了比较运算,就如上解释可知。二进制
小时候,咱们最先计算 9 + 11 的时候是这样算的。计算机
11 + 9 ------- 20
没错吧,这个我认为中国人都应该知道的吧,自行百度 竖式计算 。 其实,这个在二进制数中也是可行的,而上面那个是十进制的,二进制的就叫逢二进一。时间
11 => 1011 + 9 => + 1001 ------- -------- 20 10100
这个结果你能接受的话,那咱们就进入到代码实现部分吧。思考
对于二进制的竖式计算中存在两个逻辑,分别是 进位 和 合并 。 进位指对列同为 1 的执行逢二进一,合并指对列不一样数值的执行为 1 。 以上逻辑将分别用二进制运算来取代,前者能够视为按位与(&)和左移一位,后者能够视为两数执行按位或。 我附加一点二进制运算说明吧。while
在位运算中 ^ 和 & 分别产生如下结果。
1011 ^ 1001 --------- 0010
数学意义为,模拟逢二进一的相加结果,保留未进位的结果,这是为了模拟竖式计算的加法结果。 (由于同为 1 则进位,同为 0 无结果,最终结果都是要将其变为 0 ,则能够理解为进位后的结果。)
1011 & 1001 --------- 1001
数学意义为,标记全部进位位置,对其执行 << 1 便可获得进位的结果。
以 1011 + 1001 为例:
根据竖式运算可知:
1011 // + 1001 // --------- 00010 // 先 1011 ^ 1001 合并获得未进位的结果( 1011 ^ 1001 = 00010 ) 10010 // 1011 & 1001 = 1001 不为 0 存在进位,故执行 1001 << 1 获得进位后的结果 1001 --------- 10000 // 经过 00010 ^ 10010 合并获得未进位的结果( 00010 ^ 10010 = 10000 ) 00100 // 00010 & 10010 = 00010 不为 0 存在进位,故执行 00010 << 1 获得进位后的结果 00100 --------- 10100 // 经过 10000 ^ 00100 合并获得未进位的结果( 10000 ^ 00100 = 10100 ) 00000 // 10000 & 00100 = 00000 ,此时结束运算。 ---------
合并(^)结果后检查是否为可进位(&),直到最终不存在可进位的数,则说明加法结束。
最后贴个代码。
uint add(uint a, uint b) { uint sum, carry; while(1) { sum = a ^ b, carry = a & b; if(0 == carry) { break; } a = sum, b = carry << 1; } return sum; } uint Add(uint a, uint b) { uint sum = a ^ b, carry = a & b; return (0 != carry) ? Add(sum, carry << 1) : sum; }