仿真 – 定点整数除法(“分数除法”)算法

霍尼韦尔DPS8计算机(和其他人)有一个“分数小数”指令:

“This instruction divides a 71-bit fractional dividend (including sign) by a 36-bit
fractional divisor (including sign) to form a 36-bit fractional quotient (including
sign) and a 36-bit fractional remainder (including sign). Bit 35 of the remainder
corresponds to bit 70 of the dividend. The remainder sign is equal to the dividend
sign unless the remainder is zero.”

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

  .qqqqq / .ddddd

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

要在DPS8仿真器中实现该指令,我相信我需要从创建两个70位数字开始:71位被除数减去它的符号位,而36位除数减去其符号位并向左移位35位以便小数点排成一行.

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

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

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

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

想象一下,你有一个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<< 8)/(divisor_ip<< 8)
– 因为我们显然会失去商数的小数部分(结果).

相反,我们需要将股息向上移动多少比特我们将小数部分向上移动…

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

……那就是……
    (dividend_ip<<(dividend_precision divisor_precision)/(divisor_ip<< divisor_precision) 现在,让我们把我们的小数部分数学放到图片中……

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

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

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

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

(((dividend_ip<< 15)| dividend_fp)<< 15)/((divisor_ip<< 15)| divisor_fp) 我们的商现在具有15位精度. 好的,但是因为你只提供小数部分而且整数部分总是为零,所以你应该只能抛出整数部分.这使它….
(((dividend_ip<< 16)| dividend_fp)<< 16)/((divisor_ip<< 16)| divisor_fp)
…减少到……
(dividend_fp<< 16)/ divisor_fp …现在让我们使用64位整数,我们可以在商中获得32位精度…
(dividend_fp<< 32)/ divisor_fp …一些编译器支持int128_t(可以在某些平台上为GCC启用),因此您可以使用该类型,以便轻松获得128位.我没有尝试过,但我之前在网上看到了一些信息;搜索int128_t,你可能会发现如何. 如果让int128_t工作,你可以将被除数为128位,除数为64位,商为64位……
quotient_fp =((dividend_fp<< 36)/ divisor)>> (64 – 36)
…为了获得36位精度.
请注意,由于结果位于商的前36位,因此商需要向下移位(64 – 36)= 28位.
你甚至可以高达(128 – 36)= 92位精度:
(dividend_fp<< 92)/除数 现在,你可能(希望)有一个解决方案,我想建议你熟悉低级二进制除法(再次;因为你已经在那里了一段时间).
最好的资源似乎是硬件如何划分二进制数;例如微控制器,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是余数(休息,模数结果)

上面的例程包含无符号除法的基础知识.

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

点赞