娱乐,自己实现整数类型,不用加号计算加法,以及加法1+1为什么等于2?用另一种角度剖析1+1=2的原因

用代码的角度剖析1+1=2的原因

实际计算机在执行加法的时候,都是将数值转换成2进制,走加法器逻辑,可以更快速,此文是另一种角度的加法逻辑,并不是计算机的加法逻辑

此文包含的知识点,加法逻辑,1+1为什么等于2,进制的概念,链式结构,一点点小学知识。

  • 为什么需要数字?
  • 无限进制下1+1=2计算逻辑
  • 为什么需要有限进制?
  • 有限进制下的加法逻辑
  • 进制如何处理
  • 如何用js实现加法?
  • 如何用js不显式的用+号实现加法?
  • 知识点总结

为什么需要数字?

因为要做计算,生活中必不可少。常见的就是交易找零,货品价值表示都需要数字。

无限进制下1+1=2计算逻辑

1和2都是我们定义好的阿拉伯数字,在此之前10以内的数字大多都是用手指头表示,如果用手指表示0-10的话,应该是下面这个样子。
如果要求和,不管是js还是别的语言,都很简单,那就是两数相加即可,类似下面这个

|

||

|||

||||

|||||

||||||

|||||||

||||||||

|||||||||

在进制上,这种我们可以算是无限进制,因为永远不会有进位的存在。
我们计算加法的时候就可以这样计算,通过组合来,将两者合并在一起。

|+|=||

|+||=|||

在js中,我们计算这种加法可以用concat函数来

// |+|=||
"|".concat("|") // "||"

类似我们掰手指头算数一样的道理,不过随着数字的变大,很快我们手指头不够用了。

为什么需要有限进制?

  • 手指头不够用
  • 视觉上很难判断一堆竖杠到底是多大的数,在交易的时候找零变得非常麻烦
  • 占用了脑袋很大的空间去存储一个大数

我们需要一种高效的表示大数的方法,祖先选择了10进制(可能是十个手指的原因)。因为有进位的产生,我们上面用竖杠的表示方法行不通了,||是代表2还是代表11就有了争议,
而且一堆杠看不直观,那就需要10个能表示不同数字的字符,0,1,2,3,4,5,6,7,8,9。目前是比较通用的结构,还有我们的零,一,二,三…等等,
都可以表示十进制。

有限进制下的加法逻辑

  • 0-9如何存储?
  • 数字如何相加,进位情况下如何计算?

存储:0-9因为每连续的两个数字的间隔是一样的,存储上就用链式结构。数组当然也可以,但是查找的的时候要用到iterator才行。用链式结构我觉得比较直观。

{
    value:"0",
    next:{
        value:"1",
        next:{
            value:"2",
            next:{
                value:"3",
                next:{
                    //....
                }
            }
        }
    }
}

计算:加数和被加数长度补齐,依次遍历每一位数。
再从头遍历链式结构,每一次循环,判断当前的value值是否和被加数的值是否一致,如果不一致,将加数后移,加链式引用后移,如果一致,则跳出循环。
此时和即为加数的值。存在进位的情况下,事先将进位存储起来,下次遍历时参与加法计算,计算逻辑和加数与被加数一致。

_add(number){
    //求两个数字,哪个数字大,返回值是最大的那个数
    let max=this.isMax(number,true);
    if(max){
        //如果max为空,则是说明相等,否则,将两个数的长度强行相等,长度小的前面补flag.value 也就是虚无
        if(max===number){
            this.padStart(this,number)
        }else {
            number.padStart(number,this);
        }
    }
    //反转虚拟数字,并用遍历器,一次读取每一位数
    let value=this.value.split("").reverse().join("")[Symbol.iterator]();
    let augend=number.value.split("").reverse().join("")[Symbol.iterator]();
    let flag = NumberString.flag;
    let result=[];//结果
    let carry=flag;//进位
    while (true){
        //遍历每一个数字
        let nextCarry=flag;
        let a=value.next();
        let b=augend.next();
        if(a.done||b.done){
            //如果执行完毕,判断是否有进位的情况,有的话就把进位放到第一个。
            if(flag.value!==carry.value){
                result.unshift(carry.value);
            }
            break
        }else {
            let cur=flag;
            //获取加数的位置
            let ap=NumberString.getPosition(a.value);
            while (true){//加数和被加数相加
                //遍历链式结构,如果当前位置和被加数的值是一样的,跳出循环,否则移动当前引用到下一个虚拟数字
                if(cur.value===b.value){
                    break
                }else {
                    cur=cur.next;
                    ap=ap.next;
                    //链到头了?进位一下。重头再来
                    if(!ap){
                        nextCarry=nextCarry.next;
                        ap=flag;
                    }
                }

            }
            cur=flag;//重置
            while (true){//进位和前面的和相加
                //遍历链式结构,如果当前位置和进位数的值是一样的,跳出循环,否则移动当前引用到下一个虚拟数字
                if(cur.value===carry.value){
                    break;
                }else {
                    cur=cur.next;
                    ap=ap.next;
                    //链到头了?进位一下。重头再来
                    if(!ap){
                        nextCarry=nextCarry.next;
                        ap=flag;
                    }
                }
            }
            //进位保存下,下一项的时候要用
            carry=nextCarry;
            //每一项的保存在结果中
            result.unshift(ap.value);
        }
    }
    //将和输出
    return result.join("");
}

