【问题标题】:Where can I find soft-multiply and divide algorithms?我在哪里可以找到软乘除算法?
【发布时间】:2011-01-24 07:46:53
【问题描述】:

我正在开发一个没有硬件乘法和除法的微控制器。我需要为这些基本操作编写软件算法,这些算法在紧凑的尺寸和效率之间取得了很好的平衡。我的 C 编译器移植将使用这些算法,而不是 C 开发人员自己。

到目前为止,我的 google-fu 主要是在这个话题上出现噪音。

任何人都可以指出一些信息丰富的东西吗?我可以使用 add/sub 和 shift 指令。基于表查找的算法也可能对我有用,但我有点担心在编译器的后端塞进这么多东西……嗯,可以这么说。

【问题讨论】:

  • 这是一个奇怪的微控制器,还没有 C 编译器吗?您可以使用该编译器(可能是个好主意),或者检查其源代码(如果有)以了解例程。
  • 这个微控制器有名字吗?
  • 这是新的。这是研究项目的一部分。
  • @Carl 具体来说,OP 应该查看 libgcc,其中实际上实现了 GCC 的软乘法/除法。 gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html
  • +1 用于提及 libgcc,但代码很难阅读。

标签: c algorithm microcontroller divide multiplication


【解决方案1】:

Microchip PICmicro 16Fxxx 系列芯片没有乘法或除法指令。 也许它的一些软乘法和软除法例程可以移植到您的 MCU。

PIC Microcontroller Basic Math Multiplication Methods

PIC Microcontroller Basic Math Division Methods

还可以查看"Newton's method" for division。 我认为该方法给出了我见过的任何除法算法中最小的可执行大小,尽管解释使它听起来比实际更复杂。 我听说一些早期的 Cray 超级计算机使用牛顿法进行除法。

