1

Wondering if there is an API to calculate log_2 directly? Here is my current code, which I transfer log_2(N) to be log_e(N)/log_e(2).

BTW, it seems for normal Java Double type, there is no method to calculate log_2(double_value) directly?

My code in Java,

BigInteger x = BigInteger.valueOf(16);
BigInteger y = BigInteger.valueOf((long)(Math.log(x.longValue()) / Math.log(2)));
System.out.println(y.doubleValue()); // return 4.0 as expected
Lin Ma
  • 9,739
  • 32
  • 105
  • 175
  • 1
    "`Math.log(x.longValue())`" note that `log` takes a double, so you may as well use `x.doubleValue()` here, as it will be widened anyway. – Andy Turner Mar 16 '17 at 06:21
  • @AndyTurner, agree and nice catch. BTW, do you know if there is an API to calculate log_2 directly? – Lin Ma Mar 16 '17 at 06:23

2 Answers2

8

This is built in to the BigInteger API. From the Javadoc:

public int bitLength()

Returns the number of bits in the minimal two's-complement representation of this BigInteger, excluding a sign bit. For positive BigIntegers, this is equivalent to the number of bits in the ordinary binary representation. (Computes (ceil(log2(this < 0 ? -this : this+1))).)

Community
  • 1
  • 1
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • Hi Jim, nice method and vote up. Actually I am asking a more general question, suppose if want to calculate `log_x`, where `x` could be any number like `2,3,4,...`, then how do you calculate? I think `bigLength()` could only handle case of exponential of `2`, right? – Lin Ma Mar 16 '17 at 06:35
  • 1
    The only general method is the one you outlined. Convert to `double` and do the calculation there. `BigInteger` can represent values outside the range of `double` so you may lose information in extreme cases. – Jim Garrison Mar 16 '17 at 06:47
  • Thanks Jim, from your comments -- " may lose information in extreme cases", this is exactly my concern. Actually I am a bit surprised `BigInteger` does not provide a way to do log for any given base (but at the same time, a lot of other math computation are provided for `BigInteger`, not sure why `log` is missing). Any thoughts? – Lin Ma Mar 16 '17 at 06:56
  • BTW, Jim, it seems for double itself, it does not have a solution to calculate log for a general base? – Lin Ma Mar 16 '17 at 06:56
  • 1
    There's no need as `logBaseN(x) == log(x)/log(base)` as you showed in your code. As to the lack of `log` in `BigInteger`... you'd have to ask the language designers. – Jim Garrison Mar 16 '17 at 07:02
  • BigInteger.valueOf(16).bitLength() returns 5 but log2(16) is 4??? Should I subtract one? – Yolomep Jul 10 '21 at 15:35
  • `2^4` equals `16` which has 5 bits - `10000`. The suggestion to use `log(x)` is a red herring. – Jim Garrison Jul 10 '21 at 23:48
0

If you want partial bits:

const twoToThe50th = Math.pow(2, 50);
const log2BigInt = (x: bigint) => {
  let log = 0;
  while (x > twoToThe50th) {
    // Shift by 6 bytes to right to stay on byte boundaries
    x = x >> BigInt(48);
    log += 48;
  }
  // x is now small enough to be a Number, which we
  // can pass to JavaScript's built in log2 function
  return log + Math.log2(Number(x));
}
Stuart Schechter
  • 534
  • 6
  • 13
  • The question asked for an answer in Java. It appears that you submitted an answer in CoffeeScript. For much better performance, consider doing a binary search algorithm. Also, numbers can store 2**1024. Also, literals flow better with performance in JIT compilation than variables, so consider inlining twoToThe50th with its literal value. Also, repeatedly creating a new `BigInt` in the middle of a tight loop is a performance sinkhole. Also, consider applying integer optimizations to the log variable: `log = log + 48 |0;`. Also, consider localizing `Math.log2` and `Number`. I hope this helps – Jack G Feb 22 '20 at 23:24