四舍五入到给定数字数组中最接近的数字

Rounding to the closest number in a given array of numbers

在发布之前我查看了这两个答案:

C# Finding Nearest Number in Array

Round integer to nearest high number in array

虽然第一个对于其中一个答案中提到的扩展方法有点帮助,但我无法深入理解它,以至于我无法修改和实现它以适应我的具体情况,并且第二个不适合我的需要,因为它的数组长度很短,如果我要使用一个数组来存储所有以 2 为底的数字,最多 2.68 亿,它会有相对大量的条目。

基本上我在这里要做的是采用 RoundedSpeed(已使用 Math.Round 将其四舍五入为整数)并将其四舍五入到最接近的数字(它绝不能向下舍入,只能向上舍入)这是对 2 施加幂的结果。(示例数字包括 16384、32768、65536、131072 等)

对于这个特定的例子,我想要舍入到的数字是 32768。(虽然其他方法可能需要另一个数字,但这对我来说是一个问题,并且不在这个例子的范围内).

这是我现在拥有的代码:

double Speed = Math.Pow(2, ((-900 + Time) / -60)) - 1;
double RoundedSpeed = Math.Round(Speed);

最初我想用一个数组来存储要舍入的相关数字,但是有没有其他方法可以将 RoundedSpeed 舍入到最接近的以 2 为基数的整数?

我会使用循环:

double RoundedSpeed = Math.Round(Speed);
int maxAllowed = 100; // prevent infinite loop
for (int i=1; i < maxAllowed; i++)
{
    double number = Math.Pow(2.0, (double)i);
    if (number > RoundedSpeed)
        return number;
}

如果您不在 return 的方法中,则不要使用 return 数字,而是使用数字做一些有用的事情,然后添加 "break;" 以停止循环。

您需要添加错误处理以确保您不会溢出最大整数值,并且如果您在到达 maxAllowed 时没有找到数字则可以做一些有用的事情。可能有更好的解决方案,但这是 "easy" 解决方案...

您可以使用按位运算非常有效地执行此操作。我在这里使用 ulong 作为输入和输出。这将 运行 很快。

ulong FancyRound(ulong value) {
    value -= 1;
    value |= value >> 1;
    value |= value >> 2;
    value |= value >> 4;
    value |= value >> 8;
    value |= value >> 16;
    value |= value >> 32;
    return value + 1;
}

你可以这样使用它:

for (ulong i = 1; i < 10; i++) {
    Console.WriteLine("{0}: {1}", i, FancyRound(i));
}

输出:

1: 1
2: 2
3: 4
4: 4
5: 8
6: 8
7: 8
8: 8
9: 16

它的工作原理是 "cascading" 所有设置位向下,以便在所有 | 完成后,将设置所有低位,直到最重要的设置位。之后,将结果递增以获得 2 的整数次幂。

试试这个:

var x = 56;
var y = (int)Math.Pow(2.0, Math.Ceiling(Math.Log(x, 2.0)));

本例中的结果为 64。

实际上这是为 n 确定什么 2 ^ n = x,但由于 n 可能是小数,因此它使用 Ceiling 将此数字带到最接近的整数(如果它已经是整数,则保留它)。所以n2 = Ceiling(n)。然后它简单地计算 2 ^ n2 得到 y.

的结果

我会这样做,我没有对它进行基准测试,所以我不会对速度做出任何声明:

    long input = 32111;
    long powerTwo = 1;
    int count = 0;
    int maxCount = 63; //You can't return 2^64 in a long so 63 should be the max here, as we start at 2^0!
    while (input > powerTwo && count < maxCount) {
        count++;
        powerTwo <<= 1;
    }
    return powerTwo;

输出 32,768,之所以有效,是因为 long(或至少是下半部分)在内存中表示为 000000001(即 1),而 <<= 操作只是将位左移 1 并分配结果到 powerTwo 所以它在下一次迭代中变成 000000010(即 2),然后它只是计算出它比 input.

大时得到的值

如果您知道它会在溢出 long 之前终止,那么您可以删除计数内容。