我正在尝试以以下方式将二进制数组转换为十进制:
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位二进制数组转换为十进制,并且需要做成百万次。
有人能帮我吗?是否有更快的方法来完成上述操作?或者上述方法已经很好了?
我正在尝试以以下方式将二进制数组转换为十进制:
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位二进制数组转换为十进制,并且需要做成百万次。
有人能帮我吗?是否有更快的方法来完成上述操作?或者上述方法已经很好了?
您的方法足够好,如果称之为优秀,我只会建议不要混合位运算和“数学”方式将其转换为十进制,即只使用其中一种方式
decimal = decimal << 1 | array[i];
或者
decimal = decimal * 2 + array[i];
在尝试进行任何优化之前,对代码进行分析是非常重要的。计时,观察生成的代码,并且只有在理解正在发生的情况时才进行优化。
正如已经指出的那样,最好的优化方法是不做某些事情,而是进行更高级别的更改以消除需要。
然而...
你可能想要轻松地进行的大多数更改都可能是编译器已经完成的(对于编译器来说,移位与乘法是相同的)。有些实际上会阻止编译器进行优化(将一个add
更改为or
将限制编译器-有更多的方法来添加数字,而只有在这种情况下,你知道结果将是相同的)。
指针算术可能更好,但编译器并不愚蠢-它应该已经生成了适当的代码来取消引用数组,因此你需要检查是否实际上通过引入额外的变量而使事情更糟。
在这种情况下,循环计数是明确定义和有限的,因此展开循环可能是有意义的。
此外,这取决于你希望结果在多大程度上依赖于目标架构。如果你想要可移植性,进行优化会更加困难。
例如,下面的代码产生更好的代码:
unsigned int x0 = *(unsigned int *)array;
unsigned int x1 = *(unsigned int *)(array+4);
int decimal = ((x0 * 0x8040201) >> 20) + ((x1 * 0x8040201) >> 24);
我可以尝试制作一个64位版本,每次处理8位而不是4位。
但这肯定不是可移植的代码。如果我知道我运行的环境并且只想快速计算数字,我可能会在本地使用它。但我可能不会将其放入生产代码中。当然不能没有记录它所做的事情的文档,并且没有随附的单元测试来检查它是否实际工作。
array
是一个实际的char array[8]
,而不仅仅是指向匿名内存的char*
,那么*(unsigned int *)array
可能会导致严格别名未定义行为。并且,如果你没有使用alignas(unsigned) char array[8];
,它可能会导致对齐未定义行为。使用memcpy(&tmp, array, sizeof(tmp))
来进行严格别名安全的非对齐加载。这样可以使其完全可移植,至少在static_assert(sizeof(uint64_t) == 8)
的系统上是如此。这不依赖于字节序或其他任何东西,尽管它依赖于8位字节。像uint64_t
这样的类型保证没有填充。 - undefined二进制“压缩”可以广义地视为带权和问题,有一些有趣的技术可用。
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; return (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
return (a & 0x00ffffffffffffff) % 255 + (a>>56); // little endian, or
return (a & ~1) % 255 + (a&1);
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内部函数。
accumulate
函数,结合加倍和加法的二进制操作:int doubleSumAndAdd(const int& sum, const int& next) {
return (sum * 2) + next;
}
int decimal = accumulate(array, array+ARRAY_SIZE,
doubleSumAndAdd);
这会产生大端整数,而OP代码会产生小端整数。
试试这个,我把一个长达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位的数据。 - Blastfurnace1020
位无法用32
位变量表示。 - Blastfurnacedouble
。即使您将其更改为使用 double
,您也应该意识到 IEEE 754 双精度浮点数具有约 53 位(约 16 个十进制数字)的有效数字。您实际上无法在 double
中存储 1020 个有效位,它将是一个近似值。唯一可行的方法是使用一些大整数库。 - Blastfurnace2 * (
,在右侧以大端顺序放置所有数组位。
function b2d_16_unsigned(_, __) {
# set __[_ = 1] for langs w/ 1-based indexing e.g. julia/lua/awk etc
return \
(_ += _ = split(_, __, _ = "")^_\
)*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_* \
__[_ = 0] \
+ __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_]
}
1110111110010101 61333
function b2d_64(_, signed_flag, __) { # signed_flag is 0/1
return \
(_ += _ = split(sprintf("%64s",_), __, _ = "")^_\
)*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(\
_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(\
_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(\
_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(_*(\
(+signed_flag ? -_ : _) * \
__[_ = 0] \
+ __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]) + __[++_]) + __[++_] \
) + __[++_]) + __[++_]) + __[++_]
}
1100010001100001010000100101000110000100110110001101110110110001 0
14150664422063398321
1100010001100001010000100101000110000100110110001101110110110001 1
-4296079651646153295
++
而产生未定义的行为。而且,与uint64_t
上的简单整数运算相比,sprintf
远远不够快。 - undefineduint64_t
中。我所说的是针对这个问题所需的整数运算,而不是你可以编造的任意例子。 - undefinedtotal = total*2 + digit
来处理。无论如何,最终的结果是一个uint64_t
,所以你所需要的最宽的累加器就是一个单独的uint64_t
。 - undefined