簡體   English   中英

通過16位移位進行32位乘法運算

[英]32-bit multiplication through 16-bit shifting

我正在使用移位和加法編寫一個軟乘法函數調用。 現有的函數調用如下:

unsigned long __mulsi3 (unsigned long a, unsigned long b) {

    unsigned long answer = 0;

    while(b)
    {
        if(b & 1) {
            answer += a;
        };

        a <<= 1;
        b >>= 1;
    }
    return answer;
}

雖然我的硬件沒有倍增器,但我有一個硬移位器。 移位器一次最多可以移位16位。

如果我想充分利用我的16位移位器。 有關如何調整上述代碼以反映我的硬件功能的任何建議? 給定的代碼每次迭代僅移位1位。

16位移位器可以一次將32位無符號長值移位16個位置。 sizeof(無符號長整數)== 32位

移位多位的能力不會有多大幫助,除非你有硬件乘法,比如說8位x 8位,或者你可以負擔得起一些RAM / ROM(例如)4位乘4位乘以查找。

通過交換參數以使乘數更小,可以幫助直接轉換和添加(正如您所做)。

如果您的機器通常更快地執行16位操作,那么一次將32位“a”視為“a1:a0”16位,類似地“b”,您可能可以將某些周期相同。 你的結果只有32位,所以你不需要做'a1 * b1' - 雖然其中一個或兩個可能都是零,所以勝利可能不大! 此外,您只需要16位'a0 * b1',因此可以完全16位 - 但如果b1(假設b <= a)通常為零,那么這也不是一個大贏家。 對於'a * b0',你需要一個32位'a'和32位加'answer',但你的乘數只有16位...這可能有助於也可能沒有幫助。

跳過乘數零的運行可能會有所幫助 - 取決於處理器和乘數的任何屬性。

FWIW:做一個神奇的'a1 * b1','(a1-a0)*(b0-b1)','a0 * b0',並根據我的小經驗,通過輪班,加法和減法合成結果是絕對的噩夢......'(a1-a0)','(b0-b1)'的標志及其產品必須得到尊重,這使得看起來像一個可愛的伎倆有點混亂。 當你完成它以及添加和減去時,你必須有一個強大的緩慢乘法,以使它全部值得! 當乘以非常非常長的整數時,這可能會有所幫助......但是內存問題可能占主導地位...當我嘗試它時,這是一種令人失望的事情。

使用16位移位可以幫助您使用以下方法進行小幅度的增強:

(U1 * P + U0) * (V1 * P + V0) =
= U1 * V1 * P * P + U1 * V0 * P + U0 * V1 * P + U0 * V0 =
= U1 * V1 * (P*P+P) + (U1-U0) * (V0-V1) * P + U0 * V0 * (1-P)

假設P是2的方便冪(例如,2 ^ 16,2 ^ 32),因此乘以它是快速移位。 這減少了從4到3的較小數字的乘法,並且遞歸地,對於非常長的數字,O(N ^ 1.58)而不是O(N ^ 2)。

這種方法被命名為Karatsuba的乘法 這里描述了更多高級版本。

對於較小的數字(例如8乘8位),如果你有足夠的快速ROM,下面的方法很快:

a * b = square(a+b)/4 - square(a-b)/4

如果要將int(square(x)/4)制成表格,則無符號乘法需要1022個字節,有符號乘法需要510個字節。

基本方法是(假設換1): -

  • 將前16位移位
  • 將前16位的最低位設置為最后16位的最高位
  • 將底部16位移位

取決於你的硬件......

但你可以試試: -

  • 假設unsigned long是32位
  • 假設Big Endian

然后 :-

 union Data32
        {
           unsigned long l;
           unsigned short s[2];
        }; 

unsigned long shiftleft32(unsigned long valueToShift, unsigned short bitsToShift)
{
    union Data32 u;
    u.l  = valueToShift
    u.s[0] <<= bitsToShift;
    u.s[0] |= (u.s[1] >> (16 - bitsToShift);
    u.s[1] <<= bitsToShift

    return u.l;
}

然后反過來換右轉

上面的代碼正在以傳統方式,我們在小學學習的方式成倍增加:

EX:

    0101
  * 0111
  -------
    0101
   0101.
  0101..
 --------
  100011

當然,如果你沒有乘法運算符或1位移位器,你就無法接近它! 但是,您可以通過其他方式執行此操作,例如循環:

unsigned long _mult(unsigned long a, unsigned long b)
{
    unsigned long res =0;

    while (a > 0)
    {
        res += b;
        a--;
    }

    return res;
} 

它很實用,但它滿足您的需求,無論如何,如果你有更多的約束(比如計算時間......),你可以考慮其他方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM