浮点数的支持

我们已经在PA3中把仙剑奇侠传运行起来了, 但却不能战斗, 这是因为还有一些浮点数相关的工作需要处理. 现在到了处理的时候了. 要在NEMU中实现浮点指令也不是不可能的事情. 但实现浮点指令需要涉及x87架构的很多细节, 根据KISS法则, 我们选择了一种更简单的方式: 我们通过整数来模拟实数的运算, 这样的方法叫binary scalingarrow-up-right.

我们先来说明如何用一个32位整数来表示一个实数. 为了方便叙述, 我们称用binary scaling方法表示的实数的类型为FLOAT. 我们约定最高位为符号位, 接下来的15位表示整数部分, 低16位表示小数部分, 即约定小数点在第15和第16位之间(从第0位开始). 从这个约定可以看到, FLOAT类型其实是实数的一种定点表示.

31  30                  16                    0
+----+-------------------+--------------------+
|sign|      integer      |      fraction      |
+----+-------------------+--------------------+

这样, 对于一个实数a, 它的FLOAT类型表示A = a * 2^16(截断结果的小数部分). 例如实数1.25.6FLOAT类型来近似表示, 就是

1.2 * 2^16 = 78643 = 0x13333
+----+-------------------+--------------------+
| 0  |         1         |        3333        |
+----+-------------------+--------------------+


5.6 * 2^16 = 367001 = 0x59999
+----+-------------------+--------------------+
| 0  |         5         |        9999        |
+----+-------------------+--------------------+

而实际上, 这两个FLOAT类型数据表示的数是:

0x13333 / 2^16 = 1.19999695
0x59999 / 2^16 = 5.59999084

对于负实数, 我们用相应正数的相反数来表示, 例如-1.2FLOAT类型表示为:

接下来我们来考虑FLOAT类型的常见运算, 假设实数a, bFLOAT类型表示分别为A, B.

  • 由于我们使用整数来表示FLOAT类型, FLOAT类型的加法可以直接用整数加法来进行:

  • 由于我们使用补码的方式来表示FLOAT类型数据, 因此FLOAT类型的减法用整数减法来进行.

  • FLOAT类型的乘除法和加减法就不一样了:

    也就是说, 直接把两个FLOAT数据相乘得到的结果并不等于相应的两个浮点数乘积的FLOAT表示.

    为了得到正确的结果, 我们需要对相乘的结果进行调整: 只要将结果除以2^16, 就能得出正确的结果了.

    除法也需要对结果进行调整, 至于如何调整, 当然难不倒聪明的你啦.

  • 如果把A = a * 2^16看成一个映射, 那么在这个映射的作用下, 关系运算是保序的,

    a <= b当且仅当A <= B, 故FLOAT类型的关系运算可以用整数的关系运算来进行.

有了这些结论, 要用FLOAT类型来模拟实数运算就很方便了. 除了乘除法需要额外实现之外, 其余运算都可以直接使用相应的整数运算来进行. 例如

FLOAT类型来模拟就是

其中还引入了一些类型转换函数来实现和FLOAT相关的类型转换.

仙剑奇侠传的框架代码已经用FLOAT类型对浮点数进行了相应的处理. 你还需要实现一些和FLOAT类型相关的函数:

其中F_mul_int()F_div_int()用于计算一个FLOAT类型数据和一个整型数据的积/商, 这两种特殊情况可以快速计算出结果, 不需要将整型数据先转化成FLOAT类型再进行运算.

事实上, 我们并没有考虑计算结果溢出的情况, 不过仙剑奇侠传中的浮点数结果都可以在FLOAT类型中表示, 所以你可以不关心溢出的问题. 如果你不放心, 你可以在上述函数的实现中插入assertion来捕捉溢出错误.

Last updated