定点整数除法("分数除法")算法

时间:2014-07-04 19:51:08

标签: emulation integer-division

霍尼韦尔DPS8计算机(和其他人)有一个"除以分数"指令:

  

"该指令将71位小数除数(包括符号)除以36位   分数除数(包括符号)形成一个36位的小数商(包括   符号)和36位小数余数(包括符号)。剩下的第35位   对应于被除数的第70位。余数符号等于股息   除非剩余部分为零,否则签字。"

所以,据我所知,这是整数除法,左边是小数点。

  .qqqqq / .ddddd

(我当天在FORTH中完成了整数数学的缩放,但是我对这些技术的记忆在时间的迷雾中消失了。)

要在DPS8仿真器中实现此指令,我相信我需要从创建两个70位数字开始:71位被除数减去它的符号位,而36位除数减去其符号位并移位左边35位,小数点对齐。

我想我可以用'%'来形成余数和商(在C中)和' /',但我不确定这些结果是否需要归一化(即移位)。

我找到了一个"移位和减去"的例子。算法"Computer Arithmetic",幻灯片10),但我更喜欢更直接的实现。

我是在正确的轨道上,还是解决方案更加微妙(修复标志和错误的检测已经从这里消失;这些阶段都有很好的记录。实际的划分就是问题。)。任何指向此类硬件仿真的C实现的指针都会特别有用。

1 个答案:

答案 0 :(得分:2)

我没有明确的答案,但作为一个部门是一个部门,你可能会发现看一些基本的分工程序会很有帮助。

想象一下,你有一个32位变量,你想要一个8位小数部分。 然后,您有一个介于0和16777215之间的整数部分,以及一个介于0和255之间的小数部分。 0xiiiiiiff(其中i是整数部分,f是小数部分)。

想象一下,你有一个24位的被除数(分子),比如值3,以及一个24位的除数(分母),比如值13。 正如我们很快将看到的,3/13大于零且小于一。这意味着我们的小数部分是非零的,但我们的整数部分完全用零填充。

所以要使用标准除法函数进行上述除法,我们只需将被除数除以N,因此我们将在小数部分得到N位精度。

quotient_fp = (dividend_ip << 8) / divisor_ip

到目前为止,非常好。

但是,如果我们希望除数有一个小数部分呢?

如果我们将除数提高8,那么我们就会遇到问题:     (dividend_ip&lt;&lt; 8)/(divisor_ip&lt;&lt; 8) - 因为我们显然会失去商数的小数部分(结果)。

相反,我们需要将股息向上移动多少位,因为我们将小数部分向上移动......

((dividend_ip << 8) << 8) / (divisor_ip << 8)

......这就是......     (dividend_ip&lt;&lt;(dividend_precision + divisor_precision)/(divisor_ip&lt;&lt; divisor_precision)

现在,让我们把我们的小数部分数学放到图片中......

(((dividend_ip << dividend_precision) | dividend_fp) << divisor_precision) / ((divisor_ip << divisor_precision) | divisor_fp)

我们的商的精度与dividend_precision相同,即8位。

不幸的是,这吃了很多东西。

幸运的是,在您的情况下,整数部分并不重要,因此您将为分数部分留出大量空间。 让我们将精度提高到15位;这可以使用普通的32位整数进行测试......

<(>((dividend_ip&lt;&lt; 15)| dividend_fp)&lt;&lt; 15)/((divisor_ip&lt;&lt; 15)| divisor_fp)

我们的商现在具有15位精度。

好的,但是因为你只提供小数部分而且整数部分总是为零,所以你应该只能抛出整数部分。这使它.... (((dividend_ip&lt;&lt; 16)| dividend_fp)&lt;&lt; 16)/((divisor_ip&lt;&lt; 16)| divisor_fp) ...减少到...... (dividend_fp&lt;&lt; 16)/ divisor_fp

...现在让我们使用64位整数,我们可以在商中获得32位精度... (dividend_fp&lt;&lt; 32)/ divisor_fp

...一些编译器支持int128_t(可以在某些平台上为GCC启用),因此您可以使用该类型,以便轻松获得128位。我没有尝试过,但我之前在网上看到了一些信息;搜索int128_t,你可能会发现如何。

如果让int128_t工作,你可以将被除数为128位,除数为64位,商为64位...... quotient_fp =((dividend_fp&lt;&lt; 36)/ divisor)&gt;&gt; (64 - 36) ...为了获得36位精度。 请注意,由于结果位于商的前36位,因此商需要向下移位(64 - 36)= 28位。 你甚至可以高达(128 - 36)= 92位精度: (dividend_fp&lt;&lt; 92)/ divisor

现在,你可能(希望)有一个解决方案,我想建议你熟悉低级二进制除法(再次;因为你已经在那里了一段时间)。 最好的资源似乎是硬件如何划分二进制数;例如微控制器,CPU等。汇编语言分隔符也有助于了解内部工作原理。通常使用位移的32位除法例程是非常好的来源。

随着时间的推移,我在ARM汇编语言中遇到了一个非常聪明的ARM实现。通常我不会发布引用或汇编语言示例,但考虑到代码非常小,我认为它会没用。

取自A Fast Hi Precision Fixed Point Divide

r0是分子(被除数) r2是分母(除数)

    mov     r1,#0
    adds    r0,r0,r0
    .rept   32
    adcs    r1,r2,r1,lsl#1
    subcc   r1,r1,r2
    adcs    r0,r0,r0
    .endr

r0是商(结果) r1是余数(rest,modulo result)

上述例程包含无符号除法的基础知识。

我希望这些信息有用。它可能包含错误,因为我没有测试任何提到的代码或示例。不过,我有信心,这并非全是错的。 ;)