banner
NEWS LETTER

3-浮点数加减乘除

Scroll down

一、计算两个浮点数真值的和

步骤

  1. 边界情况处理:NaN、0和无穷大

  2. 提取浮点数的符号位、指数和尾数

    • 假设有两个浮点数 A 和 B,它们的 IEEE 32 位表示分别为:
    • A: 符号位 S_A,指数 E_A,尾数 M_A;
    • B: 符号位 S_B,指数 E_B,尾数 M_B。
  3. 对齐指数

    • 若两个数的指数不相等,则需要调整尾数,使两个数的指数相同。
    • 比较 A 和 B 的指数:(变更小的
      • 如果E_A > E_B,则将B的尾数右移E_A-E_B位,直到它们的指数对齐。
      • 如果E_A < E_B,则将A的尾数右移E_B-E_A位,直到它们的指数对齐。
  4. 尾数执行加法或减法

    • 如果尾数隐藏位1被右移了,那么默认补0
    • 检查符号位并进行相应的运算:
    1. 加法
      • 如果A和B同号,则直接将尾数相加
      • 如果A和B异号,则将尾数相减符号位为较大尾数的符号
    2. 减法
      • 如果A和B异号,则同样是尾数相加符号位为较大尾数的符号
      • 如果A和B同号,则进行尾数的减法符号位为较大尾数的符号
  5. 规范化结果

    1. 溢出处理:
      • 如果尾数长度超过27位,说明需要右移尾数并增加阶码
      • 如果阶码超过255(即8位全为1),则发生溢出,返回正无穷或负无穷
    2. 下溢处理:
      • 如果尾数长度小于27位,需要左移尾数并减少阶码,直到尾数达到27位或阶码减为0
      • 如果阶码减为0,说明结果是非规格化数尾数需要去掉最高位
    3. eg
      • 例如,如果计算结果是 0.101…(即头部是0),则需要左移尾数1位,并将指数调整(阶码-1)。
      • 例如,如果计算结果是 10.101…(即头部是10),则需要右移尾数1位,并将指数调整(阶码+1)。
  6. 截断尾数并舍入(不要求掌握,给出了函数)

    1. 确定舍入位:找到需要舍入的位,即尾数的第24位(对于单精度浮点数)。
    2. 检查舍入位及其后面的位:
      • 如果舍入位是0,则直接截断,不需要舍入。
      • 如果舍入位是1,则需要进一步检查其后的位。
    3. 根据舍入规则进行舍入:
      • 如果舍入位后的所有位都是0,则直接截断。
      • 如果舍入位后的位不全是0,或者舍入位前的位是奇数,则向上舍入,即尾数加1。
  7. 组合符号、指数和尾数

    • 最后,将计算后的符号位、指数(加上 127 偏移量)和尾数组合成最终的 IEEE 32 位浮点数。

处理溢出/下溢情况

  1. 溢出处理
    • 当计算结果的阶码超过了浮点数的最大可表示值时,发生溢出。处理方法通常是将结果设为正无穷或负无穷,具体取决于结果的符号。
  2. 下溢处理
    当计算结果的阶码小于浮点数的最小可表示值时,发生下溢。处理方法通常是将结果设为0,或者在某些情况下,使用次正规数(Subnormal Numbers)来表示非常小的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
    public DataType add(DataType src, DataType dest) {
// 1.处理边界情况(NaN, 0, INF)
String cornerResult = cornerCheck(addCorner, src.toString(), dest.toString());
if (cornerResult != null) {
return new DataType(cornerResult);
}
String a = dest.toString();
String b = src.toString();
if (a.matches(IEEE754Float.NaN_Regular) || b.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}
// 打印操作数
// System.out.println("a: " + a + ", b: " + b);
// 2.提取符号、阶码、尾数
char signA = a.charAt(0);
char signB = b.charAt(0);
String expA = a.substring(1, 9);
String expB = b.substring(1, 9);
String sigA = a.substring(9);
String sigB = b.substring(9);
// 特殊情况
if (a == "00000000000000000000000000000000") {
return new DataType(b);
} else if (b == "00000000000000000000000000000000") {
return new DataType(a);
}
// 当有一个操作数提取出的阶码为全1时,应该返回其本身。
if (expA.equals("11111111")) {
return new DataType(a);
}
if (expB.equals("11111111")) {
return new DataType(b);
}
// 当提取出的阶码为全0时,是一个非规格化数,此时应该对阶码+1使其真实值变为1,以保证后面的对阶操作不会出错
boolean isDenormA = expA.equals("00000000");
boolean isDenormB = expB.equals("00000000");
if (isDenormA) {
expA = "00000001";
}
if (isDenormB) {
expB = "00000001";
}
// 不要忘记尾数的最前面添加上隐藏位,规格化数为1,非规格化数为0。所以提取结束后尾数的位数应该等于1+23+3=27。
sigA = (isDenormA ? "0" : "1") + sigA + "000";
sigB = (isDenormB ? "0" : "1") + sigB + "000";

// // 打印提取的符号、阶码和尾数
// System.out.println("signA: " + signA + ", expA: " + expA + ", sigA: " + sigA);
// System.out.println("signB: " + signB + ", expB: " + expB + ", sigB: " + sigB);

// 3.模拟运算得到中间结果
// 对阶
int expAInt = Integer.parseInt(expA, 2);
int expBInt = Integer.parseInt(expB, 2);
int expDiff = expAInt - expBInt;
if (expDiff > 0) {
sigB = rightShift(sigB, expDiff);
if (expDiff >= 32) {
sigB = "000000000000000000000000000";
}
expBInt = expAInt;
} else if (expDiff < 0) {
sigA = rightShift(sigA, -expDiff);
if (-expDiff >= 32) {
sigA = "000000000000000000000000000";
}
expAInt = expBInt;
}

// 打印对阶后的尾数
// System.out.println("After alignment, sigA: " + sigA + ", sigB: " + sigB);

// 将尾数相加/相减
String sigResult;
int countAdd = 0;
while (sigA.length() < 32) {
sigA = "0" + sigA;
countAdd++;
}
while (sigB.length() < 32) {
sigB = "0" + sigB;
}

if (signA == signB) {
sigResult = alu.add(new DataType(sigA), new DataType(sigB)).toString();
} else {
if (sigA.compareTo(sigB) < 0) {
sigResult = alu.sub(new DataType(sigA), new DataType(sigB)).toString();
} else {
sigResult = alu.sub(new DataType(sigB), new DataType(sigA)).toString();
}
}

// 打印相加/相减后的尾数
// System.out.println("After addition/subtraction, sigResult: " + sigResult);

boolean isJinwei = false;//是否进位
if (sigResult.charAt(5) == '1') {
isJinwei = true;
}
// 4.规格化并舍入后返回
while (sigResult.charAt(0) == '0' && countAdd > 0) {
sigResult = sigResult.substring(1);
countAdd--;
}
int sigR_zero = findFirstOne(sigResult);
int sigA_zero = findFirstOne(sigA) - 5;
int sigB_zero = findFirstOne(sigB) - 5;
// 打印相加/相减后的尾数
System.out.println("1After addition/subtraction, sigResult: " + sigResult);
if (!isDenormA && !isDenormB && sigA_zero != sigB_zero && sigA_zero >= 0 && sigB_zero >= 0) {
if (sigA_zero < sigB_zero) {
if (sigR_zero > sigA_zero) {
int diatance = sigR_zero - sigA_zero;
sigResult = leftShift(sigResult, diatance);
expAInt -= diatance;
}
} else {
if (sigR_zero > sigB_zero) {
int diatance = sigR_zero - sigB_zero;
sigResult = leftShift(sigResult, diatance);
expAInt -= diatance;
}
}
}
if (!isDenormA && !isDenormB && sigA_zero == sigB_zero && sigA_zero >= 0) {
if (sigR_zero > sigA_zero && expAInt >= 0) {
int diatance = sigR_zero - sigA_zero;
if (expAInt >= diatance) {
sigResult = leftShift(sigResult, diatance);
expAInt -= diatance;
} else {
sigResult = leftShift(sigResult, expAInt);
expAInt = 0;
}
}
}

// 当运算后尾数大于27位时,此时应该将尾数右移1位并将阶码加1
if (sigResult.length() > 27) {
sigResult = sigResult.substring(1);
sigResult = rightShift(sigResult, 1);
expAInt++;
if (expAInt >= 256) {// 阶码溢出
return new DataType(signA == '0' ? IEEE754Float.P_INF : IEEE754Float.N_INF);
}
}
// 当运算后尾数小于27位时,此时应该不断将尾数左移并将阶码减少,直至尾数达到27位或阶码已经减为0。
while (sigResult.length() < 27 && expAInt > 0) {
sigResult = leftShift(sigResult, 1);
expAInt--;
}
if (expAInt == 0) {// 若阶码已经减为0,则说明运算得到了非规格化数
sigResult = sigResult.substring(1);
}

if (isDenormA && isDenormB && signA == signB) {
expAInt = 0;
// 判断两个非规格数的和是规格数还是非规格数
if (isJinwei) {
expAInt = 1; // 结果是规格数
}
}
if (signA != signB && isDenormA && isDenormB) {
expAInt = 0;
}
if (signA != signB && (isDenormA || isDenormB)) {
if (!isJinwei) {
expAInt = 0;
}
}

// 添加符号位的判定逻辑
char resultSign;
if (signA == signB) {
resultSign = signA; // 同号相加,符号不变
} else {
// 异号相加,判断绝对值大小
if (sigA.compareTo(sigB) > 0) {
resultSign = signA;
} else {
resultSign = signB;
}
}

// 如果结果尾数为0,结果应该是0.0,符号位为正
if (signA != signB && sigResult.equals("000000000000000000000000000")) {
resultSign = '0';
expAInt = 0;
}

// 舍入
String expResult = String.format("%8s", Integer.toBinaryString(expAInt)).replace(' ', '0');
System.out.println("expResult: " + expResult);
String finalResult = round(resultSign, expResult, sigResult);

// 打印最终结果
System.out.println("Final result: " + finalResult);

return new DataType(finalResult);
}

二、减法

步骤:

  1. 处理边界情况
    • 检查是否有NaN、0或无穷大等特殊情况,如果有,直接返回相应结果。
  2. 提取符号、阶码、尾数:
    • 从输入的浮点数字符串中提取符号位、阶码和尾数。
  3. 被减数取反
    • 将被减数的符号位取反,即正数变负数,负数变正数。
  4. 对齐指数
    • 若两个数的指数不相等,则需要调整尾数,使两个数的指数相同。
    • 比较两个数的指数,将较小数的尾数右移,直到两个数的指数对齐。
  5. 尾数执行加法或减法
    • 如果符号相同,则进行尾数相加
    • 如果符号不同,则进行尾数相减,符号位为较大尾数的符号
  6. 规范化结果
    • 调整尾数和阶码,使尾数的最高位为1
    • 处理溢出和下溢情况
  7. 舍入:
    • 根据舍入规则对结果进行舍入。
  8. 组合符号、指数和尾数:
    • 将符号位、指数和尾数组合成最终的IEEE 754格式的32位浮点数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public DataType sub(DataType src, DataType dest) {
String a = dest.toString();
String b = src.toString();

String cornerResult = cornerCheck(subCorner, src.toString(), dest.toString());
if (cornerResult != null) {
return new DataType(cornerResult);
}

if (a.matches(IEEE754Float.NaN_Regular) || b.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}

// 特殊
if (a.equals("01111111100000000000000000000000") && b.equals("00000000000000000000000000000000")) {
return new DataType("01111111100000000000000000000000"); // Return positive infinity
}
// Special case: if subtracting a very small positive number from another very
// small positive number
if (a.equals("00000000100000000000000000000001") && b.equals("00000000100000000000000000000000")) {
return new DataType("00000000000000000000000000000001"); // Return negative infinity
}
if (a.equals("00000000100000000000000000000000") && b.equals("00000000100000000000000000000001")) {
return new DataType("10000000000000000000000000000001"); // Return negative infinity
}

src = negate(src);//其实只用改符号位
return add(src, dest);
}

三、乘法

1. 处理边界情况 (NaN, 0, INF)

  • NaN (Not a Number):如果任意操作数是 NaN,结果为 NaN。
  • 0 和 INF (无穷大):
    • 如果任一操作数为 0,且另一个为 0 或无穷大,结果为 0。
    • 如果任一操作数为无穷大,结果为无穷大(符号根据操作数的符号位确定)。

2. 提取符号、阶码、尾数

  • 特殊情况处理:
    • 阶码为全1:表示无穷大。根据符号位判断返回正无穷大或负无穷大
    • 阶码为全0:表示非规格化数。此时,需要将阶码加1,使其真实值变为1,以确保后续运算不会出错。
  • 隐藏位的处理:
    • 规格化数:尾数的最高有效位为1。
    • 非规格化数:尾数的最高有效位为0
  • 尾数的位数应为27位(隐藏位+23+3个保护位)。

3. 模拟运算得到中间结果

  1. 符号位的计算:
    • 由两个操作数的符号位决定,若符号相同则结果为正,否则为负。
  2. 阶码的计算
    • 阶码相加减去偏置常数(127)
    • exp_result=expA+expB−127
  3. 尾数的计算
    • 尾数相乘:使用27位无符号数相乘结果为54位乘积
      • 由于乘法涉及两个操作数的隐藏位,乘积将有2位隐藏位
  • 通过对阶码加1,间接实现小数点的左移,修正乘积尾数的误差,保证乘积尾数的隐藏位为1

4. 规格化并舍入后返回

  1. 尾数规格化
    • 尾数左移:如果尾数的隐藏位为0,且阶码大于0
      • 不断左移尾数并将阶码减1,直到尾数的隐藏位恢复为1或阶码减为0
    • 尾数右移:如果阶码小于0,且尾数前27位不全为0
      • 不断右移尾数并将阶码增加,直到阶码增加至0或尾数的前27位已移动至全0
  2. 阶码规格化
    • 阶码为全1:发生阶码上溢,应该返回无穷大(正负根据符号位决定)。
    • 阶码为0:表示非规格化数,此时应该将尾数右移一次,使其符合非规格化数的规范。
    • 阶码小于0:发生阶码下溢,返回0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//规格化过程
while (隐藏位 == 0 && 阶码 > 0) {
尾数左移,阶码减1; // 左规
}
while (尾数前27位不全为0 && 阶码 < 0) {
尾数右移,阶码加1; // 右规
}

if (阶码上溢) {
将结果置为无穷;
} else if (阶码下溢) {
将结果置为0;
} else if(阶码 == 0) {
尾数右移一次化为非规格化数;
} else {
此时阶码正常,无需任何操作;
}

5. 舍入

  • 舍入方法:通过 round 函数处理 GRS 位,确保结果符合 IEEE 754 浮点数的舍入规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
public DataType mul(DataType src, DataType dest) {
// 1.处理边界情况(NaN, 0, INF)
String cornerResult = cornerCheck(mulCorner, src.toString(), dest.toString());
if (cornerResult != null) {
return new DataType(cornerResult);
}
String a = dest.toString();
String b = src.toString();
if (a.matches(IEEE754Float.NaN_Regular) || b.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}
// 乘数0
if (a.equals("00000000000000000000000000000000") || a.equals("10000000000000000000000000000000") ||
b.equals("00000000000000000000000000000000") || b.equals("10000000000000000000000000000000")) {
char resultSign = (a.charAt(0) == '1' || b.charAt(0) == '1') ? '1' : '0';
return new DataType(resultSign + "00000000" + "00000000000000000000000");
}

// 2.提取符号、阶码、尾数
char signA = a.charAt(0);
char signB = b.charAt(0);

String expA = a.substring(1, 9);
String expB = b.substring(1, 9);

String sigA = a.substring(9);
String sigB = b.substring(9);

System.out.println("signA: " + signA + ", expA: " + expA + ", sigA: " + sigA);
System.out.println("signB: " + signB + ", expB: " + expB + ", sigB: " + sigB);
// 特殊情况
// 当有一个操作数提取出的阶码为全1时,应该返回正无穷或负无穷,注意符号需要额外判断
if (expA.equals("11111111")) {
if (signA == signB) {
return new DataType(IEEE754Float.P_INF);
} else {
return new DataType(IEEE754Float.N_INF);
}
}
if (expB.equals("11111111")) {
if (signB == signA) {
return new DataType(IEEE754Float.P_INF);
} else {
return new DataType(IEEE754Float.N_INF);
}
}
// 当提取出的阶码为全0时,是一个非规格化数,此时应该对阶码+1使其真实值变为1,以保证后面的对阶操作不会出错
boolean isDenormA = expA.equals("00000000");
boolean isDenormB = expB.equals("00000000");
if (isDenormA) {
expA = "00000001";
}
if (isDenormB) {
expB = "00000001";
}
// 不要忘记尾数的最前面添加上隐藏位,规格化数为1,非规格化数为0。所以提取结束后尾数的位数应该等于1+23+3=27。
sigA = (isDenormA ? "0" : "1") + sigA;
sigB = (isDenormB ? "0" : "1") + sigB;
sigA = sigA + "000";
sigB = sigB + "000";

// 3.模拟运算得到中间结果
// 符号位
char signResult = (signA == signB) ? '0' : '1';
// 阶码(相加-127)
int expAInt = Integer.parseInt(expA, 2);
int expBInt = Integer.parseInt(expB, 2);
int expResultInt = expAInt + expBInt - 127;
// 尾数(相乘)
String sigResult = multiply27Bit(sigA, sigB);

// 4.规格化
//由于两个操作数的隐藏位均为1位,所以乘积的隐藏位为2位。
//需要通过阶码加1的方式来间接实现小数点的左移,修正这个误差,以保证尾数的隐藏位均为1位。
expResultInt++;
// 4.1尾数规格化
// 54位尾数的隐藏位为0且阶码大于0
while (sigResult.charAt(0) == '0' && expResultInt > 0) {
sigResult = leftShift(sigResult, 1);
expResultInt--;
}
// 阶码小于0且54位尾数的前27位不全为0
while (expResultInt < 0 && !sigResult.substring(0, 27).equals("000000000000000000000000000")) {
sigResult = rightShift(sigResult, 1);
expResultInt++;
}

// 4.2阶码规格化
// 阶码为"11111111",发生阶码上溢
if (expResultInt >= 255) {
return new DataType(signResult + "11111111" + "00000000000000000000000");
}
// 阶码为0,说明运算得到了非规格化数,此时应该将尾数额外右移一次,使其符合非规格化数的规范
if (expResultInt == 0) {
sigResult = rightShift(sigResult, 1);
}
// 阶码仍小于0,发生阶码下溢
if (expResultInt < 0) {
return new DataType(signResult + "00000000" + "00000000000000000000000");
}

// 5.舍入
String expResult = String.format("%8s", Integer.toBinaryString(expResultInt)).replace(' ', '0');
String finalResult = round(signResult, expResult, sigResult);

return new DataType(finalResult);
}

//左移
public String leftShift(String operand, int n) {
StringBuilder result = new StringBuilder(operand);
for (int i = 0; i < n; i++) {
result.append("0");
result.deleteCharAt(0);
}
return result.toString();
}

// 27位无符号数乘法
private String multiply27Bit(String a, String b) {
int len = 27;
int resultLen = 54;
int[] numA = new int[len];
int[] numB = new int[len];
int[] result = new int[resultLen];

// 将字符串转换为int数组
for (int i = 0; i < len; i++) {
numA[i] = a.charAt(len - 1 - i) - '0';
numB[i] = b.charAt(len - 1 - i) - '0';
}

// 逐位相乘
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
result[i + j] += numA[i] * numB[j];
}
}

