简体   繁体   中英

Rounding to the closest number in a given array of numbers

Before posting this I looked at both of these answers:

C# Finding Nearest Number in Array

Round integer to nearest high number in array

While the first was somewhat helpful with regard to the extension method mentioned in one of the answers, I was not able to understand it to the depth that would allow me to modify and implement it to suit my specific circumstance, and the second wasn't suitable to my needs due to its array being of a short length and if I was to use an array to store all the base 2 numbers up to 268 million, it would have a relatively large number of entries.

Basically what I'm trying to do here is take RoundedSpeed (Which has been rounded up to an integer using Math.Round ) and round it to the closest number (it must never round down, only up) that is the result of a power being applied to 2. (Example numbers include 16384, 32768, 65536, 131072, etc)

For this particular example, the number that I would want to round to is 32768. (Though other methods may want another number, but that is an issue for me to deal with and is out of the scope of this example).

Here's the code I have right now:

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

Originally I was looking at using an array to store the relevant numbers to round to, but is there any other way I could round up RoundedSpeed to the nearest base 2 integer?

Try this:

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

Results in 64 in this case.

Effectively this is determining for n what 2 ^ n = x , but since n is probably a fractional amount it uses Ceiling to bring this number up to the nearest integer (or leave it if it is already an integer). So n2 = Ceiling(n) . Then it simple computes 2 ^ n2 to get the result of y .

I would use a loop:

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;
}

If you aren't in a method that returns something, then instead of return number, do something useful with number and then add "break;" to stop the loop.

You'll want to add error handling to make sure you don't overflow the maximum integer value, and to do something useful if you don't find a number by the time you get to maxAllowed. There may be better solutions, but this is an "easy" solution...

You can do this very efficiently with bitwise operations. I've used ulong for input and output here. This will run very quickly.

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;
}

You can use it like this:

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

Output:

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

It works by "cascading" all the set bits down so that after all the | are complete, all the low bits will be set, up to the most significant set bit. After that, the result is incremented to get a round power of 2.

I would do it like this, I haven't benchmarked it so I won't make any claims about speed:

    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;

Outputs 32,768, it works because a long (or at least the lower half) is represented in memory as 000000001 (which is 1) and the <<= operation simply shifts the bits left 1 and assigns the result to powerTwo so it becomes 000000010 (which is 2) for the next iteration, then it just plonks out the value it got to when it was bigger than the input .

If you know it's going to terminate prior to overflowing a long then you can remove the count stuff.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM