Base-2 integer logarithm of 64-bit unsigned integer

3

Problem:

In your choice of language, write the shortest function that returns the floor of the base-2 logarithm of an unsigned 64-bit integer, or –1 if passed a 0. (Note: This means the return type must be capable of expressing a negative value.)

Test cases:

Your function must work correctly for all inputs, but here are a few which help illustrate the idea:

               INPUT ⟶ OUTPUT

                   0 ⟶ -1
                   1 ⟶  0
                   2 ⟶  1
                   3 ⟶  1
                   4 ⟶  2
                   7 ⟶  2
                   8 ⟶  3
                  16 ⟶  4
               65535 ⟶ 15
               65536 ⟶ 16
18446744073709551615 ⟶ 63

Rules:

  1. You can name your function anything you like.
  2. Character count is what matters most in this challenge.
  3. You will probably want to implement the function using purely integer and/or boolean artithmetic. However, if you really want to use floating-point calculations, then that is fine so long as you call no library functions. So, simply saying return n?(int)log2l(n):-1; in C is off limits even though it would produce the correct result. If you're using floating-point arithmetic, you may use *, /, +, -, and exponentiation (e.g., ** or ^ if it's a built-in operator in your language of choice). This restriction is to prevent "cheating" by calling log() or a variant.
  4. If you're using floating-point operations (see #3), you aren't required that the return type be integer; only that that the return value is an integer, e.g., floor(log₂(n)).
  5. If you're using C/C++, you may assume the existence of an unsigned 64-bit integer type, e.g., uint64_t as defined in stdint.h. Otherwise, just make sure your integer type is capable of holding any 64-bit unsigned integer.
  6. If your langauge does not support 64-bit integers (for example, Brainfuck apparently only has 8-bit integer support), then do your best with that and state the limitation in your answer title. That said, if you can figure out how to encode a 64-bit integer and correctly obtain the base-2 logarithm of it using 8-bit primitive arithmetic, then more power to you!
  7. Have fun and get creative!

Todd Lehman

Posted 2014-07-26T19:16:17.957

Reputation: 1 723

3Why the restriction to C? Language-specific challenges are generally frowned upon. Also, what's the meaning of the bonus? (And also I don't think there is any need to show two ungolfed solutions right away.) – Martin Ender – 2014-07-26T19:28:22.100

@MartinBüttner — Oh, ok, I didn't realize that. I'm new here (not to SX but to CG.SX). Thanks for pointing that out. I'll remove the restriction and delete the second example, and I'll eliminate the language-specific requirement. – Todd Lehman – 2014-07-26T19:30:32.880

@MartinBüttner — Actually, went ahead and deleted both examples. – Todd Lehman – 2014-07-26T19:33:02.090

6No floating point? There goes my best idea (inspired by the famous fast inverse square root.) Assign the number to float, cast it bitwise to an integer, and extract the exponent from it by rightshifting by a constant. – Level River St – 2014-07-26T19:36:04.437

@steveverrill — OK, I'll edit the question to allow floating-point so long as no external library functions are used. Looking forward to hearing your idea! – Todd Lehman – 2014-07-26T19:37:44.417

1

As you changed the rules for me I went ahead and posted :-) All questions on PPCG should have an objective winning criterion. My answer is not a winner under pure code golf. If it is your intention to reward creative answers, you should do so in an objective way. See this question for example: http://codegolf.stackexchange.com/q/23581/15599. Otherwise, you can delete your rule 3 and make it a pure code golf. I won't mind if you do that.

– Level River St – 2014-07-26T21:09:06.870

@steveverrill — I'll delete rule 3 and make it a pure code golf. That doesn't preclude someone from posting a perverse solution for fun. :) – Todd Lehman – 2014-07-26T21:24:25.783

If only I knew enough about x86 machine code to submit a 2-instruction LZCNT and subtract from 63... – user2357112 supports Monica – 2014-07-27T12:44:41.693

Answers

6

C 40 54

Edit Clever recursive trick by @Kyle - that's creative!