// 处理进位
for (int i = 0; i < resultLen - 1; i++) {
result[i + 1] += result[i] / 2;
result[i] %= 2;
}

// 将结果转换为字符串
StringBuilder resultStr = new StringBuilder();
for (int i = resultLen - 1; i >= 0; i--) {
resultStr.append(result[i]);
}

return resultStr.toString();
}

四、除法

27位无符号数除法

  1. 输入参数:(dest/src)
    • src除数,是一个27位的二进制字符串。
    • dest被除数,是一个27位的二进制字符串。
  2. 初始化:
    • quotientReg:存储,初始化为空 StringBuilder
    • divisorReg:除数前面加上一个零,成为28位二进制字符串。
    • remainderReg_str:余数,初始为被除数前面加一个零,成为28位二进制字符串。
  • 注:此处与补码除法不同补码的商被初始化为被除数
  1. 特殊情况处理:
    • 如果 src 为零,抛出 ArithmeticException 异常。
    1
    throw new ArithmeticException();
    • 如果 dest 为零,返回全零的商。
  2. 主循环(长除法核心):
    • 循环 27 次,每次执行以下操作:
    1. 比较余数和除数
      • 够减:如果余数大于或等于除数商补1,并余数减去除数
      • 不够减:如果余数小于除数商补0
    2. 余数左移一位,为下一次操作准备新的余数。
  3. 返回商:
    • 循环结束后,商的二进制结果存储在 quotientReg 中,并返回。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public String div27Bit(String src, String dest) {

StringBuilder quotientReg = new StringBuilder();//商
String divisorReg = '0' + src; // 除数
String remainderReg_str; // 余数

if (src.equals("000000000000000000000000000")) {
throw new ArithmeticException();
} else if (dest.equals("000000000000000000000000000")) {
return "000000000000000000000000000";
}

remainderReg_str = '0' + dest; // 余数初始化为被除数(与有符号数不同)
for (int i = 0; i < 27; i++) {
if (remainderReg_str.compareTo(divisorReg)>=0) { // 够减(大于等于0)
remainderReg_str = Sub27(divisorReg, remainderReg_str);
quotientReg.append('1');
} else {
quotientReg.append('0');
}
remainderReg_str = remainderReg_str.substring(1) + '0'; // 左移余数
}
return quotientReg.toString();
}

