算法程序员

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

2018-01-15  本文已影响241人  BlackChen

直接上代码

#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);
}

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

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

例如 :

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 在计算机中的二进制补码表示 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. 使用到^异或操作符 与 & 与 操作符.
上一篇下一篇

猜你喜欢

热点阅读