Bootstrap

Java实现64位定点数整数补码加减法


Java的相关文章已经有很多了,很多前辈也分享了很多很多编程经验与技巧。本文是我在学习Java过程中有感而发,代码实现计组的定点整数加减法的过程,希望对读者有帮助。
本课题要求编写一个程序:
① 输入:两个长整数
② 输出:两个数的和、差
③ 使用Java,计算过程要模拟手算64位定点整数加减法的过程,且使用字符数组储存二进制机器数。

技术要点

真值转换为64位补码

要得到补码,首先要能将真值的十进制转换为64位原码,从而得到64位反码,最后得到64位补码。
因此包含以下功能模块:

  • 十进制转二进制:使用除2取余法即可。
result[i] = num % 2 == 0 ? '0' : '1';
num /= 2;
  • 二进制补全为64位原码:符号位根据正负填充1、0,空位用0填充。
  • 64位原码各位取反得到64位反码:用一个循环以及逻辑非。
result[i] = result[i] == '1' ? '0' : '1';
  • 64位反码末位加1得到64位补码:开发一个全加器用于末位加1。
temp = FullAdder.getS(result[i], '0', c);
c = FullAdder.getC(result[i], '0', c);
result[i] = temp;

二进制机器码转十六进制

64位的二进制机器数可以说是相当的冗长,不便于开发者进行调试,因此需要开发一个方法将二进制机器码以16进制形式输出在终端上。
按照人工手算二进制转十六进制的方法,每四位划分一组,每组分别计算出一个十六进制数字,并进行字符串累加即可。

final static char[] hexCharacters = "0123456789ABCDEF".toCharArray();
private static String BtoH(char[] chars) {
        String result = "0x";
        for (int i = 0; i <= 15; i++) {
            int tag = 0;
            for (int j = 0; j <= 3; j++) {
                tag += chars[4 * i + j] == '1' ? Math.pow(2, 3 - j) : 0;
            }
            result += hexCharacters[tag];
        }
        return result;
    }

一位全加器的实现

在计算机组成原理中,我们学习了一位全加器74LS181的功能与逻辑表达式:
S i = A i ⊕ B i ⊕ C i C i + 1 = A i B i + ( A i + B i ) C i S_i=A_i\oplus B_i \oplus C_i \\C_{i+1}=A_iB_i+(A_i+B_i)C_i Si=AiBiCiCi+1=AiBi+(Ai+Bi)Ci
因此要得到 S i 、 C i + 1 S_i、C_{i+1} SiCi+1,只需要分别按照两个逻辑表达式分别写两个方法即可。

S i S_i Si

return x=='1'^y=='1'^c=='1'?'1':'0';

C i + 1 C_{i+1} Ci+1

return (x=='1'&&y=='1')||((x=='1'||y=='1')&&c=='1')?'1':'0';

模四补码加法

模四补码加法的双符号位使得溢出判断变得简单。
计算完成后,如果双符号位分别为
11或00:未溢出
10:下溢出
01:上溢出
由于使用了大小为64的字符数组,因此要实现双符号位,只需要额外再声明一个syn变量作为最高位符号位即可。判断溢出的时候,只需要将syn与字符数组的最高位进行比较、判断。

char syn = FullAdder.getS(xChar[0], yChar[0], c);
        //00或11
        if (result[0] == syn) {
            System.out.println("结果未溢出\n");
        } 
        //10
        else if (syn == '1')
            System.out.println("结果下溢出\n");
        //01
        else
            System.out.println("结果上溢出\n");

64位补码转换为十进制真值

补码加法得到的结果为一个补码,要将补码先转换为原码,再将原码转换为真值。
补码转原码,已经实现了,只需要套用原码转补码的方法就可以,一个字都不用改。
原码转真值,只需要按位乘以权重,再进行累加即可。

num += result[i] == '1' ? Math.pow(2, 63 - i) : 0;

代码实现

一位全加器的实现

FullAdder.java

public class FullAdder {
    public static char getS(char x,char y,char c){
        return x=='1'^y=='1'^c=='1'?'1':'0';
    }//得到Si
    public static char getS(char x,char y){
        return getS(x,y,'0');
    }
    public static char getC(char x,char y,char c){
        return (x=='1'&&y=='1')||((x=='1'||y=='1')&&c=='1')?'1':'0';
    }//得到进位位Ci
    public static char getC(char x,char y){
        return getC(x,y,'0');
    }
}

转换、计算功能的实现

