仅使用OR和AND实现按位移位

时间:2016-04-27 06:23:07

标签: c math bit-manipulation bitwise-operators discrete-mathematics

我想为我的虚拟CPU实现SHL64和SHR64。 我的CPU只有四个从NAND实现的指令,可以执行任何if语句(所有比较操作)

  • CMP (签名/未签名/任何类型,从8b到64b)

目前我通过以下操作实施了SHR64:〜& | + - %

我对SHR64的实现:

#include <cstdint>
#include <iostream>

const uint64_t mask[]={
   0x1,0x2,0x4,0x8,
   0x10,0x20,0x40,0x80,
   0x100,0x200,0x400,0x800,
   0x1000,0x2000,0x4000,0x8000,
   0x10000,0x20000,0x40000,0x80000,
   0x100000,0x200000,0x400000,0x800000,
   0x1000000,0x2000000,0x4000000,0x8000000,
   0x10000000,0x20000000,0x40000000,0x80000000,
   0x100000000,0x200000000,0x400000000,0x800000000,
   0x1000000000,0x2000000000,0x4000000000,0x8000000000,
   0x10000000000,0x20000000000,0x40000000000,0x80000000000,
   0x100000000000,0x200000000000,0x400000000000,0x800000000000,
   0x1000000000000,0x2000000000000,0x4000000000000,0x8000000000000,
   0x10000000000000,0x20000000000000,0x40000000000000,0x80000000000000,
   0x100000000000000,0x200000000000000,0x400000000000000,0x800000000000000,
   0x1000000000000000,0x2000000000000000,0x4000000000000000,0x8000000000000000
};

uint64_t GET_MASK(uint32_t i)
{
   return mask[i];
}

inline uint64_t IfThen(uint64_t trueAddr,uint64_t falseAddr,int condition)
{
   uint64_t c=UINT64_MAX;
   if(!(condition))
   {
      c=0;
   }
   return (trueAddr&c) | (falseAddr&(~c));
}

int64_t Shr64(int64_t a,uint8_t b)
{
   int64_t iRet=0;
   int32_t aBit;
   int32_t count=64;
   b%=count;

   count=(int32_t)count - b;

   for(int32_t i=0; i < count; i++)
   {
      aBit=(a & GET_MASK(i + b)) != 0;
      iRet=(int64_t)(iRet | IfThen(GET_MASK(i),0,aBit));
   }
   return iRet;
}

int main()
{
   uint64_t test=Shr64(23,2);
   std::cout << "My impl " << test << std::endl;
   std::cout << "Default " << (23 >> 2) << std::endl;
}

SHL64可以用类似的方式实现。

有人可以使用以下规则帮助实施SHR和SHL:

  1. 仅使用NOT,OR和AND,if语句允许。
  2. 没有循环
  3. 与原始操作相比速度快(允许最多慢20倍)

3 个答案:

答案 0 :(得分:2)

首先,我同意这是没有意义的...但无论如何它是可以解决的(但结果将慢于简单的 CPU 指令)。

  1. <强>性能

    避免子调用它们放慢速度(IfThen,GET_MASK)比计算本身更多,因为它们将事物复制到堆栈中或从堆栈中复制...

    使用&代替%您使用2的幂,所以不需要它。从你的代码我假设你想要循环旋转而不进行。所以,如果某一方从某一方面消失,请立即从另一侧返回。

  2. <强>循环

    对循环进行硬编码以避免使用for如果要使用可变位长度,可以使用#define

  3. 允许的操作

    所以你可以用&,|,if(non_zero)做到这一点。在64位变量上if是最慢的,所有其他操作都具有与 CPU 实现的SHL/SHR指令本身相当的运行时间,因此您可以不要快于此。理论上你可以做的“最快”的事情就是使用 LUT ,但这会消耗8*(2^64) Bytes,因为 CACHE ,这会导致数据太多而且也会消失废票。

  4. 那么有什么选择:

    1. 硬代码O(n)循环
    2. 使用LUT(在完整位宽上无法使用)
    3. 使用较小的位宽并将它们叠加在一起

      为此,您将数字分成位字(例如8 x BYTE)并将每个数字作为数字(基数)处理。您可以使用指向您的号码的BYTE*的联合或指针。旋转结果基数 BYTE 索引为b>>3,旋转位为b&7您还需要处理重叠和/或子结果,因此它会导致{{1 } 8*(2+1)轮换。您可以为每个操作仅8 bit的每种轮播类型设置 LUT 8 x 256 BYTEs

      你也可以用分而治之的方式做到这一点...所以16位基于8位然后32位基于16位,最后64位基于32位移位。这会使操作次数减少一些。

    4. 这里仅使用2 KByte进行8 bit轮换的 C ++ 示例:

      if(non_zero),&,|

答案 1 :(得分:1)

我不知道你为什么要这样做。

无论如何这里是uint8_t案例的一个解决方案。该解决方案使用&|if。关于速度,它可能很慢。通过shr8b内联可以实现一些速度提升。

#include <iostream>
using namespace std;

const uint8_t mask[]={
   0x1,
   0x2,
   0x4,
   0x8,
   0x10,
   0x20,
   0x40,
   0x80
};

uint8_t shr8b(uint8_t x, uint8_t sr, uint8_t cb)
{
    if ((cb >= sr) && (x & mask[cb]))
    {
        return mask[cb-sr];
    }
    return 0;
}

uint8_t shr8(uint8_t x, uint8_t sr)
{
    uint8_t res = 0;
    if (sr < 8)
    {
        res |= shr8b(x, sr, 0);
        res |= shr8b(x, sr, 1);
        res |= shr8b(x, sr, 2);
        res |= shr8b(x, sr, 3);
        res |= shr8b(x, sr, 4);
        res |= shr8b(x, sr, 5);
        res |= shr8b(x, sr, 6);
        res |= shr8b(x, sr, 7);
    }
    return res;
}

int main() {
    uint8_t x = 144;
    uint8_t y = 3;
    uint8_t test =  shr8(x,y);
    std::cout << "My impl " << ((uint32_t)test) << std::endl;
    std::cout << "Default " << ((uint32_t)(x>>y)) << std::endl;
    return 0;
}

好吧,它还使用-进行索引计算,使用>=<进行比较,所以可能不遵循所有规则。

如果你想提高速度,那就像是:

    // Instead of res |= shr8b(x, sr, 0);
    if ((0 >= sr) && (x & 0x1))
    {
        res |= mask[0 - sr];  // or just mask[0]
    }

    // Instead of res |= shr8b(x, sr, 1);
    if ((1 >= sr) && (x & 0x2))
    {
        res |= mask[1-sr];
    }

    // Instead of res |= shr8b(x, sr, 2);
    if ((2 >= sr) && (x & 0x4))
    {
        res |= mask[2-sr];
    }

    // and so on ....

答案 2 :(得分:0)

  

有人可以使用以下规则帮助实施SHR和SHL:

     
      
  1. 如果可能,只使用NOT,OR和AND
  2.   

可以有效地执行此操作。这些操作是逐位操作,不会影响位直接位以外的位位置,因此它们不能用于实现移位或需要从不同位位置输入的任何其他操作,除非扫描单个位和像在示例代码中一样逐位编写结果。

相关问题