Programming 02

题目大意

​ 完成以下3种二进制下的计算单元

  • 整型加减乘位移ALU.java
  • 浮点型加减FPU.java
  • NBCD码加减NBCDU.java

大概看一遍,整型这一块处理很简单,后面的相对麻烦一点,码了一会把第一个ALUTest.java过掉了,这部分略过。


ALU.java代码

    public class ALU {

    // 模拟寄存器中的进位标志位
    private String CF = "0";

    // 模拟寄存器中的溢出标志位
    private String OF = "0";
    private StringBuilder ans=new StringBuilder();

    public static String getComplement(String tar) {
        tar = tar.replace("0", "2").replace("1", "0").replace("2", "1");
        char[] status = tar.toCharArray();
        for (int i = tar.length() - 1, jud = 1; i >= 0; i--) {
            status[i] = (char) ((jud ^ (tar.charAt(i) - '0')) + '0');
            jud = ((tar.charAt(i) - '0') & jud);
        }
        return Arrays.toString(status).replaceAll("[\\[\\]\\s,]", "");
    } //取补码的方法

    //add two integer
    String add(String src, String dest) {
        ans=new StringBuilder();  //因为把ans写成全局的,这里要重置
        int c=0,s=0;
        for(int i=31;i>=0;i--){
            int x=src.charAt(i)-'0',y=dest.charAt(i)-'0';
            s=x^y^c;
            c=(x&c)|(y&c)|(x&y);
            ans.append(s);
        }
        CF=""+c;
        OF=(src.charAt(0)==dest.charAt(0))&&(src.charAt(0)!=ans.charAt(0))?"1":"0";
        //不知道这俩东西有什么用,姑且写一写
        return ans.reverse().toString();
    }

    //sub two integer
    // dest - src
    String sub(String src, String dest) {
        ans=new StringBuilder();
        src=getComplement(src);
        return add(src,dest);
    }

    //signed integer mod
    String imod(String src, String dest) {
        boolean flag=false;
        if(src.charAt(0)=='1'){
            src=getComplement(src);
        }
        if(dest.charAt(0)=='1'){
            flag=true;
            dest=getComplement(dest);
        }
        while(dest.charAt(0)!='1'){
            dest=sub(src,dest);
        }
        dest=add(src,dest);
        if (flag) return getComplement(dest);
        return dest;
    }

    String and(String src, String dest) {
        for (int i=0;i<32;i++)
            ans.append(dest.charAt(i)==src.charAt(i)&&dest.charAt(i)=='1'?'1':'0');
        return ans.toString();
    }

    String or(String src, String dest) {
        for (int i=0;i<32;i++)
            ans.append(dest.charAt(i)==src.charAt(i)&&dest.charAt(i)=='0'?'0':'1');
        return ans.toString();
    }

    String xor(String src, String dest) {
        for (int i=0;i<32;i++)
            ans.append(dest.charAt(i)!=src.charAt(i)?'1':'0');
        return ans.toString();
    }

    String shl(String src, String dest) {
        int count=Integer.parseInt(src,2);
        if(count>=32)
            return "00000000000000000000000000000000"; //特判一下超过32位就全是0,没必要继续了
        ans=new StringBuilder(dest);
        while(count>0){
            ans.deleteCharAt(0);
            ans.append('0');
            count--;
        }
        return ans.toString();
    }

    String shr(String src, String dest) {
        int count=Integer.parseInt(src,2);
        if(count>=32)
            return "00000000000000000000000000000000";
        ans=new StringBuilder(dest);
        while(count>0){
            ans.deleteCharAt(ans.length()-1);
            ans.insert(0,'0');
            count--;
        }
        return ans.toString();
    }

    String sal(String src, String dest) {
        return shl(src,dest);
    }

    String sar(String src, String dest) {
        ans=new StringBuilder(shr(src,dest));
        if(dest.charAt(0)=='1'){
            for(int i=0;i0){
            temp=ans.charAt(0);
            ans.deleteCharAt(0);
            ans.append(temp);
            count--;
        }
        return ans.toString();
    }

    String ror(String src, String dest) {
        int count=Integer.parseInt(src);
        count%=32;
        char temp;
        ans=new StringBuilder(dest);
        while(count>0){
            temp=ans.charAt(ans.length()-1);
            ans.deleteCharAt(ans.length()-1);
            ans.insert(0,temp);
            count--;
        }
        return ans.toString();
    }

}

二进制浮点数加减法

首先明确二进制浮点数的加减步骤,减的话则是改改符号,其实都是一个东西。

  • 第一步检查有没有0
  • 第二步看指数,这一步要求小的指数往大的靠(因为要保护主要的部分也就是更大的那个数的精度),这里会将较小的数的significant部分右移,同时判断是否全为0了,是的话说明这个数太小,直接返回大数
  • 如果能成功对齐指数,那么就有符号地相加significant(取决于符号位)
    • significant为0,返回0
    • significant溢出,右移并加指数,检查指数有没有溢出,溢出报错
  • significant未溢出或溢出但指数增加后指数未溢出,则检查是否标准化,是则直接输出,否则要左移significant(要求以1.开头),此时要减指数,并检查指数是否下溢出,溢出报错,成功结束则输出

当然也有另外一个方法,把二进制转成float,加好之后再转回去(好想偷懒)

观察一下FPUTest.java,发现里面其实要处理一些特殊的浮点数,比如N_INFN_NaN之类的,同时我们发现样例的指数部分无论是输入还是结果都是没有8个0(指数以0.开头)的情况的,这可以减少不少工作量。而且注意到这些奇怪的东西都在第一个参数,于是又稍微轻松一点。

顺着思路来的话,遇到的需要处理的点:

  • 取了一个变量e=Integer.parseInt(a.substring(1,9))-Integer.parseInt(b.substring(1,9))作为判断大小以及用来后来右移significant。考虑如果e小于0说明a更小,交换一下ab并把e取相反数,e为0说明指数已经对齐
  • 在位移significant时,注意到我们是1.significant,位移后会是0.0..1significant,所以需要先--e并在临时的sig变量中插入一个1
  • 承上,我们在指数未对齐时已经插入了1,那么最后进行significant的加法时,需要补全小数点左边的数,那么需要一个flag记录指数是否是对齐的,对齐的说明之前并没有插入1,因此此时要插入1,未对齐则说明先前已经插入了1,现在要插入0.
  • significant的有符号加法部分,检查ab的首位符号位面临两种情况
    • 两个同号,可能会溢出一位(很显然不可能溢出两位),后续需要改指数部分
    • 两个异号,要把负数取补码进行加法,如果significant结果为负则需要取补码,把结果的符号位置为负号,显然这时候就不用考虑溢出的问题了

顺着思路,先打了符号不同的情况,不考虑溢出,只补一个符号位再取补码,这时候发现一个样例fpuAddTest2()死活过不去,经过仔细的研究想了很久才发现,需要在后面加4位的保护位,修改之后就过了,但是实际上也没几个符号不同的,所以还要继续写。

经过一些简单的调整把所有的加法都解决了,之前提过减法只是换符号做加法,发现只剩一个样例fpuSubTest4()没有通过,发现这个样例是零,特殊判定一下就好了,再次测试通过。

以下是代码

public class FPU {

    /**
     * compute the float add of (a + b)
     **/
    String add(String a,String b){
        String ret="";
        if(a.substring(1,32).equals("0000000000000000000000000000000"))
            return b;
        else if(b.substring(1,32).equals("0000000000000000000000000000000"))
            return a;
        if(a.substring(1,9).equals("11111111"))
            return a;
        int e=Integer.parseInt(a.substring(1,9),2)-Integer.parseInt(b.substring(1,9),2);
        if(e<0){
            e=-e;
            String temp=a;
            a=b;
            b=temp;
        }
        boolean flag=false;
        StringBuilder sig=new StringBuilder(b.substring(9,32));
        if(e!=0){
            flag=true;
            sig.insert(0,'1');
            e--; //第一次位移把指数给加到significant里
            sig.append("000");//保护位
            for(int i=1;i<=e;i++){
                sig.deleteCharAt(sig.length()-1);
                sig.insert(0,'0');
                if(sig.toString().equals("00000000000000000000000"))
                    return a;
            }
        }
        else
            sig.append("0000");
        sig.insert(0,flag?'0':'1').insert(0,'0');
        StringBuilder tmp=new StringBuilder(a.substring(9,32)).insert(0,'1').insert(0,'0').append("0000");
        if(a.charAt(0)!=b.charAt(0)){
            if(a.charAt(0)=='1')
                tmp=new StringBuilder(ALU.getComplement(tmp.toString()));
            else sig=new StringBuilder(ALU.getComplement(sig.toString()));
        }
        ALU alu=new ALU();
        StringBuilder after=new StringBuilder(alu.add(tmp.toString(),sig.toString()));
        if(a.charAt(0)!=b.charAt(0)){
            if(after.charAt(0)=='1') {
                ret += "1";
                sig=new StringBuilder(ALU.getComplement(after.toString()));
            }
            else{
                ret+="0";
                sig=after;
            }
        }else{
            ret+=a.charAt(0);
            sig=after;
        }
        if(Pattern.matches("^0*$",sig.toString()))
            return "00000000000000000000000000000000"; //特判减后为0
        int del=1-sig.indexOf("1");
        e=Integer.parseInt(a.substring(1,9),2);
        e+=del;
        ret+= String.format("%8s",Integer.toBinaryString(e)).replaceAll(" ","0");
        if(ret.equals("011111111")||ret.equals("111111111"))
            return ret.charAt(0)=='1'?"11111111100000000000000000000000":"01111111100000000000000000000000";//特判无穷
        ret+=sig.substring(sig.indexOf("1")+1,sig.indexOf("1")+24);
        return ret;
    }

    /**
     * compute the float add of (a - b)
     **/
    String sub(String a,String b){
        return add(a,b.charAt(0)=='1'?'0'+b.substring(1,32):'1'+b.substring(1,32));
    }

}

NBCD加减法

上课讲的decimal arithmetic谈到了4位二进制表达一个十进制数时存在的超过“10”的问题,解决方法是检查二进制数的最高位和中间两位,从0开始编号也就是S3&(S2|S1),如果为真则进位(+0110),下一位+0001,依次判断。

而减法则要把A-B写成A+(10N-B),也就是借位,值得注意的是这里的(10N需要一个“取反”操作)

写加法下来发现要注意进位的处理,会有4位的二进制溢出,所以需要扩展到5位。

处理不同号时,为了方便,把负数调换到a上,把开头的0除去方便取反,同时要注意一下这里取反是要ab两数位数的最大位取10的N次幂,所以最终结果检查这个最大位数的高位上是否是0001,是的话就把它置为0000直接返回,不是的话就取反。处理了一些代码上的小bug后,测试通过

public class NBCDU {

    // 模拟寄存器中的进位标志位
    private String CF = "0";

    // 模拟寄存器中的溢出标志位
    private String OF = "0";
    private static String bitAdd(String...args){
        ALU alu=new ALU();
        StringBuilder ret=new StringBuilder(alu.add("0"+args[0],"0"+args[1]));//防止溢出
        if(args.length==4)return ret.toString();
        else if(args.length==3)
            if(args[2].equals("1"))
                ret=new StringBuilder(alu.add(ret.toString(),"00001"));
        int x=ret.charAt(1)-'0',y=ret.charAt(2)-'0',z=ret.charAt(3)-'0';
        if(ret.charAt(0)=='1'){
            return alu.add(ret.toString(),"00110");//1直接保留
        }
        else if((x&(y|z))==1){
            return "1"+alu.add(ret.toString().substring(1,5),"0110"); //说明进位
        }
        return ret.toString();
    }
    private static String reverse(String tar,int bits){
        tar=tar.substring(32-bits*4,32);//取bits位
        StringBuilder reverse=new StringBuilder();
        for(int i=0;i<=tar.length()-4;i+=4){
            String tmp=bitAdd(tar.substring(i,i+4),"0110","0","not_carry")
                    .substring(1,5)
                    .replaceAll("1","2").replaceAll("0","1").replaceAll("2","0");
            if(i==tar.length()-4)
                tmp=bitAdd(tmp,"0001","0","not_carry").substring(1,5);
            reverse.append(tmp);
            //反转时不用处理进位,not_carry
        }
        return "1100"+ String.format("%28s",reverse.toString()).replaceAll(" ","0");  //返回正数化的负数
    }
    private static int getBit(String tar){
        int cnt=0;
        for(int i=4;i<28;i+=4){
            if(tar.substring(i,i+4).equals("0000")) {
                cnt++;
                continue;
            }
            break;
        }
        return 7-cnt;
    }//算位数

    /**
     *
     * @param a A 32-bits NBCD String
     * @param b A 32-bits NBCD String
     * @return a + b
     */
    String add(String a, String b) {
        StringBuilder ret=new StringBuilder();
        if(a.substring(0,4).equals(b.substring(0,4))){  //同号
            String c="0";
            for(int i=31;i>3;i-=4){
                String temp=bitAdd(a.substring(i-3,i+1),b.substring(i-3,i+1),c);
                if(temp.charAt(0)=='1')
                    c="1";
                else c="0";
                ret.insert(0,temp.substring(1,5));
            }
            return ret.insert(0,a.substring(0,4)).toString();
        }
        else{
            String temp;
            if(a.substring(0,4).equals("1100")){
                temp=a;
                a=b;
                b=temp;
            }
            int bits=Math.max(getBit(a),getBit(b));
            temp=add(reverse(a,bits),b);
            if(temp.substring(28-4*bits,32-4*bits).equals("0001"))
                return temp.replaceFirst("0001","0000");
            else
                return reverse(temp,bits).replaceFirst("1100","1101");
        }
    }

    /***
     *
     * @param a A 32-bits NBCD String
     * @param b A 32-bits NBCD String
     * @return b - a
     */
    String sub(String a, String b) {
        return add(a.substring(0,4).equals("1100")?"1101"+a.substring(4,32):"1100"+a.substring(4,32),b);
    }

}

你可能感兴趣的:(Programming 02)