对于divisor> dividend,对于最后4-5个最低有效位,计算出的尾数会给出错误的结果。
我试图将两个IEEE-754浮点数的有效位数/尾数相除。我已经使用了这种除法算法
除数>股息时,尾数已标准化,但对于最后4-5位仍然不正确。
DivisionAlgorithm:#除数尾数($ a0)由除数尾数($ a1)进行25次迭代# #返回$ v0中的商值#
addi $sp, $sp, -12 #Decrement in $sp (Stack Pointer)
sw $s0, 0($sp) #Pushing $s0 into Stack
sw $ra, 4($sp) #Pushing $ra into Stack (Function Call Exists)
sw $s1,8($sp) #Pushing $s1 into stack
move $t0, $a0 #$t0 = Mantissa of Dividend/Remainder
move $t1, $a1 #$t1 = Mantissa of Divisor
add $s0, $0, $0 #$s0 = Initialization
add $v1, $0, $0 #$v1 = 0 (Displacement of Decimal Point Initialized)
addi $s1,$0,1 #$s1 = 1 (initialize loop variable to 1)
add $t3,$0,33
loop:
beq $t1, $0, check #If Divisor = 0, Branch to check
sub $t0, $t0, $t1 #Dividend = Dividend - Divisor
sll $s0, $s0, 1 #Quotient Register Shifted Left by 1-bit
slt $t2, $t0, $0
bne $t2, $0, else #If Dividend < 0, Branch to else
addi $s0, $s0, 1 #Setting Quotient LSb to 1
j out
else: add $t0, $t0, $t1 #Restoring Dividend Original Value
out: srl $t1, $t1, 1 #Divisor Register Shifted Right by 1-bit
j loop
check: slt $t2, $a0, $a1 #If Dividend < Divisor, Call Function 'Normalization'
beq $t2, $0, exit #If Dividend > Divisor, Branch to exit
move $a0, $s0 #$a0 = Quotient
jal Normalization #Function Call
j return
exit: move $v0, $s0 #$v0 = Calculated Mantissa
return: lw $ra, 4($sp) #Restoring $ra
lw $s0, 0($sp) #Restoring $s0
lw $s1, 8($sp) #restoring $s1
addi $sp, $sp, 8 #Increment in $sp (Stack Pointer)
jr $ra #Return
规范化:#规范尾数(在$ a0中)并计算小数点移动的小数位# #返回: #i)$ v0 =标准化尾数 #ii)$ v1 =小数位数#
lui $t0, 0x40 #$t0 = 0x40 (1 at 23rd-bit)
addi $t2, $0, 1 #$t2 = 1 (Initialization)
loop2: and $t1, $a0, $t0 #Extracting 23rd-bit of Mantissa
bne $t1, $0, else2 #If 23rd-bit = 1; Branch to else2
addi $t2, $t2, 1 #Increment in Count of Decimal Places Moved
sll $a0, $a0, 1 #Mantissa Shifted Left (To Extract Next Bit)
j loop2
else2: sll $a0, $a0, 1 #Setting 24th-bit = 1 (Implied)
move $v0, $a0 #$v0 = Normalized Mantissa
move $v1, $t2 #$v1 = Displacement of Decimal Point
jr $ra #Return
例如,我期望2.75 / 6.355的输出是00111110110111011000111011001110,但实际输出是00111110110111011000111011010110。
答案 0 :(得分:3)
您的算法不正确。
恢复分区的合适算法是
qu=0
rem=dividend
repeat N times
rem = rem - divisor
if rem < 0
rem = rem + divisor
qu = (qu<<1)
else
qu = (qu<<1) + 1
end
rem = rem << 1
end
当你在
qu=0
rem=dividend
repeat N times
rem = rem - divisor
if rem < 0
rem = rem + divisor
qu = (qu<<1)
else
qu = (qu<<1) + 1
end
divisor = divisor >> 1 // instead of left shift remainder
end
由于该算法仅依赖于divisor
和rem
的比较,因此似乎等效于右移divisor
或左移rem
。却不是。
右移除数时,请放开除数的最低有效位。
这可能会影响比较并因此影响商。
如果您打印其余部分,将会看到对它的影响很大,其值与正确结果之间可能会有2倍的差异。
将余数乘以2似乎很危险,因为可能会发生溢出。但是如果我们看一下算法,就会发现它不会发生。
最初,除数和除数是某些FP的尾数,因此1≤除数,除数<2,并且对rem的保留相同,其最初是除数的副本。请注意,这意味着rem <2 * div
现在,我们进行第一步计算
⚫如果rem
因此,在每一步中,我们始终拥有rem <2 * div的属性,并且只要可以对2 * div进行编码,就可以确保rem永远不会溢出。
就实现而言,您可以在整数寄存器的24 LSB上编码这些数字。只要精度保持不变,就足够了。
在您的实现中,您循环了32次。如果要将结果写在IEEE尾数中,它是没有用的,并且可以减少迭代次数。循环就足够了
24次(尾数大小)
+1(如果被除数<除数,则第一位为0,但第二位保证为1)
如果要对结果进行四舍五入,则必须执行另外两个步骤来获得舍入和保护位。经过这两个计算步骤,如果余数≠0,则将sticky bit设置为1,否则将其设置为0。然后使用通常的规则进行舍入。
答案 1 :(得分:1)
I。分割算法本身以您开始的形式(据我所知,汇编代码也是如此)的描述缺少主要部分。准备运行除法循环时,如果在每次迭代中将除数右移1位,则最初应尽可能将其左移,并且很有用。
让我用十进制数解释一下:假设您在6位数的机器中将10001除以73(因此,则是010001除以000073)。您应该向左移动73,直到它停止适合(因此最大移位为4,而移位的除数为730000)或其最高有效位(MSD)位置高于股息的MSD(因此,我们可以在73000处停止)。然后,使用以下命令运行循环
(对于十进制,每个移位都应嵌套嵌套减法;对于二进制,则不需要。)
如果再向右移动73,您将失去除数的有效位数,因此最终商将大于正数。
您可以无条件地将除数左移最大宽度(730000,shift = 4),但这会浪费CPU周期。如果CPU具有CountLeadingZeros操作,则可以节省时间。
II。第二个主要时刻是您要对浮点数得出的尾数进行除法。这意味着,在最典型的两个归一化数字情况下,被除数和除数的MSB相同,并且您只会得到2个商的变式-0和1。(对于IEEE二进制32,这是将第23位设置为并且除数和除数都在8388608..16777215。)
要获得更多实数,您必须执行长除法。 (有关更经济的方法,请参见下文;现在,与教科书进行比较。)对于IEEE binary32,这意味着在最后舍入之前,至少要获得27个商位(结果尾数+ 24 +舍入+粘滞)。如上所述,在28个周期内将红利左移27位,然后将51位红利除以24位除数。
@AlainMerigot所描述的版本与我所描述的基本相同-您仍然可以计算(dividend<<quotient_width)/divisor
,但是用不太明显的方式。如果将剩余数左移1,这很可能与除数右移1相同,前提是没有数据丢失。因此,第一次迭代(数字0)比较相等的位置并提供结果的MSB,但此刻它位于位0;然后,您可以在每次迭代中“激活”余数。这样可以避免双字操作。在这种算法中,如果需要最终的余数,则应该通过迭代次数右移来恢复它。但是浮动部门本身并不需要。
但是:为了正常工作,您应该从一开始就正确分配股息和除数。在十进制示例中,这意味着您不应以dividant = 10001,divisor = 73开头,而应以p股息= 10001,divisor = 73000开始。如果您的源尾数在获取整数值之前已标准化,则将自动满足此要求。否则,需要付出额外的努力,并且这些努力实际上与我为教科书划分的除数移位所描述的相同。 (这就是为什么数十年来,英特尔CPU一直遭受无限的异常处理时间的原因;)