具有32位寄存器的双精度整数减法(MIPS)

时间:2010-10-15 08:12:49

标签: mips unsigned signed integer-overflow double-precision

我正在学习计算机算术。我使用的书(Patterson和Hennessey)列出了以下问题。

  

写mips代码进行双重操作   精度整数减法   64位数据。假设第一个操作数   在寄存器$ t4(hi)和   $ t5(lo),$ t6(hi)和   $ T7(LO)。

我对答案的解决方案是

sub  $t3, $t5, $t7   # Subtract lo parts of operands. t3 = t5 - t7
sltu $t2, $t5, $t7   # If the lo part of the 1st operand is less than the 2nd,
                     #   it means a borrow must be made from the hi part
add  $t6, $t6, $t2   # Simulate the borrow of the msb-of-low from lsb-of-high
sub  $t2, $t4, $t6   # Subtract the hi's. t2 = t4 - t6

然而,作者给出了解决此问题的方法如下

对于有符号的双精度整数,

subu $t3, $t5, $t7   
sltu $t2, $t5, $t7  
add  $t6, $t6, $t2
sub  $t2, $t4, $t6

对于无符号双精度整数,

subu $t3, $t5, $t7   
sltu $t2, $t5, $t7  
addu $t6, $t6, $t2
subu $t2, $t4, $t6

我对sub/addsubu/addu操作差异的理解是溢出异常是在sub/add中生成的,而不是在subu/addu中生成的。 sub/addsubu/addu都减去/添加操作数的位,对有符号或无符号的操作数的解释与sltsltu指令不同,对结果没有影响

问题1
我从作者推断出解决方案正在处理溢出检测,而我在解决方案中没有想到相同的问题。我对吗?还有其他我想念的东西吗?

问题2
假设我的上述推论是正确的,为什么在使用addusubu减去无符号双精度的情况下,作者提供的解决方案会关闭溢出检测?

1 个答案:

答案 0 :(得分:6)

对于加法和减法,除了溢出的概念之外,有符号和无符号操作数之间没有区别。当结果的数值与您获得的位序列的解释不匹配时,会发生溢出。

例如,考虑8位序列(MIPS具有32位寄存器,但对于我的示例,8位更容易)。让我们假设无符号解释:8位序列表示0到255(含)之间的数值。如果我将10010011(数值147)添加到01110110(数值118),那么我得到00001001(数值9)。 9不等于147 + 118。我得到那个结果,因为数学值是265,不能适合8位。加法结果需要9位,但上面的第9位已被删除。

现在,想象一下使用签名解释的相同示例。 10010011现在的数值为-109。 01110110仍然具有数值118,并且获得的结果(00001001)具有值9. -109和118的数学和为9,因此没有溢出。

这意味着溢出的概念取决于您如何解释这些值。有符号和无符号解释的加法机制是相同的(对于相同的输入比特序列,你得到相同的输出比特序列 - 这是对负有符号值使用二进制补码的全部要点)但溢出处理不同。 / p>

MIPS架构提供了在溢出时触发异常的方法。从概念上讲,对32位字有三个可能的加法运算:

  • 一个静默忽略溢出的结果(结果被截断)
  • 在发生签名溢出时引发异常的加法(如果输入和输出序列被解释为带符号的数字,则会出现溢出)
  • 在发生无符号溢出时引发异常的加法(如果输入和输出序列被解释为无符号数则存在溢出)

MIPS实现了前两种添加,分别带有adduadd操作码。在MIPS文档中,它们分别被称为 unsigned signed arithmetics 。没有用于在无符号溢出上引发异常的操作码。在实践中,C编译器仅使用addu,但是他们可以使用add作为签名类型(这是C标准允许的,但会破坏大量现有代码)。 Ada编译器使用add,因为Ada强制执行溢出检查。

有人说......

Patterson和Hennessey希望在64位整数上实现有符号和无符号算术。对于未签名的算术,他们不希望任何例外,因此他们使用addusubu。对于已签名的算术,当数学结果不适合具有带符号解释的 64位序列时,他们希望发生异常。它们不希望引发异常,因为在处理低32位的一半时会出现一些类似寄生溢出的情况。这就是他们为低部分使用subu的原因。

您的解决方案是错误的,因为它可能引发异常,而不应该这样做。假设您要从-2000000000减去2000000000(二十亿)(减去二十亿)。数学结果是40亿(四十亿)。两个操作数和结果肯定适合64位(可表示的范围是-9223372036854775808到9223372036854775807)。因此,对于64位带符号的算术,没有溢出:应该没有异常。但是,在这种情况下,您的第一个sub会报告溢出。 sub适用于32位值并签名 32位算术。它的操作数将是01110111001101011001010000000000和10001000110010100110110000000000。请注意,这些值都适合32位:这些值的32位带符号解释分别为正负两十亿。然而,减法结果是四十亿,并且它不适合32位(作为有符号数)。因此,您的sub会引发异常。

根据经验,溢出检测是指依赖于符号解释的操作,这会影响对最重要位的处理。对于大整数算术,除了最重要的词之外的所有词都应被视为无符号,因此到处都是addu / subu。作为第一步,如果你首先专注于未签名的算术,事情会更容易理解,毫无例外(那么你只需使用addusubu,而永远不会 {{1 }或add)。