public class BinaryComputing {
    final static char[] hexCharacters = "0123456789ABCDEF".toCharArray();
    //二进制转十六进制
    private static String BtoH(char[] chars) {
        String result = "0x";
        for (int i = 0; i <= 15; i++) {
            int tag = 0;
            for (int j = 0; j <= 3; j++) {
                tag += chars[4 * i + j] == '1' ? Math.pow(2, 3 - j) : 0;
            }
            result += hexCharacters[tag];
        }
        return result;
    }
	//输入十进制真值得到原码的二进制形式
    private static char[] getTrueFormBinary(long num) {
        char[] result = new char[64];
        //符号位
        result[0] = num >= 0 ? '0' : '1';
        if (num < 0) num = -num;
        //除2取余
        for (int i = 63; i > 0; i--) {
            result[i] = num % 2 == 0 ? '0' : '1';
            num /= 2;
        }
        return result;
    }
	//输入十进制真值得到补码的二进制形式
    private static char[] getCompletionFormBinary(long num) {
        char[] result = getTrueFormBinary(num);
        return getCompletionFormBinary(result);
    }
	//输入十进制真值得到补码的二进制形式
    private static char[] getCompletionFormBinary(char[] result) {
    	//原码为正数时,无需操作
        if (result[0] == '0') return result;
        //按位取反得到反码
        for (int i = 1; i < 64; i++) {
            result[i] = result[i] == '1' ? '0' : '1';
        }
        //使用一位全加器进行末位加1操作
        char c = '1', temp;
        for (int i = 63; i > 0; i--) {
            temp = FullAdder.getS(result[i], '0', c);
            c = FullAdder.getC(result[i], '0', c);
            result[i] = temp;
            if (c == '0') break;
        }
        return result;
    }
	//输入二进制原码得到十进制真值
    public static long trueFormToNum(char[] result) {
        long num = 0;
        for (int i = 1; i < 64; i++) {
            num += result[i] == '1' ? Math.pow(2, 63 - i) : 0;
        }
        return result[0] == '0' ? num : -num;
    }
	//输入二进制补码得到十进制真值
    public static long completionFormToNum(char[] result) {
        result = getCompletionFormBinary(result);
        return trueFormToNum(result);
    }
	//输入十进制真值得到十六进制原码
    public static String getTrueFormHex(long num) {
        return BtoH(getTrueFormBinary(num));
    }
	//输入十进制真值得到十六进制补码
    public static String getCompletionFormHex(long num) {
        return BtoH(getCompletionFormBinary(num));
    }
	//加法器
    public static long adder(long x, long y) {
        char[] xChar = getCompletionFormBinary(x), yChar = getCompletionFormBinary(y);
        char[] result = new char[64];
        System.out.println("转换得到x补码为" + BtoH(xChar) + "\n");
        System.out.println("转换得到y补码为" + BtoH(yChar) + "\n");
        char c = '0';
        for (int i = 63; i >= 0; i--) {
        	//利用一位全加器进行串行加法计算
            result[i] = FullAdder.getS(xChar[i], yChar[i], c);
            c = FullAdder.getC(xChar[i], yChar[i], c);
        }
        //声明syn变量作为拓展符号位
        char syn = FullAdder.getS(xChar[0], yChar[0], c);
        //判断溢出情况
        if (result[0] == syn) {
            System.out.println("结果未溢出\n");
        }
        else if (syn == '1')
            System.out.println("结果下溢出\n");
        else
            System.out.println("结果上溢出\n");
        System.out.println("结果的补码(16进制)为:" + BtoH(result) + "\n");
        //补码转真值
        return completionFormToNum(result);
    }
    //减法器:x-y=x+(-y)
    public static long subtractor(long x,long y){
        return adder(x,-y);
    }
}

测试样例

Test1.java

import java.util.Scanner;

public class Test1 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String scIn;
        long a, b;
            System.out.println("请输入x、y:\n");
            a = sc.nextLong();
            b = sc.nextLong();
            System.out.println("\nx+y:\n");
            System.out.println("结果为"+BinaryComputing.adder(a, b)+"\n");
            System.out.println("\nx-y:\n");
            System.out.println("结果为"+BinaryComputing.subtractor(a, b)+"\n");
    }
}

输入

-23 59

输出

请输入x、y:

-23 59

x+y:

转换得到x补码为0xFFFFFFFFFFFFFFE9

转换得到y补码为0x000000000000003B

结果未溢出

结果的补码(16进制)为:0x0000000000000024

结果为36


x-y:

转换得到x补码为0xFFFFFFFFFFFFFFE9

转换得到y补码为0xFFFFFFFFFFFFFFC5

结果未溢出

结果的补码(16进制)为:0xFFFFFFFFFFFFFFAE

结果为-82

希望对读者有所帮助

;