美文网首页算法程序员
2018-01-15 A+B 不用加号实现整数相加

2018-01-15 A+B 不用加号实现整数相加

作者: BlackChen | 来源:发表于2018-01-15 11:24 被阅读241次

    直接上代码

    #include <stdio.h>
    
    int add_one(int a, int b) {
        return a - (-b);
    }
    
    int add_two(int a, int b) {
        char *p = (char *) a;
        int c = (int) &p[b];
        return c;
    }
    
    int add_three(int a, int b) {
        if (0 == b)
            return a;
        int cxor = a ^b;
        int cand = a & b;
        return add_three(cxor, cand << 1);
    }
    
    int add_four(int a, int b) {
    
        int cxor = 0;
        int cand = 0;
        while (b != 0) {
            cxor = a ^ b;
            cand = (a & b) << 1;
            a = cxor;
            b = cand;
        }
        return cxor;
    }
    int main() {
        printf("#########ONE#########\n");
        int c = add_one(-20, -20);
        printf("c = %d\n", c);
    
        printf("#########TWO#########\n");
        c = add_two(-30, -20);
        printf("c = %d\n", c);
    
        printf("#########THREE#########\n");
        c = add_three(-40, 90);
        printf("c = %d\n", c);
    
        printf("#########FOUR#########\n");
        c = add_four(40, -50);
        printf("c = %d\n", c);
    
        return 0;
    }
    
    输出:
    #########ONE#########
    c = -40
    #########TWO#########
    c = -50
    #########THREE#########
    c = 50
    #########FOUR#########
    c = -10
    
    

    add_one

    int add_one(int a, int b) {
        return a - (-b);
    }
    

    不解释.......小聪明

    add_two

    int add_two(int a, int b) {
        char *p = (char *) a;
        int c = (int) &p[b];
        return c;
    }
    

    使用p[b] 获取char数组 b个字节的偏移量.

    int main() {
    
        char a[10] = {'1','2','3','4','5'};
        char *p = a;
    
        printf("p = %p\n",p);
        printf("a = %p\n",&a);
    
        printf("a[2] = %c\n",a[2]);
        printf("p+2= %c\n",*(p+2));
    
        printf("a[2]的地址: ,%p\n",&a[2]);
        printf("p + 2 的地址: %p\n",p+2);
    }
    
    输出:
    p = 0x7fff5ade795e
    a = 0x7fff5ade795e
    a[2] = 3
    p+2= 3
    a[2]的地址: ,0x7fff5ade7960
    p + 2 的地址: 0x7fff5ade7960
    

    指针 + 数字--> 指针类型指向的类型长度 * 数字 + 指针本来地址,现在把char 换为int

    int a[10] = {'1', '2', '3', '4', '5'};
        int *p = a;
    
        printf("p = %p\n", p);
        printf("a = %p\n", &a);
    
        printf("a[2] = %d\n", a[2]);
        printf("p+2= %d\n", *(p + 2));
    
        printf("a[2]的地址: ,%p\n", &a[2]);
        printf("p + 2 的地址: %p\n", p + 2);
    
    输出:
    p = 0x7fff57360940
    a = 0x7fff57360940
    a[2] = 51
    p+2= 51
    a[2]的地址: ,0x7fff57360948
    p + 2 的地址: 0x7fff57360948
    
    

    add_three 和 add_four

    int add_three(int a, int b) {
        if (0 == b)
            return a;
        int cxor = a ^b;
        int cand = a & b;
        return add_three(cxor, cand << 1);
    }
    

    使用位运算来计算加法,涉及到位的操作.

    • ^ 异火运算符 简称不进位加法
      0 ^ 0 = 0
      1 ^ 0 = 1
      1 ^ 1 = 0

    • & 与运算符
      0 ^ 0 = 0
      1 ^ 0 = 0
      1 ^ 1 = 1

    使用^ 运算符可以计算出两个数相加的所有不进位的值
    使用& 运算符可以计算出两个数相加的所有进位值,

    例如 :

    • 计算 a = 11
      b = 5
      a + b = 16
    a = 11 的二进制表示  1011
    b = 5 的二进制表示   0101
    
    cxor = a ^ b = 1 0 1 1 
                   0 1 0 1
                   1 1 1 0 
    
    cand = a & b =  1 0 1 1
                    0 1 0 1
                    0 0 0 1
    然后 cand << 1 = 0010,有进位,再次做运算
    
    cxor = 1110 ^ 0010 = 1100
    cand = 1110 & 0010 = 0010 
    cand << 1  = 0100, 有进位,再次做运算
    
    cxor = 1100 ^ 0100  = 1000
    cand = 1100 & 0100 = 0100
    cand << 1 = 1000, 有进位,再次做运算
    
    cxor = 1000 ^ 1000 = 0000
    cand = 1000 ^ 1000 = 1000
    cand << 1 = 0001 0000 有进位,再次做运算
    
    cxor = 0000  0000 ^ 0001 0000 = 0001 0000
    cand = 0000 0000 & 0001 0000 = 0000 0000
    cand << 1 = 0 无进位,运算结束,相加值为 0001 000 = 16  
    
    

    我们计算一下负数的相加:
    这里我们要清楚的是,所有的数值在计算机中存储的都是补码
    将一个整数,转换成二进制,就是其原码。如单字节的5的原码为:0000 0101;-5的原码为1000 0101。
    反码:正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。如单字节的5的反码为:0000 0101;-5的反码为1111 1010。
    补码:正数的补码就是其原码;负数的反码+1就是补码。如单字节的5的补码为:0000 0101;-5的原码为1111 1011。

    在计算机中,正数是直接用原码表示的(正数的源码= 反码 = 补码),如单字节5,在计算机中就表示为:0000 0101。负数用补码表示,如单字节-5,在计算机中表示为1111 1011。

    • 计算 a = - 11
      b = 5
      a + b = - 6
    a = -11 在计算机中的二进制补码表示 1111 0101
    b = 5 的二进制表示   0000 0101
    
    cxor = a ^ b = 1111 0101
                   0000 0101
                    1111  0000
    
    cand = a & b =  1111 0101
                   0000 0101
                   0000 0101
    然后 cand << 1 = 0000 1010 ,有进位,再次做运算
    
    cxor =  1111  0000 ^ 0000 1010= 1111 1010
    cand = 1111  0000 &  0000 1010 = 0000 0000
    cand << 1 = 0 无进位,运算结束,相加值为 1111 1010  最高位为 1 说明是负数,现在把补码转换为源码:
    
    先减1 ,然后除符号位外按位取反:
    减 1 :
    1111 1010 - 0000 0001 = 1111 1001 
    取反:
    1111 1001  ---> 1000 0110
    
    0110  = 6
    最高位为 1 所以为 -6 
    

    总结

    1. 使用到了计算机中数值的表示,源码,反码,补码 ,计算机中是用补码来表示数值的
    2. 使用到地址的运算,与指针的运算
    3. 使用到^异或操作符 与 & 与 操作符.

    相关文章

      网友评论

        本文标题:2018-01-15 A+B 不用加号实现整数相加

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