【问题标题】:C++: Binary to Decimal ConversionC++:二进制到十进制转换
【发布时间】:2013-02-06 08:31:08
【问题描述】:

我正在尝试通过以下方式将二进制数组转换为十进制:

uint8_t array[8] = {1,1,1,1,0,1,1,1} ;
int decimal = 0 ;    

for(int i = 0 ; i < 8 ; i++)
    decimal = (decimal << 1) + array[i] ;

实际上我必须将 64 位二进制数组转换为十进制,我必须这样做一百万次。

任何人都可以帮助我,有没有更快的方法来完成上述操作?还是上面那个好看?

【问题讨论】:

  • 位是在数组中还是作为字符串都没有关系,无论如何您都必须遍历这些位。可能有更多特定于 C++ 的方法可以做到这一点,但最终总会有一个循环。
  • this question 可以帮到你
  • 通常大的优化收益不是从小函数中获得的,而是从更高的层次上获得的。为什么数字是这样存储的?
  • 您可以使用循环展开和/或指针算法来代替数组访问。这会加快速度,但会保持相同的算法复杂度。

标签: c++


【解决方案1】:

你的方法足够了,可以称之为 nice 我不会混合按位运算和转换为十进制的“数学”方式,即使用任何一个

    decimal = decimal << 1 | array[i];

    decimal = decimal * 2 + array[i];

【讨论】:

  • 但是请注意,任何体面的编译器都会以相同的方式处理移位和乘法,而使用“或”而不是加法会限制它的优化能力。实际上,MSVC 为这里的第一个示例生成了较差的代码,但第二个示例的代码与原始示例中的代码相同。
【解决方案2】:

在尝试任何优化之前,分析代码很重要。计时,查看正在生成的代码,并仅在您了解正在发生的事情时进行优化。

正如已经指出的那样,最好的优化不是做某事,而是进行更高级别的更改以消除需求。

不过……

您可能希望在此处轻松进行的大多数更改可能是编译器已经完成的事情(移位与编译器的乘法相同)。有些实际上可能阻止编译器进行优化(将 add 更改为 or 将限制编译器 - 有更多添加数字的方法,只有您知道在这种情况下结果将是相同的)。

指针算法可能更好,但编译器并不愚蠢 - 它应该已经生成了用于取消引用数组的体面代码,因此您需要检查您实际上并没有通过引入额外的变量来使事情变得更糟。

在这种情况下,循环计数定义明确且有限,因此展开可能有意义。

此外,这还取决于您希望结果对目标架构的依赖程度。如果您想要可移植性,则很难(呃)进行优化。

例如,下面的代码会产生更好的代码:

unsigned int x0 = *(unsigned int *)array;
unsigned int x1 = *(unsigned int *)(array+4);

int decimal = ((x0 * 0x8040201) >> 20) + ((x1 * 0x8040201) >> 24);

我也可以推出一次 8 位而不是 4 位的 64 位版本。

但它绝对不是可移植的代码。如果我知道自己在做什么并且只想快速处理数字,我可能会在本地使用它。但我可能不会把它放在生产代码中。当然,如果没有记录它做了什么,也没有附带的单元测试来检查它是否确实有效。

