我有一组数字:1,2,4,8,16,32,64等。
现在给出一个数字,比方说44,我必须确定它有孩子32,8和4。 (32 + 8 + 4 = 44)
到目前为止我所拥有的是以下代码:
public static long[] GetBTreeChildren(long? parentMask)
{
var branches = new List<long>();
if (parentMask == null) return branches.ToArray();
double currentValue = (double)parentMask;
while (currentValue > 0D)
{
double power = Math.Floor(Math.Log(currentValue, 2.0D));
double exponentResult = Math.Pow(2, power);
branches.Add((long)exponentResult);
currentValue -= exponentResult;
}
return branches.ToArray();
}
但是当给定数字非常大时(例如36028797018963967),上面的代码无法正常工作
我正在使用VS2012(C#)。
答案 0 :(得分:1)
它不适用于非常大的数字的原因是因为您使用的double
数据类型的精度有限(大约16位)。
您可以通过简单,极其高效的按位操作来完成所需的一切,而不是使用Math.Pow
和Math.Log
。
public static long[] GetBTreeChildren(long? parentMask)
{
var branches = new List<long>();
if (parentMask == null) return branches.ToArray();
for(int i = 0; i < 63; ++i)
{
if( (parentMask & (1L << i)) != 0)
branches.Add(1L << i);
}
return branches.ToArray();
}
基本上,每个位已经是2的幂,这正是你要找的。通过执行(long) 1 << i
,您将第一位移至第2位。您可以调整上面的代码,使其与您的代码更相似,并且稍微提高效率,而不是迭代i
,只需将parentMask
的位向右移,但是你必须知道负数会发生什么,以及逻辑移位与算术移位的区别。