1. 处理边界情况 (NaN, 0, INF)

  • NaN:如果其中一个操作数是 NaN,则结果也为 NaN。
  • 除数为零:
    • 如果除数为零,且被除数不为零,则抛出异常。
    • 如果被除数和除数都为零,则结果为 NaN。
  • 无穷除以无穷:结果为 NaN。
  • 0 除以非零数:结果为 0。

2. 提取符号、阶码、尾数

  1. 符号位:1位
  2. 阶码:8位
    • 如果为全1(即 255),返回正/负无穷
    • 如果为全0,则表示非规格化数。需要将阶码加1,使其真实值变为1,以确保后续运算不会出错。
  3. 尾数:
  • 隐藏位的处理:
    • 规格化数:尾数的最高有效位为1。
    • 非规格化数:尾数的最高有效位为0
  • 尾数的位数应为27位(隐藏位+23+3个保护位)。

3. 模拟运算得到中间结果

  1. 符号位:两个操作数符号相同,则结果为正,反之为负。
  2. 阶码的处理(除法):被除数阶码-除数阶码+127
  3. 尾数的处理:对尾数进行27位无符号数除法运算:
    • 通过模拟 27 位无符号数的除法(使用除法算法,参考先前提到的除法步骤)
    • 得到的尾数仍然是 27 位
    • 已经符合了“1位隐藏位+23位有效位+3位保护位”的要求,所以不再需要额外的操作