【讨论】:

    【解决方案3】:

    二进制“压缩”可以概括为加权和问题——为此有一些有趣的技术。

    • X mod (255) 本质上是对所有独立的 8 位数字求和。
    • X mod 254 表示将每个数字加倍权重,因为 1 mod 254 = 1、256 mod 254 = 2、256*256 mod 254 = 2*2 = 4 等等。

      如果编码是大端,那么 *(unsigned long long)array % 254 将产生加权和(截断范围为 0..253)。然后删除权重为 2 的值并手动添加将产生正确的结果:

      uint64_t a = *(uint64_t *)array; 返回 (a & ~256) % 254 + ((a>>9) & 2);

    获得权重的其他机制是将每个二进制数字预乘 255 并屏蔽正确的位:

    uint64_t a = (*(uint64_t *)array * 255) & 0x0102040810204080ULL; // little endian
    uint64_t a = (*(uint64_t *)array * 255) & 0x8040201008040201ULL; // big endian  
    

    在这两种情况下,都可以取 255 的余数(现在用权重 1 更正):

    return (a & 0x00ffffffffffffff) % 255 + (a>>56); // little endian, or  
    return (a & ~1) % 255 + (a&1);  
    

    对于持怀疑态度的人:我实际上确实将模数版本(略)比 x64 上的迭代快。

    继续 JasonD 的答案,可以迭代地利用并行位选择。 但首先以完整形式表达方程将有助于编译器消除使用累积的迭代方法产生的人为依赖:

    ret =  ((a[0]<<7) | (a[1]<<6) | (a[2]<<5) | (a[3]<<4) |
            (a[4]<<3) | (a[5]<<2) | (a[6]<<1) | (a[7]<<0));
    

    对比

    HI=*(uint32_t)array, LO=*(uint32_t)&array[4];
    LO |= (HI<<4);    // The HI dword has a weight 16 relative to Lo bytes
    LO |= (LO>>14);   // High word has 4x weight compared to low word
    LO |= (LO>>9);    // high byte has 2x weight compared to lower byte
    return LO & 255;
    

    一种更有趣的技术是利用 crc32 作为压缩函数;然后碰巧结果是 LookUpTable[crc32(array) & 255];因为与这个给定的 256 个不同数组的小子集没有冲突。然而,要应用它,人们已经选择了更不便携的道路,并且最终可能会使用 SSE 内在函数。

    【讨论】:

      【解决方案4】:

      您可以使用accumulate,进行加倍和二元运算:

      int doubleSumAndAdd(const int& sum, const int& next) {
          return (sum * 2) + next;
      }
      
      int decimal = accumulate(array, array+ARRAY_SIZE,
                               doubleSumAndAdd);
      

      这产生大端整数,而 OP 代码产生小端。

      【讨论】:

        【解决方案5】:

        试试这个,我转换了一个最多1020位的二进制数

        #include <sstream>
        #include <string>
        #include <math.h>
        #include <iostream>
        using namespace std;
        
        long binary_decimal(string num) /* Function to convert binary to dec */
        {
            long dec = 0, n = 1, exp = 0;
            string bin = num;
               if(bin.length() > 1020){
                  cout << "Binary Digit too large" << endl;
               }
               else {
        
                    for(int i = bin.length() - 1; i > -1; i--)
                    {
                         n = pow(2,exp++);
                         if(bin.at(i) == '1')
                         dec += n;
                    }
        
               }
          return dec;
        }
        

        理论上这种方法适用于无限长的二进制数字

        【讨论】:

        • 您确实意识到long 是 32(或 64)位,而您实际上无法将 1020 位放入其中?
        • 是的..你可能读错了。它的工作原理是这样的: bin: 101 dec: 5 我读取了一个最多 1020 位的字符串,每次达到 1 时,我都会添加 2* pow 。
        • 尝试使用超过 31 位或 63 位的字符串来测试您的代码。您不可能在 32 位或 64 位有符号整数中表示 1020 位的值。结果溢出并环绕。 Look at your code running on ideone.com。当我给它 32 位或更多位时,结果是 -1。很明显,1020 位不能用32 位变量来表示。
        • 我正在设计一个虚拟机,它运行良好,我解码了一个 1020 位二进制文​​件并将结果存储在一个双精度文件中
        • 此代码不使用或返回double。即使您将其更改为使用 double,您也应该意识到 IEEE 754 双精度浮点的有效位为 53 位(大约 16 个十进制数字)。您实际上不能在double 中存储 1020 个有效位,这将是一个近似值。唯一可行的方法是使用一些 Big Integer 库。
        猜你喜欢
        • 1970-01-01
        • 2013-05-14
        • 2023-04-11
        • 2019-04-09
        • 1970-01-01
        • 1970-01-01
        • 2017-01-18
        相关资源
        最近更新 更多