int l(uint64_t n){return n?l(n/2)+1:-1;}

(Previous version: That's the bare starting point - creativity level 0)

int l(uint64_t n){int r=-1;for(;n;n>>=1)r++;return r;}

Test: Ideone

edc65

Posted 2014-07-26T19:16:17.957

Reputation: 31 086

Nice. I can see how to shorten that by 1 character with either of {int r=-1;for(;n;n/=2)r++;return r;} or {int r=0;for(;n;n/=2)r++;return--r;}, but I can't see how to go any shorter than that. – Todd Lehman – 2014-07-26T23:35:44.760

4make it a ternary-recursive? return n?l(n/2)+1:-1; – Kyle McCormick – 2014-07-27T01:34:37.613

Daaaayammm, guys!! That is amazing work. That's not just creative; that's sick genius right there. – Todd Lehman – 2014-07-27T08:21:18.463

6

C,89

Per my comment on the question, here's a quirky way to do it, inspired by this famous function: http://en.wikipedia.org/wiki/Fast_inverse_square_root

f(uint64_t x){__float128 y=x;__int128_t i = *(__int128_t*)&y;return x?(i>>112)-16383:-1;}

I store the number as a float. Then to extract the exponent of the float, I cast it bitwise to an integer, rightshift the integer and subtract the bias.

Unfortunately to get the last example to run correctly, a 128 bit float is required. A 64 bit float has only 52 bits for the mantissa, so it rounds 18446744073709551615 up to 18446744073709551616 (2^64). The standard IEEE 128-bit float has a 112 bit mantissa (which we shift out and discard) and a bias of 16383 on the exponent. These are the constants you see in the function.

the requirement f(0)=-1 has to be handled with a ternary operator ?:. Otherwise it would return -16383.

Here's a complete program using type names per GCC. I can't get it to run on visual studio or ideone at the moment, will try later.

#include <stdint.h>

uint64_t a;

f(uint64_t x){
  __float128 y=x;
  __int128_t i = *(__int128_t*)&y;
  return x?(i>>112)-16383:-1;
}

main(){
  scanf("%llu",&a);
  printf("%llu %d",a,f(a)); 
}

Level River St

Posted 2014-07-26T19:16:17.957

Reputation: 22 049

Wicked cool. Can this method be adapted to use long double instead of __float128, assuming your compiler's long double is at least 80 bits? Because I know that at least on my compiler, which has long double of 80 bits, it works fine for all 64-bit unsigned integers to do return (int)log2l(x);. – Todd Lehman – 2014-07-26T21:16:04.747

1@Todd If your 80-bit long double can hold the 64-bit integer without rounding (I believe most do) you should be able to adapt this. I went with the first thing I found, some of the definitions were a bit vague, and it was guaranteed to work with 128 bits, so I didn't waste much time looking at 80 bits. You'll still need an integer larger than 64 to cast your 80-bit float into, though (unless you cast it into an array.) You might get away with casting to a 64 bit integer on big-endian machines, which are more likely to throw away the least significant bits than the most significant bits. – Level River St – 2014-07-26T22:23:56.330

6

Haskell, 24 bytes

Can't come remotely close to the Golfscript answer, but I think this one in Haskell has everything else beat so far...

f 0= -1;f n=f(div n 2)+1

E.g.: Running with the test cases provided gives:

> map f [0,1,2,3,4,7,8,16,65535,65536,18446744073709551615]
[-1,0,1,1,2,2,3,4,15,16,63]

mrputter

Posted 2014-07-26T19:16:17.957

Reputation: 61

Ah, a recursive solution! Very nice. – Todd Lehman – 2014-07-27T08:17:04.190

Of course, recursion is the bread and butter of haskell – proud haskeller – 2014-07-31T09:36:27.980

5

Golfscript 7 (or 11)

2base,(

or, if you want the actual function definition:

{2base,(}:f

you can test it here.

If you consider "base" to be cheating, then add two chars for:

{}{2/}/,(

Kyle McCormick

Posted 2014-07-26T19:16:17.957

Reputation: 3 651

Wow. That is clever. Convert it to a base-2 number, take the length, and decrement. Nice. – Todd Lehman – 2014-07-27T00:53:29.787

1I would consider a log function as cheating, but not base as you've used it, as you've basically stringified it (into an array) and measured the length. It's not quite in the spirit of what I'd been thinking (which was to use integer arithmetic) but every language has peculiar magical features, and this isn't your standard straightfoward cheat. I'm guessing nobody is going to do better than this one! – Todd Lehman – 2014-07-27T00:54:47.087

1Thanks! I actually like my alternative solution better (and I'd understand if you want to disallow "base" type operations - feel free). My alternative collects all the divisions by 2 until it reaches 0, then takes the size and decrements. – Kyle McCormick – 2014-07-27T01:01:46.933

1It is interesting that Golfscript's base function returns an empty array for the value 0, rather than [0]. That gives you the –1 with no extra effort. :) – Todd Lehman – 2014-07-27T01:02:23.533

1Yeah I always thought that feature of GS was weird but now it makes sense - it allows you to easily calculate logs in any base. – Kyle McCormick – 2014-07-27T01:07:22.007

1Your 9-character looping solution is awesome. Probably my favorite so far. It's 100% within the spirit of the question, and extremely terse. (Although, technically, it's not a callable function, so it's really only 99% within the spirit of the question. It's 4 more characters to make it an actual function definition then?) – Todd Lehman – 2014-07-27T01:10:49.393

Yeah I meant to imply that the same rule applies to that version - add 4 to make it callable. That's why I said "add two chars" instead of "make it 9 bytes", because it depends on which you were counting. – Kyle McCormick – 2014-07-27T01:24:59.307

Technically you should add a semicolon after f – aditsu quit because SE is EVIL – 2014-08-04T23:36:44.980

@aditsu I believe the semicolon is unnecessary for the function definition to be valid. Why? Because if the user wanted well-golfed code, they'd typically define it where they first use it. So the user of the code would finish it with a '~' (tilde). While declaring your functions at the top of your program and using them below is considered best practice in normal programming, doing so in codegolf is a waste of precious characters. – Kyle McCormick – 2014-08-05T00:41:20.343

4

Python 2, 26

t=lambda n:len(bin(n+n))-4

This is similar to the Python 3 answer by Tim S. However, doubling n and then subtracting 4 from the length has the advantage of working whether n is positive or zero.

If n > 0, then doubling n adds one to the binary length, so we compensate by subtracting 4 instead of 3. On the other hand, if n = 0, then the function returns -1 as desired.

mathmandan

Posted 2014-07-26T19:16:17.957

Reputation: 943

3

GNU dc, 30 bytes

[_1pq]sz?d0=z[d2/d0<m]dsmxz2-p

Takes input from STDIN. Counts the number of times we can divide by 2.

Test output:

$ for i in 0 1 2 3 4 7 8 16 65535 65536 18446744073709551615
> do echo $i | dc log.dc
> done
-1
0
1
1
2
2
3
4
15
16
63
$ 

Digital Trauma

Posted 2014-07-26T19:16:17.957

Reputation: 64 644

Ha! NICE. Wasn't expecting something like that! – Todd Lehman – 2014-07-26T23:18:32.110

3

J, 11 chars

Uses the length of the base2 representation but for 0 it yields 1 We add the signum of the original number and subtract 2 thus getting the desired values for all n>=0.

   (2-~*+#@#:) 18446744073709551615x  NB. x is for extended precision number
63

randomra

Posted 2014-07-26T19:16:17.957

Reputation: 19 909

your solution only 9 characters according to the usual scoring rules as you can omit the braces. – FUZxxl – 2015-02-15T20:41:06.427

2

Python 3, 38 bytes

def f(n):return(-1,len(bin(n))-3)[n>0]

bin(n) produces a string like 0b100, so you have to subtract 3, not just 1. (a,b)[condition] is a trick I took from Tips for golfing in Python.

Tim S.

Posted 2014-07-26T19:16:17.957

Reputation: 615

lambda x:len(bin(x))-3if x else-1 is what I came up with, but since it's so similar to yours, I'll give it as a golf tip. f=lambda x:x and len(bin(x))-3or-1 this would work, except that the output is 0 for 1, so it will incorrectly return -1. – mbomb007 – 2015-07-02T18:43:40.363

2

C, 72

Using a binary split method

int k(uint64_t x){int i=64,r=-!x;while(i/=2)x>>i?x>>=i,r+=i:0;return r;}

ungolfed, unwound version with lookup table options.

#define USETABLE256
int msb(unsigned long long x){
    char ret = -1;

    if (x>0xFFFFFFFF){ ret+=32; x>>=32; }
    if (x>0xFFFF){ ret+=16; x>>=16; }
    if (x>0xFF){  ret+=8;  x>>=8;  }
#ifdef USETABLE256
    return ret + ((const char[256]){
 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
    })[x];
#else
    if (x>0xF){        ret+=4;  x>>=4;  }
#ifdef USETABLE16
    return ret + ((const char[16]){0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4})[x];
#else
    if (x>3){        ret+=2;  x>>=2;  }
    if (x>1){        ret+=1;  x>>=1;  }
    return ret + x;
#endif
#endif
}

technosaurus

Posted 2014-07-26T19:16:17.957

Reputation: 231

Nice. Both of these run in essentially constant time then? Given that log₂64 is constant, that is? – Todd Lehman – 2014-07-27T04:53:14.133

Hey, it looks like you can shave off 5 additional characters from the (original version of) body of your looping version by doing this: int i=32,r=-!n;for(;i;i/=2)n>=1LL<<i?r+=i,n>>=i:0;return r;. That gets you down under 80 characters (to 78, if I'm subtracting correctly)! – Todd Lehman – 2014-07-27T05:15:40.503

technosaurus — I really like your looping version a lot because I think it is probably the fastest possible general way to do it. Have you benchmarked the looping version vs. the table-lookup version? One advantage the looping version has is that it doesn't have to do any memory accesses. – Todd Lehman – 2014-07-27T05:19:20.583

I see you took the suggested edits. :) You can also shave off one more character still by replacing i>>=1 with i/=2 :-) BTW, my apologizes for any confusion on n vs. x. (In my own test program, I was using n and I just cut & pasted it without thinking.) – Todd Lehman – 2014-07-27T05:20:57.920

1@ToddLehman - thanks changed - left the bitops in the unwound version (compiler normally does this for factors of 2 anyhow). I added the lookup table versions for systems where jumps are expensive compared to memory access. It will vary with architecture. Putting them as an inline const vs using a local variable helps with locality to try to prevent cache misses. – technosaurus – 2014-07-27T05:28:15.203

That's cool. BTW, you could shave yet off another character, bringing it down to 77 characters, by starting i at 64 instead of 32 and then combining the test and the shift: int i=64,r=-!n;while(i/=2)x>=1LL<<i?r+=i,x>>=i:0;return r; – Todd Lehman – 2014-07-27T05:41:40.320

Oh hey! You might like this! It's possible to shave off still another 5 characters (bringing it down to 72) by testing x>>i rather x>=1LL<<i. Check it out: int i=64,r=-!x;while(i/=2)x>>i?x>>=i,r+=i:0;return r;. I'm pretty sure now it's impossible to get it any shorter in C using this method. – Todd Lehman – 2014-07-27T06:09:08.897

1@ToddLehman nice, I used it to put together this macro that optimizes well for any integer type #define MSB(x) do{int i=(sizeof(x)*8),r=-!x;while(i>>=1)x>>i?x>>=i,r+=i:0;x=r;}while(0) – technosaurus – 2014-07-27T07:01:35.593

Awesome. I like that you wrapped it in a #define macro. MSB(x) can't be used as an rvalue, but at least it works for any integer size. – Todd Lehman – 2014-07-27T07:13:21.283

1@ToddLehman the compilers did not optimize /2 very well but with the sizeof part it can do any integer type without extra jumps .... oddly you can calculate the number of jumps by passing the number of bits to itself – technosaurus – 2014-07-27T17:01:28.110

technosaurus — That's odd; what compiler are you using? Do you mean that it generated different assembly code between i>>=1 and i/=2? (Wait...oh! Because i is int and not unsigned int?) – Todd Lehman – 2014-07-27T18:44:31.813

2

Befunge 93 - 23

1-&: v
v+1\ _$.@
>\2/:^

Limited by implementation to 2^31 or 32-bit signed ints. Given a 64 bit unsigned (128 bit signed?!) implementation this code meets criteria.

sig_seg_v

Posted 2014-07-26T19:16:17.957

Reputation: 147

2

TI-30XB - 19 (Instructions)

I won't actually participate in this contest with the following codes, but I found out this clever solution for my TI-30XB calculator: log(x)/log(2)+10^12-10^12. First of all, I wont participate because I clearly used the log function. Second, who actually has TI calculators... Third, this one's probably gonna win because its only 19 instructions. :D (Oh wow, but look at that golfscript code...) I just want to point out that there are more ways to floor a float, if any of you are interested. (For the C programmers its probably still smaller to use int's instead). By the way I am just abusing overflow handeling here. Since the TI-30XB stores floats, adding 10^12 to it will remove everything behind the dot.

TI-BASIC - 35 bytes

This one is a actual participant, but I bet none of you can execute it... Oh well just buy a TI-84 Plus then :D

:PROGRAM:LOG
:0→X
:If N=0
:-1→X
:While N>1
:iPart(N/2→N
:X+1→X
:END
:X

You would call the function (Or programs as they are called) like this:

:PROGRAM:TEST
:65535→N
:prgmLOG

X should now contain the value 15. Also, note that the X on the end of the program can actually be removed if the program is executed using prgmLOG (As shown above), since the X at the end is only used to display the number when the function is executed via the HOME screen. Yes, While, If, End and iPart( are one instruction each.

YoYoYonnY

Posted 2014-07-26T19:16:17.957

Reputation: 1 173

This is really clever! Nice solution. Fun to see! – Todd Lehman – 2015-01-12T05:15:08.593

Technically, these don't work as the calculators' floats are not precise enough to store 64 bits. If we allow them, then sum(Ans≥2^randIntNoRep(1,63 would also work. – lirtosiast – 2015-07-02T16:48:27.650

1

Ruby, 30 bytes

f=->n{n>0?n.to_s(2).size-1:-1}

E.g.

irb(main):019:0> f[0]
=> -1
irb(main):024:0> f[65535]
=> 15
irb(main):025:0> f[65536]
=> 16

Tim S.

Posted 2014-07-26T19:16:17.957

Reputation: 615

This converts n to a base-2 string and measures the length? – Todd Lehman – 2014-07-27T03:21:07.350

@Todd Yep! Also, it has to handle 0 as a special case. – Tim S. – 2014-07-27T03:27:19.333

1

Batch - 82

Due to language limitations, this only supports 32-bit ints

@set a=-2&set n=%1
:1
@set /aa=%a%+1&set /an=%n%/2&if %n% GTR 0 goto 1
echo %a%

Οurous

Posted 2014-07-26T19:16:17.957

Reputation: 7 916

1

PHP - 50 40

My final answer* inspired by @edc65, who was inspired to do a recursive version from @Kyle

function l($n){return $n?l($n>>1)+1:-1;}

My original version before the recursive answer was this:

function l($n){while($n){$n>>=1;$r++;}return$r-1;}

*I had to use the binary shift (>>) because division (/) kept making it a floating point, yielding wildy inaccurate/large answers (doing floating division until it ran out of decimal places and "became 0").
And casting to an (int) or using floor() cost more characters than the simple right shift.

JPMC

Posted 2014-07-26T19:16:17.957

Reputation: 161