Synchronizationlockexception on Monitor.Exit When Using Await

What's the quickest way to compute log2 of an integer in C#?

The Cleanest and Quickest... (works in .Net core 3.1 and up and takes the performance lead starting in .Net 5.0)

int val = BitOperations.Log2(x);

Runner up... (fastest in versions below .Net 5, 2nd place in .Net 5)

int val = (int)((BitConverter.DoubleToInt64Bits(x) >> 52) + 1) & 0xFF;

Notes:

  • The idea of using the exponent in a floating-point was inspired by SPWorley
    3/22/2009.
  • This also supports more than 32 bits. I have not tested the max but did go to at least 2^38.
  • Use with caution on production code since this can possibly fail on architectures that are not little-endianness.

Here are some benchmarks: (code here: https://github.com/SunsetQuest/Fast-Integer-Log2)

                                      1-2^32                  32-Bit  Zero 
Function Time1 Time2 Time3 Time4 Time5 Errors Support Support
Log2_SunsetQuest3 18 18 79167 19 18 255 N N
Log2_SunsetQuest4 18 18 86976 19 18 0 Y N
LeadingZeroCountSunsetq - - - 30 20 0 Y Y
Log2_SPWorley 18 18 90976 32 18 4096 N Y
MostSigBit_spender 20 19 86083 89 87 0 Y Y
Log2_HarrySvensson 26 29 93592 34 31 0 Y Y
Log2_WiegleyJ 27 23 95347 38 32 0 Y N
Leading0Count_phuclv - - - 33 20 10M N N
Log2_SunsetQuest1 31 28 78385 39 30 0 Y Y
HighestBitUnrolled_Kaz 33 33 284112 35 38 2.5M N Y
Log2_Flynn1179 58 52 96381 48 53 0 Y Y
BitOperationsLog2Sunsetq - - - 49 15 0 Y Y
GetMsb_user3177100 58 53 100932 60 59 0 Y Y
Log2_Papayaved 125 60 119161 90 82 0 Y Y
FloorLog2_SN17 102 43 121708 97 92 0 Y Y
Log2_DanielSig 28 24 960357 102 105 2M N Y
FloorLog2_Matthew_Watso 29 25 94222 104 102 0 Y Y
Log2_SunsetQuest2 118 140 163483 184 159 0 Y Y
Msb_version 136 118 1631797 212 207 0 Y Y
Log2_SunsetQuest0 206 202 128695 212 205 0 Y Y
BitScanReverse2 228 240 1132340 215 199 2M N Y
Log2floor_version 89 101 2 x 10^7 263 186 0 Y Y
UsingStrings_version 2346 1494 2 x 10^7 2079 2122 0 Y Y

Zero_Support = Supports Zero if the result is 0 or less
Full-32-Bit = Supports full 32-bit (some just support 31 bits)
Time1 = benchmark for sizes up to 32-bit (same number tried for each size)
Time2 = benchmark for sizes up to 16-bit (for measuring perf on small numbers)
Time3 = time to run entire 1-2^32 in sequence using Parallel.For. Most results range will on the larger end like 30/31 log2 results. (note: because random was not used some compiler optimization might have been applied so this result might not be accurate)
Time4 = .Net Core 3.1
Time5 = .Net 5

Benchmark notes: AMD Ryzen CPU, Release mode, no-debugger attached, .net core 3.1

I really like the one created by spender in another post. This one does not have the potential architecture issue and it also supports Zero while maintaining almost the same performance as the float method from SPWorley.

Update 3/13/2020: Steve noticed that there were some errors in Log2_SunsetQuest3 that were missed.

Update 4/26/2020: Added new .Net Core 3's BitOperations.LeadingZeroCount() as pointed out by phuclv.

Fastest way to compute log_2(n) where n is of form 2^k?

This page gives a half-dozen different ways to do that in C; it should be trivial to change them to C#. Try them all, see which is fastest for you.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious

However, I note that those techniques are designed to work for all 32 bit integers. If you can guarantee that the input is always a power of two between 1 and 2^31 then you can probably do better with a lookup table. I submit the following; I have not performance tested it, but I see no reason why it oughtn't to be pretty quick:

static int[] powers = new[] {0, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 
30, 28, 11, 0, 13, 4, 7, 17, 0, 25, 22,
31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
20, 8, 19, 18};

static int Log2OfAPower(int x)
{
return powers[((uint)x) % 37]
}

The solution relies upon the fact that the first 32 powers of two each have a different remainder when divided by 37.

If you need it to work on longs then use 67 as the modulus; I leave you to work out the correct values for the array.

Commenter LukeH correctly points out that it is bizarre to have a function that purportedly takes the log of a negative number (1<<31 is a negative signed integer.) The method could be modified to take a uint, or it could be made to throw an exception or assertion if given a number that doesn't meet the requirements of the method. Which is the right thing to do is not given; the question is somewhat vague as to the exact data type that is being processed here.

CHALLENGE:

Hypothesis: The first n powers of two each have a different modulus when divided by p, where p is the smallest prime number that is larger than n.

If the hypothesis is true then prove it. If the hypothesis is false then provide a counter-example that demonstrates its falsity.

Calculating Log base 2

Math.Log(num) returns the log of base e

Math.Log(num, base) is probably what you are looking for.

Fast computing of log2 for 64-bit integers

Intrinsic functions are really fast, but still are insufficient for a truly cross-platform, compiler-independent implementation of log2. So in case anyone is interested, here is the fastest, branch-free, CPU-abstract DeBruijn-like algorithm I've come to while researching the topic on my own.

const int tab64[64] = {
63, 0, 58, 1, 59, 47, 53, 2,
60, 39, 48, 27, 54, 33, 42, 3,
61, 51, 37, 40, 49, 18, 28, 20,
55, 30, 34, 11, 43, 14, 22, 4,
62, 57, 46, 52, 38, 26, 32, 41,
50, 36, 17, 19, 29, 10, 13, 21,
56, 45, 25, 31, 35, 16, 9, 12,
44, 24, 15, 8, 23, 7, 6, 5};

int log2_64 (uint64_t value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
return tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
}

The part of rounding down to the next lower power of 2 was taken from Power-of-2 Boundaries and the part of getting the number of trailing zeros was taken from BitScan (the (bb & -bb) code there is to single out the rightmost bit that is set to 1, which is not needed after we've rounded the value down to the next power of 2).

And the 32-bit implementation, by the way, is

const int tab32[32] = {
0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31};

int log2_32 (uint32_t value)
{
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return tab32[(uint32_t)(value*0x07C4ACDD) >> 27];
}

As with any other computational method, log2 requires the input value to be greater than zero.

Faster Log2Ceil

See the answer to this question. The code it references is in C, but most of it works in C# as well.

Alternatively, you can use

private int Log2Ceil(int n) {
long bits = BitConverter.DoubleToInt64Bits((double)n);
return ((int)(bits >> 52) & 0x7ff) - 1022;
}

which uses the fact that floating-point numbers contain an encoding of the binary exponent. A quick benchmark showed this to be 13x faster than your original on x64 and about 21x faster on x86.

performance of log10 function returning an int

The operation can be done in (fast) constant time on any architecture that has a count-leading-zeros or similar instruction (which is most architectures). Here's a C snippet I have sitting around to compute the number of digits in base ten, which is essentially the same task (assumes a gcc-like compiler and 32-bit int):

unsigned int baseTwoDigits(unsigned int x) {
return x ? 32 - __builtin_clz(x) : 0;
}

static unsigned int baseTenDigits(unsigned int x) {
static const unsigned char guess[33] = {
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 9
};
static const unsigned int tenToThe[] = {
1, 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000, 1000000000,
};
unsigned int digits = guess[baseTwoDigits(x)];
return digits + (x >= tenToThe[digits]);
}

GCC and clang compile this down to ~10 instructions on x86. With care, one can make it faster still in assembly.

The key insight is to use the (extremely cheap) base-two logarithm to get a fast estimate of the base-ten logarithm; at that point we only need to compare against a single power of ten to decide if we need to adjust the guess. This is much more efficient than searching through multiple powers of ten to find the right one.

If the inputs are overwhelmingly biased to one- and two-digit numbers, a linear scan is sometimes faster; for all other input distributions, this implementation tends to win quite handily.

How can I compute a base 2 logarithm without using the built-in math functions in C#?

For the BigInteger you could use the toByteArray() method and then manually find the most significant 1 and count the number of zeroes afterward. This would give you the base-2 logarithm with integer precision.

Fastest way to calculate the decimal length of an integer? (.NET)

Two suggestions:

  1. Profile and put the common cases first.
  2. Do a binary search to minimize the number of comparions in the worst case. You can decide among 8 alternatives using exactly three comparisons.

This combination probably doesn't buy you much unless the distribution is very skew.



Related Topics



Leave a reply



Submit