views:

2620

answers:

7

In the C++ standard libraries I found only a floating point log method. Now I use log to find the level of an index in a binary tree ( floor(2log(index)) ).

Code (C++):

int targetlevel = int(log(index)/log(2));

I am afraid that for some of the edge elements (the elements with value 2^n) log will return n-1.999999999999 instead of n.0. Is this fear correct? How can I modify my statement so that it always will return a correct answer?

+5  A: 

You can use this method instead:

int targetlevel = 0;
while (index >>= 1) ++targetlevel;

Note: this will modify index. If you need it unchanged, create another temporary int.

The corner case is when index is 0. You probably should check it separately and throw an exception or return an error if index == 0.

Igor Krivokon
Does the while loop evaluate 0-integers to false?
Peter Smit
If index = 0, targetlevel is going to be 0. In your code it will probably cause exception. What value would you like to get for index = 0?
Igor Krivokon
I mean to say, the loop has to stop when index >>= 1 evaluates to 0. I couldn't find somewhere quickly that the while loop will really stop when the expression evaluates to an integer zero. It is of course logic it will, because the bits are the same then as boolean false.
Peter Smit
...actually, in your code it's not not exception - it will evaluate to minus infinity and then converted to int as maximum negative int value.
Igor Krivokon
Ah, yes, while loop will stop at 0. There were no booleans in C, and C++ follows the same rules.
Igor Krivokon
A: 

How deep do you project your tree to be? You could set a range of say... +/- 0.00000001 to the number to force it to an integer value.

I'm actually not certain you'll hit a number like 1.99999999 because your log2 should not lose any accuracy when calculating 2^n values (Since floating point rounds to the nearest power of 2).

CookieOfFortune
+1  A: 
int targetIndex = floor(log(i + 0.5)/log(2.0));
Andy
+5  A: 

If you just want a fast integer log2 operation, the following function will do it without having to worry about floating-point accuracy:

static unsigned int mylog2 (unsigned int val) {
    unsigned int ret = -1;
    while (val != 0) {
        val >>= 1;
        ret++;
    }
    return ret;
}

It will return -1 for an input value of 0, rather than the correct infinity (which is a little hard to represent in an integer :-)

By the way, there are some insanely fast hacks to do exactly this (find the highest bit set in a 2's complement number) available from here. I wouldn't suggest using them unless speed is of the essence (I prefer readability myself) but you should be made aware that they exist.

paxdiablo
+2  A: 

I've never had any problem with floating-point accuracy on the formula you're using (and a quick check of numbers from 1 to 231 - 1 found no errors), but if you're worried, you can use this function instead, which returns the same results and is about 66% faster in my tests:

int HighestBit(int i){
    if(i == 0)
        return -1;

    int bit = 31;
    if((i & 0xFFFFFF00) == 0){
        i <<= 24;
        bit = 7;
    }else if((i & 0xFFFF0000) == 0){
        i <<= 16;
        bit = 15;
    }else if((i & 0xFF000000) == 0){
        i <<= 8;
        bit = 23;
    }

    if((i & 0xF0000000) == 0){
        i <<= 4;
        bit -= 4;
    }

    while((i & 0x80000000) == 0){
        i <<= 1;
        bit--;
    }

    return bit; 
}
P Daddy
+6  A: 

If you are on a recent-ish x86 or x86-64 platform (and you probably are), use the bsr instruction which will return the position of the highest set bit in an unsigned integer. It turns out that this is exactly the same as log2(). Here is a short C or C++ function that invokes bsr using inline ASM:

#include <stdint.h>
static inline uint32_t log2(const uint32_t x) {
  uint32_t y;
  asm ( "\tbsr %1, %0\n"
      : "=r"(y)
      : "r" (x)
  );
  return y;
}
Matt J
And on ARM you'd want clz, which returns 31 minus the value you want. GCC has __builtin_clz, which presumably uses bsr on x86.
Steve Jessop
A: 

This isn't standard or necessarily portable, but it will in general work. I don't know how efficient it is.

Convert the integer index into a floating-point number of sufficient precision. The representation will be exact, assuming the precision is sufficient.

Look up the representation of IEEE floating-point numbers, extract the exponent, and make the necessary adjustment to find the base 2 log.

David Thornley