ALU:逻辑算术单元
1 实验要求
在ALU类中实现2个方法,具体如下
1.计算两个32位二进制整数补码真值的和
public DataType add(DataType src, DataType dest)
2.计算两个32位二进制整数补码真值的差,dest表示被减数,src表示减数(即计算dest - src)
public DataType sub(DataType src, DataType dest)
3.在ALU类中实现实现整数的二进制乘法(要求使用布斯乘法实现)。
输入和输出均为32位二进制补码,计算结果直接截取低32位作为最终输出
public DataType mul(DataType src, DataType dest)
2 实验攻略
2.1 代码实现要求
有些同学可能注意到,将传入的参数通过transformer转化为int,再通过整数的加减运算后,将结果重新转化为DataType即可轻松完成实验。在此,我们明确禁止各位采用这种方法来完成本次实验。
2.2 数据封装
从本次实验开始,我们采用统一的类DataType来封装32位的二进制数,包括二进制补码整数、NBCD码与IEEE754浮点数。核心数据结构如下
package util;
import java.util.Collections;
public class DataType {
private final byte[] data = new byte[4];
// 下面代码不需要考虑,只需要知道是32位2进制数的一个包装类即可
public DataType(String dataStr) {
// 目前是大端实现,高位字节存放在低地址
int length = dataStr.length();
if (length == 8 || length == 16 || length == 32) {
dataStr = String.join("", Collections.nCopies(32 - length, "0")) + dataStr;
for (int i = 0; i < 32; i++) {
char temp = dataStr.charAt(i);
if (temp == '0' || temp == '1') {
data[i / 8] |= ((dataStr.charAt(i) - '0') << (7 - i % 8));
} else {
throw new NumberFormatException("Illegal dataStr: " + dataStr);
}
}
} else {
throw new NumberFormatException("Illegal dataStr: " + dataStr);
}
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 4; i++) {
stringBuilder.append(Transformer.intToBinary(String.valueOf(data[i])).substring(24));
}
return stringBuilder.toString();
}
}
采用这样的数据封装将保证DataType类中存放的一定是32位二进制数,并且有利于ALU等运算模块与其他模块的整合。为了方便编码,我们为DataType类提供了构造函数与toString函数,便于DataType对象与String对象之间的转化,具体可阅读DataType类源码。
3实现
3.0 取反加一操作
加一操作,额外使用jinWei
来代表加1
操作,再通过四个if-else
使得逻辑非常的清晰
public static String reverse(String str) {
char[] ansArr = new char[str.length()];
for (int i = 0; i < ansArr.length; i++) {
ansArr[i] = str.charAt(i) == '0' ? '1' : '0';
}
return new String(ansArr);
}
public static String oneAdder(String str) {
char[] ansArr = new char[str.length()];
int jinWei = 1;
for (int i = 31; i >= 0; i--) {
if (str.charAt(i) == '0' && jinWei == 1) {
ansArr[i] = '1';
jinWei = 0;
} else if (str.charAt(i) == '0' && jinWei == 0) {
ansArr[i] = '0';
} else if (str.charAt(i) == '1' && jinWei == 1) {
ansArr[i] = '0';
} else if (str.charAt(i) == '1' && jinWei == 0) {
ansArr[i] = '1';
}
}
return new String(ansArr);
}
3.1 32位补码加法
3.1.1 代码实现
对于加法操作,可以直接进行x+y+jinWei
的操作,而不需要分正负号
/**
* 返回两个二进制整数的和
* dest + src
*
* @param src 32-bits
* @param dest 32-bits
* @return 32-bits
*/
public DataType add(DataType src, DataType dest) {
char[] ansArr = new char[32];
Arrays.fill(ansArr, '0');
char[] srcArr = src.toString().toCharArray();
char[] destArr = dest.toString().toCharArray();
boolean jinWei = false;
String ans;
for (int i = 31; i >= 0; i--) {
if (srcArr[i] == '0' && destArr[i] == '0') {
ansArr[i] = jinWei ? '1' : '0';
jinWei = false;
} else if (srcArr[i] == '1' && destArr[i] == '1') {
ansArr[i] = jinWei ? '1' : '0';
jinWei = true;
} else {
ansArr[i] = jinWei ? '0' : '1';
// jinWei状态不变
}
}
ans = new String(ansArr);
return new DataType(ans);
/**
* 加法直接进行加减,不需要管正负号
*/
}
3.1.2 测试用例
package cpu.alu;
import org.junit.Test;
import util.DataType;
import util.Transformer;
import java.util.Random;
import static org.junit.Assert.assertEquals;
public class ALUAddTest {
private final ALU alu = new ALU();
private DataType src;
private DataType dest;
private DataType result;
@Test
public void AddTest1() {
src = new DataType("10000000000000000000000000000000");
dest = new DataType("00000000000000000000000000000100");
result = alu.add(src, dest);
assertEquals("00000000000000000000000000001000", result.toString());
}
@Test
public void AddTest2() {
src = new DataType("10000000000000000000000000000000");
dest = new DataType("00000000000000000000000000000100");
result = alu.add(src, dest);
assertEquals("00000000000000000000000000001000", result.toString());
}
@Test
public void mulTest() {
for (int i = 0; i < 100000; i++) {
Random random = new Random();
int x = random.nextInt(999999999);
int y = random.nextInt(999999999);
src = new DataType(Transformer.intToBinary(String.valueOf(x)));
dest = new DataType(Transformer.intToBinary(String.valueOf(y)));
result = alu.add(src, dest);
assertEquals(Transformer.intToBinary(String.valueOf(x + y)), result.toString());
}
}
}
3.2 32位补码减法
3.2.1 代码实现
- 对于减法,只需要将被减数取反加1即可,这样子就能够得到对应的相反数的补码,从而实现由减法变为加法。
- 在x86中,减法是
dest-src
/**
* 返回两个二进制整数的差
* dest - src
*/
public DataType sub(DataType src, DataType dest) {
/**
* 减法直接把src取反加1即可,变为加法
*/
return add(dest, new DataType(oneAdder(reverse(src.toString()))));
}
3.2.2 测试用例
package cpu.alu;
import org.junit.Test;
import util.DataType;
import util.Transformer;
import static org.junit.Assert.assertEquals;
public class ALUSubTest {
private final ALU alu = new ALU();
private DataType src;
private DataType dest;
private DataType result;
@Test
public void SubTest0() {
src = new DataType("00000000000000000000000000000001"); //2147483647
dest = new DataType("10000000000000000000000000000001"); // -1
result = alu.sub(src, dest); // -2147483648
assertEquals("10000000000000000000000000000000", result.toString());
}
@Test
public void SubTest1() {
src = new DataType("01111111111111111111111111111111"); //2147483647
dest = new DataType("11111111111111111111111111111111"); // -1
result = alu.sub(src, dest);
assertEquals("10000000000000000000000000000000", result.toString());
}
@Test
public void SubTest2() {
src = new DataType("11111111111111111111111111111000"); // -7
dest = new DataType("00000000000000000000000000000111"); // 7
result = alu.sub(dest, src); //-14
assertEquals("11111111111111111111111111110001", result.toString());
}
@Test
public void SubTest3() {
src = new DataType("00000000000000000000000000000000");
dest = new DataType("10000000000000000000000000000000");
result = alu.sub(dest, src);
assertEquals("10000000000000000000000000000000", result.toString());
}
@Test
public void SubTest4() {
src = new DataType("00000000000000000000000000000000");
dest = new DataType("10000000000000000000000000000001");
result = alu.sub(dest, src);
assertEquals("01111111111111111111111111111111", result.toString());
}
@Test
public void SubTest5() {
src = new DataType("11111010001000011010110101011001"); //-98456231
dest = new DataType("11111111111111101000000001001001"); //-98231
result = alu.sub(dest, src);
assertEquals("11111010001000110010110100010000", result.toString());
}
@Test
public void SubTest6() {
src = new DataType("11111111111111111111110000101000"); //-984
dest = new DataType("00000000000000000000000011100111"); //231
result = alu.sub(dest, src); //-1215 src - dest
assertEquals("11111111111111111111101101000001", result.toString());
}
@Test
public void SubTest7() {
src = new DataType("01111111111111111111111111111111"); // 2147483647
dest = new DataType("10000000000000000000000000000000"); // -2147483648
result = alu.sub(dest, src);
assertEquals("11111111111111111111111111111111", result.toString());
}
@Test
public void SubTest8() {
src = new DataType("01111111111111111111111111111111"); // 2147483647
dest = new DataType("10000000000000000000000000000001"); // -2147483647
result = alu.sub(dest, src);
assertEquals("11111111111111111111111111111110", result.toString());
}
}
3.3 32位补码加法
参考
其中
P
n
P_n
Pn代表的是存储的乘法结果
3.3.1 代码实现
public DataType mul(DataType src, DataType dest) {
char[] srcArr = src.toString().toCharArray();
char[] destArr = dest.toString().toCharArray();
char[] ansArr = new char[32];
char[] retArr = new char[32];
Arrays.fill(retArr, '0');
Arrays.fill(ansArr, '0');
int y0 = 0;
int y1 = 0;
for (int i = 31; i >= 0; i--) {
y1 = destArr[i] - '0';
if (y1 == 1 && y0 == 0) {
// 进行减法,在部分积上减去 被乘数
ansArr = sub(src, new DataType(new String(ansArr))).toString().toCharArray();
} else if (y1 == 0 && y0 == 1) {
ansArr = add(src, new DataType(new String(ansArr))).toString().toCharArray();
}
retArr[i] = ansArr[31];
ansArr = rightMove(ansArr);
y0 = y1;
}
return new DataType(new String(retArr));
}
public char[] rightMove(char[] str) {
for (int i = str.length - 1; i >= 1; i--) {
str[i] = str[i - 1];
}
return str;
}
需要注意的是:
- 32*32位实际上得到的是64位乘法结果,因此我们需要取低32位作为结果
- 在这里,我们分别使用
ansArr
和retArr
两个数组进行存储 - 因为实际上计算时,是高32位进行加减操作,因此我们用
ansArr
进行加减操作 - 同时,用
retArr
来记录低32位,从而得到结果。
3.3.2 测试用例
package cpu.alu;
import org.junit.Test;
import util.DataType;
import util.Transformer;
import static org.junit.Assert.assertEquals;
public class ALUMulTest {
private final ALU alu = new ALU();
private DataType src;
private DataType dest;
private DataType result;
@Test
public void MulTest1() {
// 11111111111111111111111111111101
src = new DataType("00000000000000000000000000000110");
dest = new DataType("11111111111111111111111111111101");
result = alu.mul(src, dest);
assertEquals("11111111111111111111111111101110", result.toString());
}
@Test
public void MulTest2() {
// 11111111111111111111111111111101
src = new DataType("00000000000000000000000000000010");
dest = new DataType("00000000000000000000000000000010");
result = alu.mul(src, dest);
assertEquals("00000000000000000000000000000100", result.toString());
}
}