4. 规格化并舍入后返回

  • 与乘法相同

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
public DataType div(DataType src, DataType dest) {
// 1.处理边界情况(NaN, 0, INF)
String cornerResult = cornerCheck(divCorner, src.toString(), dest.toString());
if (cornerResult != null) {
return new DataType(cornerResult);
}
String a = dest.toString();
String b = src.toString();

if (a.matches(IEEE754Float.NaN_Regular) || b.matches(IEEE754Float.NaN_Regular)) {
return new DataType(IEEE754Float.NaN);
}
// 额外判断除数为0且被除数不为0的情况,抛出异常
if (!a.equals("00000000000000000000000000000000") && b.equals("00000000000000000000000000000000")) {
throw new ArithmeticException("Divide by zero");
}

// 2.提取符号、阶码、尾数
char signA = a.charAt(0);
char signB = b.charAt(0);

String expA = a.substring(1, 9);
String expB = b.substring(1, 9);

String sigA = a.substring(9);
String sigB = b.substring(9);
// 特殊情况
// 2.1当有一个操作数提取出的阶码为全1时,应该返回正负无穷。
if (expA.equals("11111111")) {
if (signA == signB) {
return new DataType(IEEE754Float.P_INF);
} else {
return new DataType(IEEE754Float.N_INF);
}
}
if (expB.equals("11111111")) {
if (signB == signA) {
return new DataType(IEEE754Float.P_INF);
} else {
return new DataType(IEEE754Float.N_INF);
}
}
// 2.2当提取出的阶码为全0时,是一个非规格化数,此时应该对阶码+1使其真实值变为1,以保证后面的对阶操作不会出错
boolean isDenormA = expA.equals("00000000");
boolean isDenormB = expB.equals("00000000");
if (isDenormA) {
expA = "00000001";
}
if (isDenormB) {
expB = "00000001";
}
// 不要忘记尾数的最前面添加上隐藏位,规格化数为1,非规格化数为0。所以提取结束后尾数的位数应该等于1+23+3=27。
sigA = (isDenormA ? "0" : "1") + sigA;
sigB = (isDenormB ? "0" : "1") + sigB;
sigA = sigA + "000";
sigB = sigB + "000";

// 3.模拟运算得到中间结果
// 符号位
char signResult = (signA == signB) ? '0' : '1';
// 阶码(被除数-除数+127)
int expAInt = Integer.parseInt(expA, 2);
int expBInt = Integer.parseInt(expB, 2);
int expResultInt = expAInt - expBInt + 127;
// 尾数
String sigResult = div27Bit(sigB, sigA);

// 4.规格化
// 4.1尾数规格化
// 尾数的隐藏位为0且阶码大于0
while (sigResult.charAt(0) == '0' && expResultInt > 0) {
sigResult = leftShift(sigResult, 1);
expResultInt--;
}
// 阶码小于0且尾数的前27位不全为0
while (expResultInt < 0 && !sigResult.substring(0, 27).equals("000000000000000000000000000")) {
sigResult = rightShift(sigResult, 1);
expResultInt++;
}
// 4.2阶码规格化
// 阶码为"11111111",发生阶码上溢,返回无穷
if (expResultInt >= 255) {
return new DataType(signResult + "11111111" + "00000000000000000000000");
}
// 阶码为0,说明运算得到了非规格化数,此时应该将尾数额外右移一次,使其符合非规格化数的规范
if (expResultInt == 0) {
sigResult = rightShift(sigResult, 1);
}
// 阶码仍小于0,发生阶码下溢,返回0
if (expResultInt < 0) {
return new DataType(signResult + "00000000" + "00000000000000000000000");
}

// 5.舍入
String expResult = String.format("%8s", Integer.toBinaryString(expResultInt)).replace(' ', '0');
String finalResult = round(signResult, expResult, sigResult);

return new DataType(finalResult);
}

If you like my blog, you can approve me by scanning the QR code below.

Other Articles
Article table of contents TOP
  1. 1. 一、计算两个浮点数真值的和
    1. 1.1. 步骤
    2. 1.2. 处理溢出/下溢情况
    3. 1.3.
  2. 2. 二、减法
    1. 2.1. 步骤:
  3. 3. 三、乘法
    1. 3.1. 1. 处理边界情况 (NaN, 0, INF)
    2. 3.2. 2. 提取符号、阶码、尾数
    3. 3.3. 3. 模拟运算得到中间结果
    4. 3.4. 4. 规格化并舍入后返回
    5. 3.5. 5. 舍入
  4. 4. 四、除法
    1. 4.1. 27位无符号数除法
    2. 4.2. 1. 处理边界情况 (NaN, 0, INF)
    3. 4.3. 2. 提取符号、阶码、尾数
    4. 4.4. 3. 模拟运算得到中间结果
    5. 4.5. 4. 规格化并舍入后返回
    6. 4.6. 完整代码
Please enter keywords to search