进制如何处理

其实很简单,链式结构如果只有0,1就是2进制,如果链式结构长度为10,就是10进制。

如何用js实现加法?

function add(a,b) {
    return a+b
}

如何用js不显式的用+号实现加法?

下面代码只讨论实现,不讨论效率优化。

//既然不能用+号,那我们用number类型就失去了意义,虚拟数字类型登场,虚拟数字代替数字,add函数代替+号 自定义isNaN代替isNaN,自定义Max函数代替Math.max。
class NumberString {
    //唯一的目的就是将数字变成字符串
    constructor(number,system){
        this.system=system||"9";//默认十进制
        this.value=String(number);
        //判断是否是个数字
        if(this.isNaN())throw new Error(String(this.value).concat(' is not a number'));
    }
    // 要确保后面两个数都是虚拟数字类型
    static add(number1,number2){
        if(!number1||!number2){
            throw new Error("number 必填哦");
        }
        if(!(number1 instanceof NumberString)){
            number1=new NumberString(number1);
        }
        if(!(number2 instanceof NumberString)){
            number2=new NumberString(number2);
        }
        return number1._add(number2);
    }
    add(number){
        return NumberString.add(this,number);
    }
    //模拟实现isNaN函数,遍历每一个字符,确保每一个字符都在flag链式结构中,否则就不是数字
    isNaN(number=this.value){
        if(typeof number!=='string'){
            number=String(number);
        }
        let value=number[Symbol.iterator]();
        while (true){
            let next=value.next();
            if(next.done){
                break;
            }else {
                let cur=this.getFlag();
                let isTrue=false;
                while (cur){
                    if(cur.value===next.value){
                        isTrue=true;
                        break
                    }else {
                        cur=cur.next
                    }
                }
                if(!isTrue){
                    return true
                }
            }
        }
        return false
    }
    //加法的核心逻辑
    _add(number){
        //求两个数字,哪个数字大,返回值是最大的那个数
        if (this.system!==number.system)throw new Error("两个虚拟数字的进制不同,暂时只支持相同进制的数字相加");
        let max=this.isMax(number,true);
        if(max){
            //如果max为空,则是说明相等,否则,将两个数的长度强行相等,长度小的前面补flag.value
            if(max===number){
                this.padStart(this,number)
            }else {
                number.padStart(number,this);
            }
        }
        //反转虚拟数字,并用遍历器,一次读取每一位数
        let value=this.value.split("").reverse().join("")[Symbol.iterator]();
        let augend=number.value.split("").reverse().join("")[Symbol.iterator]();
        let flag = this.getFlag();
        let result=[];
        let carry=flag;
        while (true){
            //遍历每一个数字
            let nextCarry=flag;
            let a=value.next();
            let b=augend.next();
            if(a.done||b.done){
                //如果执行完毕,判断是否有进位的情况,有的话就把进位放到第一个。
                if(flag.value!==carry.value){
                    result.unshift(carry.value);
                }
                break
            }else {
                let cur=flag;
                //获取加数的位置
                let ap=this.getPosition(a.value);
                while (true){//加数和被加数相加
                    //遍历链式结构,如果当前位置和被加数的值是一样的,跳出循环,否则移动当前引用到下一个虚拟数字
                    if(cur.value===b.value){
                        break
                    }else {
                        cur=cur.next;
                        ap=ap.next;
                        //链到头了?进位一下。重头再来
                        if(!ap){
                            nextCarry=nextCarry.next;
                            ap=flag;
                        }
                    }

                }
                cur=flag;//重置
                while (true){//进位和前面的和相加
                    //遍历链式结构,如果当前位置和进位数的值是一样的,跳出循环,否则移动当前引用到下一个虚拟数字
                    if(cur.value===carry.value){
                        break;
                    }else {
                        cur=cur.next;
                        ap=ap.next;
                        //链到头了?进位一下。重头再来
                        if(!ap){
                            nextCarry=nextCarry.next;
                            ap=flag;
                        }
                    }
                }
                //进位保存下,下一项的时候要用
                carry=nextCarry;
                //每一项的保存在结果中
                result.unshift(ap.value);
            }
        }
        //将和输出
        return result.join("");
    }
    getPosition(str){
        //遍历当前数字在链式结构的位置,也就是0=top,1=top.next,2=top.next.next;
        let cur=this.getFlag();
        while (cur){
            if(cur.value===str){
                break;
            }else {
                cur=cur.next;
            }
        }
        return cur
    }
    getFlag(system=this.system){
        let srcFlag=NumberString.flag;
        let flag = {};
        let _flag=flag;
        if(!system)system=NumberString.flag.next.value;
        while (true){
            _flag.value=srcFlag.value;
            if(_flag.value===system){
                _flag.next=null;
                break;
            }else {
                _flag.next={};
                _flag=_flag.next;
                srcFlag=srcFlag.next;
            }
        }
        return flag
    }
    //补虚拟0操作
    padStart(_this=this,number,str=NumberString.flag.value){
        while (_this.isMax(number,true)){
            _this.value=str.concat(_this.value);
        }
        return _this.value
    }
    //判断那个数字大,没考虑首位为0的情况
    isMax(number,onlyLength){
        let cur=this.getFlag();
        let value=this.value[Symbol.iterator]();
        let augend=number.value[Symbol.iterator]();
        let unitMax;
        while (true){
            let vn=value.next();
            let an=augend.next();
            //谁先结束谁小
            if(vn.done||an.done){
                if(vn.done===an.done){
                    //长度相同则首字符大的为大
                    return unitMax
                }
                if(vn.done){
                    return number
                }
                return this
            }
            if(!vn.done&&an.done){
                return this
            }
            //谁的首字符大谁大
            if(vn.value!==an.value&&!unitMax&&!onlyLength){
                while (cur.value){
                    if(vn.value===cur.value){
                        unitMax=number;
                        break;
                    }
                    if(an.value===cur.value){
                        unitMax=this;
                        break;
                    }
                }
            }
        }
    }
}
NumberString.flag = {
    value:"0",
    next:{
        value:"1",
        next:{
            value:"2",
            next:{
                value:"3",
                next:{
                    value:"4",
                    next:{
                        value:"5",
                        next:{
                            value:"6",
                            next:{
                                value:"7",
                                next:{
                                    value:"8",
                                    next:{
                                        value:"9",
                                        next:{
                                            value:"a",
                                            next:{
                                                value:"b",
                                                next:{
                                                    value:"c",
                                                    next:{
                                                        value:"d",
                                                        next:{
                                                            value:"e",
                                                            next:{
                                                                value:"f",
                                                                next:null
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
(function () {
    const a= new NumberString("9");
    const b= new NumberString("999");
    console.log(a.add(b));
})();

总结

  • 为什么1+2=3,因为根据链式结构的的移动1+1+1,最后移动到3的位置了。
  • 为什么2>1 ,因为根据链式结构的遍历得知,1先到,2后到,得出2在1的后面,先到的值是小的。
  • 进制就是逢x进1,对应的就是链的终止点。

加法借鉴了小学的加法思路,因个人能力有限,以上内容多有疏漏,欢迎大家指正。

完整代码地址

附:计算机加法器

int类型在执行加法时,是走的加法器,2个半加器组成1个全加器,32个全加器就可以执行32bit的加法计算了。
下面代码是用js模拟半加器和全加器的逻辑,对加法器原理不理解的可以自行百度加法器,网上讲的配有图文,更加容易理解。
感谢阅读。

function add(a,b) {
    //先将ab两个数字转换成2进制字符串
    let as = Number(a).toString(2);
    let bs = Number(b).toString(2);
    [as,bs]=alignment(as,bs);
    return cascade32(as,bs)
}
function alignment(a,b) {
    const space=a.length-b.length;
    if(space>0){
        b=b.padStart(a.length,"0")
    }else if(space<0){
        a=a.padStart(b.length,"0")
    }
    return [a,b]
}


function halfAdder(a,b) {
    a=Number(a);
    b=Number(b);
    return [a^b,a&&b]
}


function fullAdder(a,b,cin) {
    let result = halfAdder(a,b);
    let result2=halfAdder(result[0],cin);
    return [result2[0],result2[1]||result[1]]
}

function cascade32(a,b) {
    let len=a.length-1;
    const result=[];
    let cin=0;
    while (len>-1){
        let curA=a[len];
        let curB=b[len];
        let curR=fullAdder(curA,curB,cin);
        result.unshift(curR[0]);
        cin=curR[1];
        len--;
        if(len===-1&&cin===1){
            result.unshift(cin);
        }
    }
    return result.join("").padStart(32,"0");
}
console.log(add(100000,15));
console.log(parseInt(add(100000,15),2));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值