十进制补码的本质是借9当10,关键操作是别忘记加上1。而如此做的原因是避免直接算减法而频繁处理借位的情况,所以干脆一下子把所有的位都借了
比如一个数有四位,-1234,那么先借9,就是8765,然后再加上1,就是8766 。
在这里,借了4个9,又借了1,刚好是欠了1 0000 。这里欠的,再计算玩结果后,还是要还的,这样得到的值才正确。
而怎么还呢,如果刚好有进位,那么这个位就是在万位上,此时把进位舍去,就刚好换掉了刚开始借的1 0000
比如
1234 + (-1234) = 1234 + (9999 - 1234 + 1) - 10000 = 1234 + 8766 - 10000 = 10000 - 10000 = 0;
那么如果,两个数相加没有进位呢?比如
-66 + (-66) = (99 - 66 + 1) + (99 - 66 + 1) - 100 - 100 = 34 + 34 - 100 - 100 = 68 - 100 - 100 = - (99 - 68 + 1) - 100 = -132;
这样看起来还是可以的,但是因为没有进位抵消掉一个,所以最后多减去100,这 显得有点不好看,为了保证都进位,所以干脆多借一位,于是
-66 + (-66) = (999 - 66 + 1) + (999 - 66 + 1) - 1000 - 1000 = 934 + 934 - 1000 - 1000 =1868 - 1000 - 1000 =868 - 1000 =-(999 - 868 + 1)= - 132;
这就舒服多了吧。所以进位一律舍去即可。这里多借的一位,就是传说中的符号位。
那正数为啥补码是他本身呢?这个嘛,因为正数不缺,所以不用补齐。补码是方便把减法转换成加法计算,所以负数由源码得到补码,再由补码换回源码,这里负数所借的位,在最后换算回来的时候,都已经还掉了。所以这个过程只和负数本身有关系,和正数没有任何关系。
呵呵,可能大家都发现了,只要最后加了一个1,前面加多少个9都可以,之后最后结果舍去进位即可(因为我已经强制他进位了,符号位)。这也是为什么在对齐数位的时候
负数补9正数补0了。
-66 + 34 = 934 + 034 = 968 = - 32;
看起来,已经差不多可以解释通了,但是0的问题呢?如何证明-0的补码就是0呢?因为本身0就是一个稳定点,既不是负,也不是正,也可以说既是正又是负。不是很好表达
-0 = 9 - 0 + 1 - 10 = 10 - 10 = 0 ;
注意到这里的进位已经偿还了0的借位。不用等到和其他数字加后,就已经偿还了。哦,叫他自偿还吧。不生不灭,不垢不净,不增不减。:P
/**
----------------------------------------------------------------------------------------------------------------------------------------
终于有时间把这件事情开个头。呵呵
处理说明:
如何求补码:
非负数的补码是自己
负数的补码是用9去减自己每位上的数,如此肯定够减,不用借位。得到的结果再加上1
如何补齐:
做加法的时候需要低位对齐,对于非负数,高位补0,对于负数,高位补9
如何处理最后的进位:
对齐后的数字按正常的运算逻辑进行加法即可,不用考虑符号位,但是高位的进位处理需要考虑,因为这里是要求准确值,不允许截断。
所以需要考虑两个数的符号。
若是两个数都是负数,那么结果一定是负数,不需要进位直接舍弃,然后再由补码得到真实值即可
若是两个数一个非负一个负,如果有进位,那么结果为正,此时直接返回即可,如果没有进位那么结果为负,由补码求真实值即可
若是两个数都是非负数,如果有进位则进位,然后返回,否则直接返回。
----------------------------------------------------------------------------------------------------------------------------------------
*/
package complement;
public class Complement {
private static final int OFFSET = '0';
private int[][] table = new int[10][10];
public Complement() {
this.initTable();
}
public void initTable() {
for (int i = 0; i < 10; i++) {
for (int j = 0; j <= i; j++) {
table[i][j] = i + j;
table[j][i] = i + j;
}
}
}
public String lpadnum(String num, int len, boolean sign) {
for (; len > 0; len--) {
if (sign) {
num = "9".concat(num);
} else {
num = "0".concat(num);
}
}
return num;
}
// 处理补码运算,999xx-num + 1
public String processComplement(String num) {
StringBuilder sb = new StringBuilder(num);
int len = sb.length();
for (len--; len > -1; len--) {
sb.setCharAt(len, (char) ('9' - sb.charAt(len) + '0'));
}
// +1
len = sb.length() - 1;
while (true) {
char ch = sb.charAt(len);
if (ch == '9') {
ch = '0';
sb.setCharAt(len, ch);
if (len == 0) {
break;
}
len--;
} else {
ch += 1;
sb.setCharAt(len, ch);
break;
}
}
return sb.toString();
}
// 由补码表示的数,求出真实值
public String deComplement(String num) {
return "-" + processComplement(num);
}
// 求一个数的补码
public String getComplement(String num) {
if (num.startsWith("-")) {
return "9" + processComplement(num.substring(1));
} else {
return num;
}
}
// 只是处理了整数
public String add(String num1, String num2) {
/**
* 在处理-0问题上,代码没有能够和一般负数公用,先特例出来吧。
*/
if (num1.equals("-0")) {
num1 = "0";
}
if (num2.equals("-0")) {
num2 = "0";
}
String info = num1 + " + " + num2;
// 符号系统
boolean flag1 = num1.startsWith("-");
boolean flag2 = num2.startsWith("-");
// 求补数
num1 = getComplement(num1);
num2 = getComplement(num2);
// 对齐
int pad = num1.length() - num2.length();
if (pad > 0) {
num2 = lpadnum(num2, pad, flag2);
} else {
num1 = lpadnum(num1, -pad, flag1);
}
info += " = " + num1 + " + " + num2;
int len = num1.length();
boolean carry = false;
StringBuilder sb = new StringBuilder(num1);
for (len--; (len > -1); len--) {
int ret = table[num1.charAt(len) - OFFSET][num2.charAt(len) - OFFSET];
// 处理进位
if (carry) {
ret++;
}
// 设置结果
sb.setCharAt(len, (char) (ret % 10 + '0'));
if (ret > 9) {// 标志进位
carry = true;
} else {
carry = false;
}
}
String ret;
// 这里要计算加减要得到正确的值,就必须得处理符号
if (flag1 && flag2) {// 都是负的那么结果是负的
ret = deComplement(sb.toString());
} else if (!flag1 && !flag2) {// 有进位,且都是正的,那么进位
if (carry) {
ret = "1" + sb;
} else {
ret = sb.toString();
}
} else {// 两个数异号
if (carry) {
ret = sb.toString();
} else {
ret = deComplement(sb.toString());
}
}
System.out.println(info + " = " + ret);
return ret;
}
public static void main(String[] args) {
Complement comp = new Complement();
comp.add("988", "-12");
comp.add("988", "988");
comp.add("999", "-1");
comp.add("-999", "1");
comp.add("1", "-1");
comp.add("-11", "-12");
comp.add("12", "-12");
comp.add("1", "1");
comp.add("10", "10");
comp.add("100", "100");
comp.add("10000", "-100");
comp.add("10000", "-99999");
comp.add("10000", "-0");
}
}