【讨论】:

    【解决方案2】:

    事实证明,我仍然有一些旧的 68000 汇编代码用于长乘法和长除法。 68000 代码非常干净和简单,所以应该很容易为您的芯片翻译。

    68000 具有乘法和除法指令 IIRC - 我认为这些是作为学习练习而编写的。

    决定把代码放在这里。添加了 cmets,并在此过程中修复了一个问题。

    ;
    ; Purpose  : division of longword by longword to give longword
    ;          : all values signed.
    ; Requires : d0.L == Value to divide
    ;          : d1.L == Value to divide by
    ; Changes  : d0.L == Remainder
    ;          : d2.L == Result
    ;          : corrupts d1, d3, d4
    ;
    
            section text
    
    ldiv:   move    #0,d3     ; Convert d0 -ve to +ve - d3 records original sign
            tst.l   d0
            bpl.s   lib5a
            neg.l   d0
            not     d3
    lib5a:  tst.l   d1        ; Convert d1 -ve to +ve - d3 records result sign
            bpl.s   lib5b
            neg.l   d1
            not     d3
    lib5b:  tst.l   d1        ; Detect division by zero (not really handled well)
            bne.s   lib3a
            rts
    lib3a:  moveq.l #0,d2     ; Init working result d2
            moveq.l #1,d4     ; Init d4
    lib3b:  cmp.l   d0,d1     ; while d0 < d1 {
            bhi.s   lib3c
            asl.l   #1,d1     ; double d1 and d4
            asl.l   #1,d4
            bra.s   lib3b     ; }
    lib3c:  asr.l   #1,d1     ; halve d1 and d4
            asr.l   #1,d4
            bcs.s   lib3d     ; stop when d4 reaches zero
            cmp.l   d0,d1     ; do subtraction if appropriate
            bhi.s   lib3c
            or.l    d4,d2     ; update result
            sub.l   d1,d0
            bne.s   lib3c
    lib3d:                    ; fix the result and remainder signs
    ;       and.l   #$7fffffff,d2  ; don't know why this is here
            tst     d3
            beq.s   lib3e
            neg.l   d2
            neg.l   d0
    lib3e:  rts
    
    ;
    ; Purpose  : Multiply long by long to give long
    ; Requires : D0.L == Input 1
    ;          : D1.L == Input 2
    ; Changes  : D2.L == Result
    ;          : D3.L is corrupted
    ;
    
    lmul:   move    #0,d3       ; d0 -ve to +ve, original sign in d3
            tst.l   d0
            bpl.s   lib4c
            neg.l   d0
            not     d3
    lib4c:  tst.l   d1          ; d1 -ve to +ve, result sign in d3
            bpl.s   lib4d
            neg.l   d1
            not     d3
    lib4d:  moveq.l #0,d2       ; init d2 as working result
    lib4a:  asr.l   #1,d0       ; shift d0 right
            bcs.s   lib4b       ; if a bit fell off, update result
            asl.l   #1,d1       ; either way, shift left d1
            tst.l   d0
            bne.s   lib4a       ; if d0 non-zero, continue
            tst.l   d3          ; basically done - apply sign?
            beq.s   lib4e       ; was broken! now fixed
            neg.l   d2
    lib4e:  rts
    lib4b:  add.l   d1,d2      ; main loop body - update result
            asl.l   #1,d1
            bra.s   lib4a
    

    顺便说一句 - 我从来没有弄清楚是否有必要在一开始就将所有内容都转换为正数。如果您对移位操作小心,这可能是可以避免的开销。

    【讨论】:

      【解决方案3】:

      这是一个简单的乘法算法:

      1. 从乘数的最右边开始。

      2. 如果乘数中的位为1,则添加被乘数

      3. 将被乘数移动 1

      4. 移动到乘法器中的下一位并返回步骤 2。

      这是一个除法算法:

      1. 如果除数大于被除数,则停止。

      2. 当除数寄存器小于被除数寄存器时,左移。

      3. 将除数寄存器右移 1。

      4. 从被除数寄存器中减去除数寄存器,并将结果寄存器中与除数寄存器的移位总数相对应的位更改为 1。

      5. 在第 1 步重新开始,除数寄存器处于原始状态。

      当然,您需要检查除以 0,但它应该可以工作。

      这些算法当然只适用于整数。

      【讨论】:

      • 如果将 (1
      • @jbcreix,我不确定我是否理解您的要求。你能详细说明一下吗?
      • 如果在除数较大时将除数左移,则最高位将在非常大的除数中移出。所以,当你向右移动时,你会得到一个不同的数字,如果是 2 的除数,0。
      • @jbcreix,是的,当然你要小心避免溢出问题。
      【解决方案4】:

      我最喜欢的此类参考资料,以书本形式提供:

      http://www.hackersdelight.org/

      TAoCP 也不会出错:http://www-cs-faculty.stanford.edu/~uno/taocp.html

      【讨论】:

        【解决方案5】:

        要进行乘法运算,如果乘法器中的相应位已设置,则将移位被乘数的部分乘积加到累加器中。在循环结束时移动被乘数和乘数,测试乘数和 1 以查看是否应该进行加法。

        【讨论】:

          【解决方案6】:

          一种简单且性能相当好的整数乘法算法是Russian Peasant Multiplication

          对于有理数,您可以尝试二进制quote notation,它的除法比平时更容易。

          【讨论】:

          • 我认为算法是针对人的,而不是针对机器的。
          • 算法与处理器无关。
          • 不一定——一种算法的性能可能比其他算法更好或更差,这取决于它运行的硬件是否支持正确的基本操作。人脑支持对 CPU 的不同基本操作。例如,我个人的大脑 32 位整数加法至少比 x86 慢几十亿倍,而我的硬件加速视觉算法远远优于任何当前的 PC。
          • 好吧,因为“除以 2 并丢弃余数”只是“右移”,而“奇数测试”是“逻辑和 1 并针对 0 进行测试”,它似乎映射得很漂亮以及基本的 CPU 操作。它本质上与贾斯汀的回答所描述的算法相同。
          • @Steve314:你说得对,算法效率取决于基本操作的效率,这取决于处理器,但算法的正确性与处理器无关。从这个意义上说,农民乘法不适用于任何特定的处理器。正如 caf 指出的那样,农民乘法在 CPU 上是有效的。
          【解决方案7】:

          这是一个除法算法:http://www.prasannatech.net/2009/01/division-without-division-operator_24.html

          我假设我们在谈论整数?

          如果没有硬件支持,您必须实现自己的除零异常。

          (我没有太多运气很快找到乘法算法,但如果其他人找不到,我会继续寻找)。

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-16
          • 2017-07-27
          • 2022-01-19
          • 2010-11-04
          • 2011-06-16
          • 1970-01-01
          • 2012-04-12
          • 1970-01-01
          相关资源
          最近更新 更多