浮点数的支持
Last updated
Was this helpful?
Last updated
Was this helpful?
我们已经在PA3中把仙剑奇侠传运行起来了, 但却不能战斗, 这是因为还有一些浮点数相关的工作需要处理. 现在到了处理的时候了. 要在NEMU中实现浮点指令也不是不可能的事情. 但实现浮点指令需要涉及x87架构的很多细节, 根据KISS法则, 我们选择了一种更简单的方式: 我们通过整数来模拟实数的运算, 这样的方法叫.
我们先来说明如何用一个32位整数来表示一个实数. 为了方便叙述, 我们称用binary scaling方法表示的实数的类型为FLOAT
. 我们约定最高位为符号位, 接下来的15位表示整数部分, 低16位表示小数部分, 即约定小数点在第15和第16位之间(从第0位开始). 从这个约定可以看到, FLOAT
类型其实是实数的一种定点表示.
这样, 对于一个实数a
, 它的FLOAT
类型表示A = a * 2^16
(截断结果的小数部分). 例如实数1.2
和5.6
用FLOAT
类型来近似表示, 就是
而实际上, 这两个FLOAT
类型数据表示的数是:
对于负实数, 我们用相应正数的相反数来表示, 例如-1.2
的FLOAT
类型表示为:
接下来我们来考虑FLOAT
类型的常见运算, 假设实数a
, b
的FLOAT
类型表示分别为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来捕捉溢出错误.