For some reason, I'm getting the correct result, but my negative sign is having issues.
For example, if I do 1/4 - (-2/4), I get (-3/4).
Here is my minus method for subtracting fractions.
/**
Subtracts a fraction from another fraction.
#param toUse, the fraction to subtract.
#return minusFraction, the result after subtraction.
*/
public Fraction minus(Fraction toUse)
{
int newNum = ((toUse.numerator * denominator)
- (numerator * toUse.denominator));
int newDen = denominator * toUse.denominator;
Fraction minusFraction = new Fraction(newNum, newDen);
return minusFraction;
}
Here is my reduce() method, just in case...
/**
Reduces the fraction, if possible, to it's simplest form.
Converts negative fractions to the form -x/y, or if -x/-y --> x/y
*/
private void reduce()
{
int lowest = Math.abs(numerator);
int highest = Math.abs(denominator);
if (lowest > highest)
{
int temp = highest;
highest = lowest;
lowest = temp;
}
while (lowest != 0)
{
int temp = lowest;
lowest = highest % lowest;
highest = temp;
}
numerator /= highest;
denominator /= highest;
if (denominator < 0)
{
numerator *= -1;
denominator *= -1;
}
}
I only switched an operator from my previous addition method, given here as well. I think only switching the + to a - may have caused my issue.
/**
Adds two fractions together.
#param toUse, the fraction to be added.
#return plusFraction, the sum of the two fractions.
*/
public Fraction plus(Fraction toUse)
{
int newNum = ((toUse.numerator * denominator)
+ (numerator * toUse.denominator));
int newDen = denominator * toUse.denominator;
Fraction plusFraction = new Fraction(newNum, newDen);
return plusFraction;
}
i think the problem is in your Fraction Minus function:
public Fraction minus(Fraction toUse)
{
int newNum = ( (numerator * toUse.denominator)-
(toUse.numerator * denominator)
);
int newDen = denominator * toUse.denominator;
Fraction minusFraction = new Fraction(newNum, newDen);
return minusFraction;
}
ex:
1/4-(-2/4).....
here the toUse.numerator is -2 and toUseDenominator is 4
what your code was doing it made your toUse fraction(-2/4) as base fraction and was getting subtracted from the original base Fraction(1/4) i.e (-2/4)-(1/4)...hence the result -3/4
hopefully it works
Two problems:
You are subtracting in the wrong order.
Your javadoc says:
/**
Subtracts a fraction from another fraction.
#param toUse, the fraction to subtract.
#return minusFraction, the result after subtraction.
*/
Which implies this.numerator - toUse.numerator
But here you do the opposite:
int newNum = ((toUse.numerator * denominator)
- (numerator * toUse.denominator));
Which, when you call 1/4.minus(-2/4) ends up computing:
int newNum = (-2*4) - (1*4) = -9
int newDen = 4 * -4 = -16
// Your new fraction will have -9/-16 which you reduce to -3/-4
// (which is how you hold -3/4)
Just switch the code to:
/**
Subtracts a fraction from another fraction.
#param toUse, the fraction to subtract.
#return minusFraction, the result after subtraction.
*/
public Fraction minus(Fraction toUse)
{
int newNum = ((numerator * toUse.denominator)
- (toUse.numerator * denominator));
int newDen = denominator * toUse.denominator;
Fraction minusFraction = new Fraction(newNum, newDen);
return minusFraction;
}
Now when
int newNum = (1*4) - (-2*4) = 9
int newDen = 4 * -4 = -16
This brings us to the second problem. Your reduce method relies on the denominator to determine if the number is negative or positive. But it is the numerator that should really carry the sign because you do the subtraction on the numerator.
Related
I am currently doing MIT's OCW 6.005 Elements of Software Construction for self- study.
I am on problem set 1.
The part I am currently stuck on is 1.d. It asks me to "Implement computePiInHex() in PiGenerator. Note that this function should
only return the fractional digits of Pi, and not the leading 3."
Here is what I have so far:
public class PiGenerator {
/**
* Returns precision hexadecimal digits of the fractional part of pi.
* Returns digits in most significant to least significant order.
*
* If precision < 0, return null.
*
* #param precision The number of digits after the decimal place to
* retrieve.
* #return precision digits of pi in hexadecimal.
*/
public static int[] computePiInHex(int precision) {
if(precision < 0) {
return null;
}
return new int[0];
}
/**
* Computes a^b mod m
*
* If a < 0, b < 0, or m < 0, return -1.
*
* #param a
* #param b
* #param m
* #return a^b mod m
*/
public static int powerMod(int a, int b, int m) {
if(a < 0 || b < 0 || m < 0)
return -1;
return (int) (Math.pow(a, b)) % m;
}
/**
* Computes the nth digit of Pi in base-16.
*
* If n < 0, return -1.
*
* #param n The digit of Pi to retrieve in base-16.
* #return The nth digit of Pi in base-16.
*/
public static int piDigit(int n) {
if (n < 0) return -1;
n -= 1;
double x = 4 * piTerm(1, n) - 2 * piTerm(4, n) -
piTerm(5, n) - piTerm(6, n);
x = x - Math.floor(x);
return (int)(x * 16);
}
private static double piTerm(int j, int n) {
// Calculate the left sum
double s = 0;
for (int k = 0; k <= n; ++k) {
int r = 8 * k + j;
s += powerMod(16, n-k, r) / (double) r;
s = s - Math.floor(s);
}
// Calculate the right sum
double t = 0;
int k = n+1;
// Keep iterating until t converges (stops changing)
while (true) {
int r = 8 * k + j;
double newt = t + Math.pow(16, n-k) / r;
if (t == newt) {
break;
} else {
t = newt;
}
++k;
}
return s+t;
}
}
The main method initiates a constant and then calls computePiInHex().
public static final int PI_PRECISION = 10000;
int [] piHexDigits = PiGenerator.computePiInHex(PI_PRECISION);
From my knowledge, the method piDigits() gets the nth digit of pi in base 16. So in computePiHex, should implement the BBP digit-extraction algorithm for Pi? Otherwise could someone point me in the right direction because I don't know what they're asking for in computePiInHex().
Try a different powermod algorithm.
For me this article helped a lot:
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-005-elements-of-software-construction-fall-2011/assignments/MIT6_005F11_ps1.pdf
The actual algorithm I used is based on the pseudocode from Applied Cryptography by Bruce Schneier
https://en.m.wikipedia.org/wiki/Modular_exponentiation
function modular_pow(base, exponent, modulus)
if modulus = 1 then return 0
Assert :: (modulus - 1) * (modulus - 1) does not overflow base
result := 1
base := base mod modulus
while exponent > 0
if (exponent mod 2 == 1):
result := (result * base) mod modulus
exponent := exponent >> 1
base := (base * base) mod modulus
return result
I'm having trouble figuring out when to use int, double and long.
I'm working on calculating the power of an integer and return the result as long as the power provided is not a negative number.
For the assignment, I'm required to use the following code to start:
public static long powerN(int number, int power) {
Here's what I came up with:
public class PowerCalculator
{
/**
* Calculate the non-negative power of an integer number. If a negative power is input, the method returns 1.
*
* #param number The number to take power.
* #param power The power factor to be taken to.
* #return The calculation result after taking power of the integer number.
*/
public static long powerN(int number, int power) {
if (power > 0)
{
double result = (Math.pow(number, power));
return result;
}
else
{
return 1;
}
}
}
I know I'm messing up the use of int, double or long but I don't know how to fix it.
Wait! If you're doing it yourself, use a faster algorithm like Exponentiation by squaring, something like this:
long powerN(long number, int power){
long res = 1;
long sq = number;
while(power > 0){
if(power % 2 == 1){
res *= sq;
}
sq = sq * sq;
power /= 2;
}
return res;
}
You could do it by yourself:
public static long powerN(
int number,
int power
) {
if(power == 0) return 1;
int result = number;
while(power > 1) {
result*=number;
power--;
}
return (long)result;
}
PS: This does not handle negative powers.
In case you'd like to use Math.pow:
public static long powerN(int number, int power) {
return (long) Math.pow(number, power);
}
Option 1: Cast result of Math.pow to long:
public class PowerCalculator{
/**
* Calculate the non-negative power of an integer number. If a negative power is input, the method returns 1.
*
* #param number The number to take power.
* #param power The power factor to be taken to.
* #return The calculation result after taking power of the integer number.
*/
public static long powerN(int number, int power) {
// write your code after this line
if (power < 0){
return 1;
} else{
return (long) Math.pow(number, power);
}
Option 2: without using Math.pow
public class PowerCalculator{
/**
* Calculate the non-negative power of an integer number. If a negative power is input, the method returns 1.
*
* #param number The number to take power.
* #param power The power factor to be taken to.
* #return The calculation result after taking power of the integer number.
*/
public static long powerN(int number, int power) {
// write your code after this line
long result = 1;
while (power > 0) {
result *= number;
power--;
}
return result;
}
I am trying to print the contents of a of my object Fraction(), but it keeps giving me a lot of errors and referring me back to where my print statements start. I have a toString() method that I created, but that does not seem to work either. Here is my class and tester class:
package fraction;
public class Fraction
{
/**
* The numerator and denominator.
*/
private int top;
private int bottom;
/**
* Creates the fraction 0/1
*/
public Fraction()
{
top = 0;
bottom = 1;
}
/**
* Creates the fraction n/1
* #param n an integer
*/
public Fraction(int n)
{
top = n;
bottom = 1;
}
/**
* Creates a fraction with the specified numerator and denominator
* #param n the numerator
* #param d the denominator
*/
public Fraction(int n, int d)
{
top = n;
bottom = d;
if (bottom == 0) {
throw new IllegalArgumentException();
}
}
/**
* Computes the sum of this fraction and the specified fraction
* #param f a fraction
* #return the fraction obtained by adding this fraction from the specified fraction
*/
public Fraction add(Fraction f)
{
return new Fraction(this.numerator() * f.denominator() + f.numerator() * this.denominator());
}
/**
* Compares this fraction and the specified fraction
* #param f a fraction
* #return 0 when this fraction is equal to the specified fraction; 1 when this fraction is greater than the specified
* fraction; -1 otherwise
*/
public int compareTo(Fraction f)
{
if (top > f.numerator())
return 1;
else if (top < f.numerator())
return -1;
else if (bottom > f.denominator())
return 1;
else if (bottom < f.denominator())
return -1;
else
return 0;
}
/**
* Gives the denominator of this fraction
* #return the denominator of this fraction
*/
public int denominator()
{
return bottom;
}
/**
* Gives the quotient of this fraction and the specified fraction
* #param f a fraction
* #return the fraction obtained by dividing this fraction by the specified fraction
* #throws IllegalArgumentException when the numerator equals 0
*/
public Fraction divide(Fraction f)
{
if (f.numerator() == 0) {
throw new IllegalArgumentException();
}
else
{
return new Fraction(this.numerator() * f.numerator(), this.denominator() * f.denominator());
}
}
/**
* Determines whether this fraction is equal to the specified fraction
* #param f a fraction
* #return true when this fraction is equal to the specified fraction; otherwise, false
*/
public boolean equals(Fraction f)
{
return top == f.numerator() && bottom == f.denominator();
}
/**
* Computes the greatest common divisor of the specified numbers
* #param num1 an integer
* #param num2 an integer
* #return the greatest common divisor of the specified parameters
*/
private int gcd(int num1, int num2)
{
if (num2 == 0) {
return num1;
}
else
{
return gcd(num2, num1%num2);
}
}
/**
* Calculates the product of this fraction and the specified fraction
* #param f a fraction
* #return the product of this fraction and the specified fraction
*/
public Fraction multiply(Fraction f)
{
return new Fraction(this.numerator() * f.numerator(), this.denominator() * f.denominator());
}
/**
* Simplifies this fraction by expressing its numerator and denominator in standard form:
* the denominator is positive and the numerator and denominator are relative primes
*/
public void normalize()
{
int gcd = gcd(top,bottom);
top /= gcd;
bottom /= gcd;
}
/**
* Gives the numerator of this fraction
* #return the numerator of this fraction
*/
public int numerator()
{
return top;
}
/**
* Computes the reciprocal of this fraction
* #return the reciprocal of this fraction
* #throws IllegalArgumentException when the numerator is equal to 0
*/
public Fraction reciprocal()
{
if (top == 0) {
throw new IllegalArgumentException();
}
int temp;
temp = numerator();
top = denominator();
bottom = temp;
return new Fraction(top, bottom);
}
/**
* Modifies this fraction
* #param n the new numerator of this fraction
* #param d the new denominator of this fraction
* #throws IllegalArgumentException when the denominator equals 0
*/
public void setFraction(int n, int d)
{
if (d == 0) {
throw new IllegalArgumentException();
}
top = n;
bottom = d;
}
/**
* Computes the difference of this fraction and the specified fraction
* #param f a fraction
* #return the fraction obtained by subtracting this fraction from the specified fraction
*/
public Fraction subtract(Fraction f)
{
return new Fraction(this.numerator() * f.denominator() - f.numerator() * this.denominator(), this.denominator() * f.denominator());
}
/**
* Gives a string representing this fraction in in-line notation, numerator/denominator
* #return a string representing this fraction in in-line notation, numerator/denominator
*/
#Override public String toString()
{
return String.format("%d/%d",top,bottom);
}
}
package fraction;
import java.util.Scanner;
public class FractionTester
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter the numerator and denominator of f1 > ");
int n1 = in.nextInt(), d1 = in.nextInt();
System.out.print("Enter the numerator and denominator of f2 > ");
int n2 = in.nextInt(), d2 = in.nextInt();
System.out.print("Enter the numerator and denominator of f3 > ");
int n3 = in.nextInt(), d3 = in.nextInt();
System.out.print("Enter the numerator and denominator of f4 > ");
int n4 = in.nextInt(), d4 = in.nextInt();
System.out.println();
Fraction f1 = new Fraction(n1, d1);
Fraction f2 = new Fraction(n2, d2);
Fraction f3 = new Fraction(n3, d3);
Fraction f4 = new Fraction(n4, d4);
System.out.printf("f1 = %d", f1);
System.out.printf("f2 = %d", f2);
System.out.printf("f3 = %d", f3);
System.out.printf("f4 = %d", f4);
if (f1.compareTo(f2) == 0) {
System.out.println("f1 = f2");
}
else if (f1.compareTo(f2) < 0) {
System.out.println("f1 < f2");
}
else {
System.out.println("f1 > f2");
}
if (f3.compareTo(f4) == 0) {
System.out.println("f1 = f2");
}
else if (f3.compareTo(f4) < 0) {
System.out.println("f1 < f2");
}
else {
System.out.println("f1 > f2");
}
System.out.println();
}
}
I am trying to print the contents of the objects f1, f2, f3, and f4. It says my error is on the first print statement (System.out.printf("f1 = %d", f1);).
Your fraction is not a number (%d) but an Object of which the toString() method should be called. Change %d to %s and it will work, i.e.
System.out.printf("f1 = %s", f1);
That's not how toString() works, change:
System.out.printf("f1 = %d", f1)
to:
System.out.println("f1 = "+ f1)
When you print an object its toString() will be called, which will return a String, while you where trying to print %d (a digit).
I'm new to Java and I've been trying to implement an algorithm for finding the roots of a cubical equation. The problem arises when I calculate the discriminant and try to check where it falls relative to zero.
If you run it and enter the numbers "1 -5 8 -4", the output is as follows:
1 -5 8 -4
p=-0.333333, q=0.074074
disc1=0.001372, disc2=-0.001372
discriminant=0.00000000000000001236
Discriminant is greater than zero.
I know the problem arises because the calculations with doubles are not precise. Normally the discriminant should be 0, but it ends up being something like 0.00000000000000001236.
My question is, what is the best way to avoid this? Should I check if the number falls between an epsilon neighborhood of zero? Or is there a better and more precise way?
Thank you in advance for your answers.
import java.util.Scanner;
class Cubical {
public static void main(String[] args) {
// Declare the variables.
double a, b, c, d, p, q, gamma, discriminant;
Scanner userInput = new Scanner(System.in);
a = userInput.nextDouble();
b = userInput.nextDouble();
c = userInput.nextDouble();
d = userInput.nextDouble();
// Calculate p and q.
p = (3*a*c - b*b) / (3*a*a);
q = (2*b*b*b) / (27*a*a*a) - (b*c) / (3*a*a) + d/a;
// Calculate the discriminant.
discriminant = (q/2)*(q/2) + (p/3)*(p/3)*(p/3);
// Just to see the values.
System.out.printf("p=%f, q=%f\ndisc1=%f, disc2=%f\ndiscriminant=%.20f\n", p, q, (q/2)*(q/2), (p/3)*(p/3)*(p/3), (q/2)*(q/2) + (p/3)*(p/3)*(p/3));
if (discriminant > 0) {
System.out.println("Discriminant is greater than zero.");
}
if (discriminant == 0) {
System.out.println("Discriminant is equal to zero.");
}
if (discriminant < 0) {
System.out.println("Discriminant is less than zero.");
}
}
}
The simplest epsilon check is
if(Math.abs(value) < ERROR)
a more complex one is proportional to the value
if(Math.abs(value) < ERROR_FACTOR * Math.max(Math.abs(a), Math.abs(b)))
In your specific case you can:
if (discriminant > ERROR) {
System.out.println("Discriminant is greater than zero.");
} else if (discriminant < -ERROR) {
System.out.println("Discriminant is less than zero.");
} else {
System.out.println("Discriminant is equal to zero.");
}
Should I check if the number falls between an epsilon neighborhood of
zero?
Exactly
Here's solution that is precise when the input values are integers, though it is probably not the most practical.
It will probably also work fine on input values that have a finite binary representation (eg. 0.125 does, but 0.1 doesn't).
The trick: Remove all divisions from the intermediate results and only divide once at the end. This is done by keeping track of all the (partial) numerators and denominators. If the discriminant should be 0 then it's numerator will be 0. No round-off error here as long as values at intermediate additions are within a magnitude of ~2^45 from each other (which is usually the case).
// Calculate p and q.
double pn = 3 * a * c - b * b;
double pd = 3 * a * a;
double qn1 = 2 * b * b * b;
double qd1 = 27 * a * a * a;
double qn2 = b * c;
double qn3 = qn1 * pd - qn2 * qd1;
double qd3 = qd1 * pd;
double qn = qn3 * a + d * qd3;
double qd = qd3 * a;
// Calculate the discriminant.
double dn1 = qn * qn;
double dd1 = 4 * qd * qd;
double dn2 = pn * pn * pn;
double dd2 = 27 * pd * pd * pd;
double dn = dn1 * dd2 + dn2 * dd1;
double dd = dd1 * dd2;
discriminant = dn / dd;
(only checked on the provided input values, so tell me if something's wrong)
maybe BigDecimal is worth a look at...
http://download.oracle.com/javase/1.4.2/docs/api/java/math/BigDecimal.html
you can secify the round mode in the divide-operation
I have a BigInteger number, for example beyond 264.
Now i want to calculate the logarithm of that BigInteger number, but the method BigInteger.log() does not exist. How do I calculate the (natural) logarithm of my large BigInteger value?
If you want to support arbitrarily big integers, it's not safe to just do
Math.log(bigInteger.doubleValue());
because this would fail if the argument exceeds the double range (about 2^1024 or 10^308, i.e. more than 300 decimal digits ).
Here's my own class that provides the methods
double logBigInteger(BigInteger val);
double logBigDecimal(BigDecimal val);
BigDecimal expBig(double exponent);
BigDecimal powBig(double a, double b);
They work safely even when the BigDecimal/BigInteger are too big (or too small) to be representable as a double type.
import java.math.*;
/**
* Provides some mathematical operations on {#code BigDecimal} and {#code BigInteger}.
* Static methods.
*/
public class BigMath {
public static final double LOG_2 = Math.log(2.0);
public static final double LOG_10 = Math.log(10.0);
// numbers greater than 10^MAX_DIGITS_10 or e^MAX_DIGITS_E are considered unsafe ('too big') for floating point operations
private static final int MAX_DIGITS_10 = 294;
private static final int MAX_DIGITS_2 = 977; // ~ MAX_DIGITS_10 * LN(10)/LN(2)
private static final int MAX_DIGITS_E = 677; // ~ MAX_DIGITS_10 * LN(10)
/**
* Computes the natural logarithm of a {#link BigInteger}
* <p>
* Works for really big integers (practically unlimited), even when the argument
* falls outside the {#code double} range
* <p>
*
*
* #param val Argument
* #return Natural logarithm, as in {#link java.lang.Math#log(double)}<br>
* {#code Nan} if argument is negative, {#code NEGATIVE_INFINITY} if zero.
*/
public static double logBigInteger(BigInteger val) {
if (val.signum() < 1)
return val.signum() < 0 ? Double.NaN : Double.NEGATIVE_INFINITY;
int blex = val.bitLength() - MAX_DIGITS_2; // any value in 60..1023 works here
if (blex > 0)
val = val.shiftRight(blex);
double res = Math.log(val.doubleValue());
return blex > 0 ? res + blex * LOG_2 : res;
}
/**
* Computes the natural logarithm of a {#link BigDecimal}
* <p>
* Works for really big (or really small) arguments, even outside the double range.
*
* #param val Argument
* #return Natural logarithm, as in {#link java.lang.Math#log(double)}<br>
* {#code Nan} if argument is negative, {#code NEGATIVE_INFINITY} if zero.
*/
public static double logBigDecimal(BigDecimal val) {
if (val.signum() < 1)
return val.signum() < 0 ? Double.NaN : Double.NEGATIVE_INFINITY;
int digits = val.precision() - val.scale();
if (digits < MAX_DIGITS_10 && digits > -MAX_DIGITS_10)
return Math.log(val.doubleValue());
else
return logBigInteger(val.unscaledValue()) - val.scale() * LOG_10;
}
/**
* Computes the exponential function, returning a {#link BigDecimal} (precision ~ 16).
* <p>
* Works for very big and very small exponents, even when the result
* falls outside the double range.
*
* #param exponent Any finite value (infinite or {#code Nan} throws {#code IllegalArgumentException})
* #return The value of {#code e} (base of the natural logarithms) raised to the given exponent,
* as in {#link java.lang.Math#exp(double)}
*/
public static BigDecimal expBig(double exponent) {
if (!Double.isFinite(exponent))
throw new IllegalArgumentException("Infinite not accepted: " + exponent);
// e^b = e^(b2+c) = e^b2 2^t with e^c = 2^t
double bc = MAX_DIGITS_E;
if (exponent < bc && exponent > -bc)
return new BigDecimal(Math.exp(exponent), MathContext.DECIMAL64);
boolean neg = false;
if (exponent < 0) {
neg = true;
exponent = -exponent;
}
double b2 = bc;
double c = exponent - bc;
int t = (int) Math.ceil(c / LOG_10);
c = t * LOG_10;
b2 = exponent - c;
if (neg) {
b2 = -b2;
t = -t;
}
return new BigDecimal(Math.exp(b2), MathContext.DECIMAL64).movePointRight(t);
}
/**
* Same as {#link java.lang.Math#pow(double,double)} but returns a {#link BigDecimal} (precision ~ 16).
* <p>
* Works even for outputs that fall outside the {#code double} range.
* <br>
* The only limitation is that {#code b * log(a)} cannot exceed the {#code double} range.
*
* #param a Base. Should be non-negative
* #param b Exponent. Should be finite (and non-negative if base is zero)
* #return Returns the value of the first argument raised to the power of the second argument.
*/
public static BigDecimal powBig(double a, double b) {
if (!(Double.isFinite(a) && Double.isFinite(b)))
throw new IllegalArgumentException(
Double.isFinite(b) ? "base not finite: a=" + a : "exponent not finite: b=" + b);
if (b == 0)
return BigDecimal.ONE;
else if (b == 1)
return BigDecimal.valueOf(a);
if (a <= 0) {
if (a == 0) {
if (b >= 0)
return BigDecimal.ZERO;
else
throw new IllegalArgumentException("0**negative = infinite b=" + b);
} else
throw new IllegalArgumentException("negative base a=" + a);
}
double x = b * Math.log(a);
if (Math.abs(x) < MAX_DIGITS_E)
return BigDecimal.valueOf(Math.pow(a, b));
else
return expBig(x);
}
}
I had some help from google but apparently you don't need to apply log to your very big BigInteger numbers directly, since it can be broken down in the following way:
928 = 1000 * 0.928
lg 928 = lg 1000 + lg 0.928 = 3 + lg 0.928
Your problem is therefore reduced to the computation/approximation of logarithms that allow for arbitrary increasing precision, maybe math.stackexchange.com?
Convert it into a BigDecimal liek this:
new BigDecimal(val); // where val is a BigInteger
and call log from BigDecimalUtils on it :D
How accurate do you need it to be? If you only need 15 digits of accuracy you can do
BigInteger bi =
double log = Math.log(bi.doubleValue());
This would work for values up to 1023 bits. After that the value would not fit into a double anymore.
If you can use Google Guava, and only require base 2 or base 10 log, you can use methods from Guava's BigIntegerMath class.
If you need a different base, you can always use the logarithm change-of-base formula to convert from one of these, to the one you need.