How can I calculate the logarithm of a BigDecimal? Does anyone know of any algorithms I can use?
My googling so far has come up with the (useless) idea of just converting to a double and using Math.log.
I will provide the precision of the answer required.
edit: any base will do. If it's easier in base x, I'll do that.
Java Number Cruncher: The Java Programmer's Guide to Numerical Computing provides a solution using Newton's Method. Source code from the book is available here. The following has been taken from chapter 12.5 Big Decimal Functions (p330 & p331):
/**
* Compute the natural logarithm of x to a given scale, x > 0.
*/
public static BigDecimal ln(BigDecimal x, int scale)
{
// Check that x > 0.
if (x.signum() <= 0) {
throw new IllegalArgumentException("x <= 0");
}
// The number of digits to the left of the decimal point.
int magnitude = x.toString().length() - x.scale() - 1;
if (magnitude < 3) {
return lnNewton(x, scale);
}
// Compute magnitude*ln(x^(1/magnitude)).
else {
// x^(1/magnitude)
BigDecimal root = intRoot(x, magnitude, scale);
// ln(x^(1/magnitude))
BigDecimal lnRoot = lnNewton(root, scale);
// magnitude*ln(x^(1/magnitude))
return BigDecimal.valueOf(magnitude).multiply(lnRoot)
.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
}
}
/**
* Compute the natural logarithm of x to a given scale, x > 0.
* Use Newton's algorithm.
*/
private static BigDecimal lnNewton(BigDecimal x, int scale)
{
int sp1 = scale + 1;
BigDecimal n = x;
BigDecimal term;
// Convergence tolerance = 5*(10^-(scale+1))
BigDecimal tolerance = BigDecimal.valueOf(5)
.movePointLeft(sp1);
// Loop until the approximations converge
// (two successive approximations are within the tolerance).
do {
// e^x
BigDecimal eToX = exp(x, sp1);
// (e^x - n)/e^x
term = eToX.subtract(n)
.divide(eToX, sp1, BigDecimal.ROUND_DOWN);
// x - (e^x - n)/e^x
x = x.subtract(term);
Thread.yield();
} while (term.compareTo(tolerance) > 0);
return x.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
}
/**
* Compute the integral root of x to a given scale, x >= 0.
* Use Newton's algorithm.
* #param x the value of x
* #param index the integral root value
* #param scale the desired scale of the result
* #return the result value
*/
public static BigDecimal intRoot(BigDecimal x, long index,
int scale)
{
// Check that x >= 0.
if (x.signum() < 0) {
throw new IllegalArgumentException("x < 0");
}
int sp1 = scale + 1;
BigDecimal n = x;
BigDecimal i = BigDecimal.valueOf(index);
BigDecimal im1 = BigDecimal.valueOf(index-1);
BigDecimal tolerance = BigDecimal.valueOf(5)
.movePointLeft(sp1);
BigDecimal xPrev;
// The initial approximation is x/index.
x = x.divide(i, scale, BigDecimal.ROUND_HALF_EVEN);
// Loop until the approximations converge
// (two successive approximations are equal after rounding).
do {
// x^(index-1)
BigDecimal xToIm1 = intPower(x, index-1, sp1);
// x^index
BigDecimal xToI =
x.multiply(xToIm1)
.setScale(sp1, BigDecimal.ROUND_HALF_EVEN);
// n + (index-1)*(x^index)
BigDecimal numerator =
n.add(im1.multiply(xToI))
.setScale(sp1, BigDecimal.ROUND_HALF_EVEN);
// (index*(x^(index-1))
BigDecimal denominator =
i.multiply(xToIm1)
.setScale(sp1, BigDecimal.ROUND_HALF_EVEN);
// x = (n + (index-1)*(x^index)) / (index*(x^(index-1)))
xPrev = x;
x = numerator
.divide(denominator, sp1, BigDecimal.ROUND_DOWN);
Thread.yield();
} while (x.subtract(xPrev).abs().compareTo(tolerance) > 0);
return x;
}
/**
* Compute e^x to a given scale.
* Break x into its whole and fraction parts and
* compute (e^(1 + fraction/whole))^whole using Taylor's formula.
* #param x the value of x
* #param scale the desired scale of the result
* #return the result value
*/
public static BigDecimal exp(BigDecimal x, int scale)
{
// e^0 = 1
if (x.signum() == 0) {
return BigDecimal.valueOf(1);
}
// If x is negative, return 1/(e^-x).
else if (x.signum() == -1) {
return BigDecimal.valueOf(1)
.divide(exp(x.negate(), scale), scale,
BigDecimal.ROUND_HALF_EVEN);
}
// Compute the whole part of x.
BigDecimal xWhole = x.setScale(0, BigDecimal.ROUND_DOWN);
// If there isn't a whole part, compute and return e^x.
if (xWhole.signum() == 0) return expTaylor(x, scale);
// Compute the fraction part of x.
BigDecimal xFraction = x.subtract(xWhole);
// z = 1 + fraction/whole
BigDecimal z = BigDecimal.valueOf(1)
.add(xFraction.divide(
xWhole, scale,
BigDecimal.ROUND_HALF_EVEN));
// t = e^z
BigDecimal t = expTaylor(z, scale);
BigDecimal maxLong = BigDecimal.valueOf(Long.MAX_VALUE);
BigDecimal result = BigDecimal.valueOf(1);
// Compute and return t^whole using intPower().
// If whole > Long.MAX_VALUE, then first compute products
// of e^Long.MAX_VALUE.
while (xWhole.compareTo(maxLong) >= 0) {
result = result.multiply(
intPower(t, Long.MAX_VALUE, scale))
.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
xWhole = xWhole.subtract(maxLong);
Thread.yield();
}
return result.multiply(intPower(t, xWhole.longValue(), scale))
.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
}
A hacky little algorithm that works great for large numbers uses the relation log(AB) = log(A) + log(B). Here's how to do it in base 10 (which you can trivially convert to any other logarithm base):
Count the number of decimal digits in the answer. That's the integral part of your logarithm, plus one. Example: floor(log10(123456)) + 1 is 6, since 123456 has 6 digits.
You can stop here if all you need is the integer part of the logarithm: just subtract 1 from the result of step 1.
To get the fractional part of the logarithm, divide the number by 10^(number of digits), then compute the log of that using math.log10() (or whatever; use a simple series approximation if nothing else is available), and add it to the integer part. Example: to get the fractional part of log10(123456), compute math.log10(0.123456) = -0.908..., and add it to the result of step 1: 6 + -0.908 = 5.092, which is log10(123456). Note that you're basically just tacking on a decimal point to the front of the large number; there is probably a nice way to optimize this in your use case, and for really big numbers you don't even need to bother with grabbing all of the digits -- log10(0.123) is a great approximation to log10(0.123456789).
This one is super fast, because:
No toString()
No BigInteger math (Newton's/Continued fraction)
Not even instantiating a new BigInteger
Only uses a fixed number of very fast operations
One call takes about 20 microseconds (about 50k calls per second)
But:
Only works for BigInteger
Workaround for BigDecimal (not tested for speed):
Shift the decimal point until the value is > 2^53
Use toBigInteger() (uses one div internally)
This algorithm makes use of the fact that the log can be calculated as the sum of the exponent and the log of the mantissa. eg:
12345 has 5 digits, so the base 10 log is between 4 and 5.
log(12345) = 4 + log(1.2345) = 4.09149... (base 10 log)
This function calculates base 2 log because finding the number of occupied bits is trivial.
public double log(BigInteger val)
{
// Get the minimum number of bits necessary to hold this value.
int n = val.bitLength();
// Calculate the double-precision fraction of this number; as if the
// binary point was left of the most significant '1' bit.
// (Get the most significant 53 bits and divide by 2^53)
long mask = 1L << 52; // mantissa is 53 bits (including hidden bit)
long mantissa = 0;
int j = 0;
for (int i = 1; i < 54; i++)
{
j = n - i;
if (j < 0) break;
if (val.testBit(j)) mantissa |= mask;
mask >>>= 1;
}
// Round up if next bit is 1.
if (j > 0 && val.testBit(j - 1)) mantissa++;
double f = mantissa / (double)(1L << 52);
// Add the logarithm to the number of bits, and subtract 1 because the
// number of bits is always higher than necessary for a number
// (ie. log2(val)<n for every val).
return (n - 1 + Math.log(f) * 1.44269504088896340735992468100189213742664595415298D);
// Magic number converts from base e to base 2 before adding. For other
// bases, correct the result, NOT this number!
}
You could decompose it using
log(a * 10^b) = log(a) + b * log(10)
Basically b+1 is going to be the number of digits in the number, and a will be a value between 0 and 1 which you could compute the logarithm of by using regular double arithmetic.
Or there are mathematical tricks you can use - for instance, logarithms of numbers close to 1 can be computed by a series expansion
ln(x + 1) = x - x^2/2 + x^3/3 - x^4/4 + ...
Depending on what kind of number you're trying to take the logarithm of, there may be something like this you can use.
EDIT: To get the logarithm in base 10, you can divide the natural logarithm by ln(10), or similarly for any other base.
This is what I've come up with:
//http://everything2.com/index.pl?node_id=946812
public BigDecimal log10(BigDecimal b, int dp)
{
final int NUM_OF_DIGITS = dp+2; // need to add one to get the right number of dp
// and then add one again to get the next number
// so I can round it correctly.
MathContext mc = new MathContext(NUM_OF_DIGITS, RoundingMode.HALF_EVEN);
//special conditions:
// log(-x) -> exception
// log(1) == 0 exactly;
// log of a number lessthan one = -log(1/x)
if(b.signum() <= 0)
throw new ArithmeticException("log of a negative number! (or zero)");
else if(b.compareTo(BigDecimal.ONE) == 0)
return BigDecimal.ZERO;
else if(b.compareTo(BigDecimal.ONE) < 0)
return (log10((BigDecimal.ONE).divide(b,mc),dp)).negate();
StringBuffer sb = new StringBuffer();
//number of digits on the left of the decimal point
int leftDigits = b.precision() - b.scale();
//so, the first digits of the log10 are:
sb.append(leftDigits - 1).append(".");
//this is the algorithm outlined in the webpage
int n = 0;
while(n < NUM_OF_DIGITS)
{
b = (b.movePointLeft(leftDigits - 1)).pow(10, mc);
leftDigits = b.precision() - b.scale();
sb.append(leftDigits - 1);
n++;
}
BigDecimal ans = new BigDecimal(sb.toString());
//Round the number to the correct number of decimal places.
ans = ans.round(new MathContext(ans.precision() - ans.scale() + dp, RoundingMode.HALF_EVEN));
return ans;
}
If all you need is to find the powers of 10 in the number you can use:
public int calculatePowersOf10(BigDecimal value)
{
return value.round(new MathContext(1)).scale() * -1;
}
A Java implementation of Meower68 pseudcode which I tested with a few numbers:
public static BigDecimal log(int base_int, BigDecimal x) {
BigDecimal result = BigDecimal.ZERO;
BigDecimal input = new BigDecimal(x.toString());
int decimalPlaces = 100;
int scale = input.precision() + decimalPlaces;
int maxite = 10000;
int ite = 0;
BigDecimal maxError_BigDecimal = new BigDecimal(BigInteger.ONE,decimalPlaces + 1);
System.out.println("maxError_BigDecimal " + maxError_BigDecimal);
System.out.println("scale " + scale);
RoundingMode a_RoundingMode = RoundingMode.UP;
BigDecimal two_BigDecimal = new BigDecimal("2");
BigDecimal base_BigDecimal = new BigDecimal(base_int);
while (input.compareTo(base_BigDecimal) == 1) {
result = result.add(BigDecimal.ONE);
input = input.divide(base_BigDecimal, scale, a_RoundingMode);
}
BigDecimal fraction = new BigDecimal("0.5");
input = input.multiply(input);
BigDecimal resultplusfraction = result.add(fraction);
while (((resultplusfraction).compareTo(result) == 1)
&& (input.compareTo(BigDecimal.ONE) == 1)) {
if (input.compareTo(base_BigDecimal) == 1) {
input = input.divide(base_BigDecimal, scale, a_RoundingMode);
result = result.add(fraction);
}
input = input.multiply(input);
fraction = fraction.divide(two_BigDecimal, scale, a_RoundingMode);
resultplusfraction = result.add(fraction);
if (fraction.abs().compareTo(maxError_BigDecimal) == -1){
break;
}
if (maxite == ite){
break;
}
ite ++;
}
MathContext a_MathContext = new MathContext(((decimalPlaces - 1) + (result.precision() - result.scale())),RoundingMode.HALF_UP);
BigDecimal roundedResult = result.round(a_MathContext);
BigDecimal strippedRoundedResult = roundedResult.stripTrailingZeros();
//return result;
//return result.round(a_MathContext);
return strippedRoundedResult;
}
I was searching for this exact thing and eventually went with a continued fraction approach. The continued fraction can be found at here or here
Code:
import java.math.BigDecimal;
import java.math.MathContext;
public static long ITER = 1000;
public static MathContext context = new MathContext( 100 );
public static BigDecimal ln(BigDecimal x) {
if (x.equals(BigDecimal.ONE)) {
return BigDecimal.ZERO;
}
x = x.subtract(BigDecimal.ONE);
BigDecimal ret = new BigDecimal(ITER + 1);
for (long i = ITER; i >= 0; i--) {
BigDecimal N = new BigDecimal(i / 2 + 1).pow(2);
N = N.multiply(x, context);
ret = N.divide(ret, context);
N = new BigDecimal(i + 1);
ret = ret.add(N, context);
}
ret = x.divide(ret, context);
return ret;
}
Pseudocode algorithm for doing a logarithm.
Assuming we want log_n of x
double fraction, input;
int base;
double result;
result = 0;
base = n;
input = x;
while (input > base){
result++;
input /= base;
}
fraction = 1/2;
input *= input;
while (((result + fraction) > result) && (input > 1)){
if (input > base){
input /= base;
result += fraction;
}
input *= input;
fraction /= 2.0;
}
The big while loop may seem a bit confusing.
On each pass, you can either square your input or you can take the square root of your base; either way, you must divide your fraction by 2. I find squaring the input, and leaving the base alone, to be more accurate.
If the input goes to 1, we're through. The log of 1, for any base, is 0, which means we don't need to add any more.
if (result + fraction) is not greater than result, then we've hit the limits of precision for our numbering system. We can stop.
Obviously, if you're working with a system which has arbitrarily many digits of precision, you will want to put something else in there to limit the loop.
Old question, but I actually think this answer is preferable. It has good precision and supports arguments of practically any size.
private static final double LOG10 = Math.log(10.0);
/**
* Computes the natural logarithm of a BigDecimal
*
* #param val Argument: a positive BigDecimal
* #return Natural logarithm, as in Math.log()
*/
public static double logBigDecimal(BigDecimal val) {
return logBigInteger(val.unscaledValue()) + val.scale() * Math.log(10.0);
}
private static final double LOG2 = Math.log(2.0);
/**
* Computes the natural logarithm of a BigInteger. Works for really big
* integers (practically unlimited)
*
* #param val Argument, positive integer
* #return Natural logarithm, as in <tt>Math.log()</tt>
*/
public static double logBigInteger(BigInteger val) {
int blex = val.bitLength() - 1022; // any value in 60..1023 is ok
if (blex > 0)
val = val.shiftRight(blex);
double res = Math.log(val.doubleValue());
return blex > 0 ? res + blex * LOG2 : res;
}
The core logic (logBigInteger method) is copied from this other answer of mine.
I created a function for BigInteger but it can be easily modified for BigDecimal. Decomposing the log and using some properties of the log is what I do but I get only double precision. But it works for any base. :)
public double BigIntLog(BigInteger bi, double base) {
// Convert the BigInteger to BigDecimal
BigDecimal bd = new BigDecimal(bi);
// Calculate the exponent 10^exp
BigDecimal diviser = new BigDecimal(10);
diviser = diviser.pow(bi.toString().length()-1);
// Convert the BigDecimal from Integer to a decimal value
bd = bd.divide(diviser);
// Convert the BigDecimal to double
double bd_dbl = bd.doubleValue();
// return the log value
return (Math.log10(bd_dbl)+bi.toString().length()-1)/Math.log10(base);
}
Related
This question already has answers here:
How to round a number to n decimal places in Java
(39 answers)
Closed 6 years ago.
I have been trying to find a way to make sure the length of the double "value" is not larger than 10. However, this is becoming difficult to program since I am not rounding it to a certain number of decimal places.
For example, 1234567.8912 and 12.345678912 are both larger than 10 digits however, they will have to be rounded to a different number of decimal places. My logic was to find where the decimal point occurs and rounding the double to "10 - number of digits before the decimal place".
I created two different methods and both methods won't seem to work correctly.
if ((Double.toString(value)).length()> 10){
int counter = 0;
double no_of_dec = 1;
double no_of_int = 1;
double new_value = 0;
for (int i = 0; i< (Double.toString(value)).length(); i++){
if ( (Character.toString((Double.toString(value)).charAt(i))).equals(".") ){
counter = 1;
} if (counter == 1){
no_of_dec = no_of_dec * 10;
} else if (counter == 0){
no_of_int = no_of_int * 10;
}
}
no_of_dec = no_of_dec / (no_of_int);
new_value = (double)Math.round(value * 100d/100d);
return Double.toString(new_value);
} else {
return Double.toString(value);
}
if ((Double.toString(value)).length()> 10){
double no_of_dec = 0;
double no_of_int = 0;
double new_value = 0;
for (int i = 0; i< (Double.toString(value)).length(); i++){
while (!(Character.toString((Double.toString(value)).charAt(i))).equals(".")){
no_of_int = no_of_int + 1;
}
}
no_of_dec = (Double.toString(value)).length() - no_of_int;
no_of_dec = no_of_dec * 10;
new_value = (double)Math.round(value * no_of_dec/no_of_dec);
return Double.toString(new_value);
} else {
return Double.toString(value);
}
}
I did it this way:
private static BigDecimal getRounded(double n, int totalDigits) {
String nString = Double.toString(n); // transform to string to make the job easier
if(nString.contains(".")) {
int dotPos = nString.indexOf("."); // = number of digits before the decimal point
int remainingDigits = totalDigits - dotPos; // = remaining digits after the decimal point
return new BigDecimal(nString).setScale(remainingDigits, BigDecimal.ROUND_HALF_UP); // round
}
return new BigDecimal(n);
}
This was my test:
double n = 1234567.8912;
System.out.println(getRounded(n, 10));
n = 12.345678915;
System.out.println(getRounded(n, 10));
And this the result:
1234567.891
12.34567892
Demo: http://ideone.com/my7eB2
BigDecimal has an operation for doing this:
double truncate(double value) {
BigDecimal b = new BigDecimal(String.valueOf(value));
b = b.round(new MathContext(10));
return b.doubleValue();
}
There is a reason for using the BigDecimal(String) constructor and not the BigDecimal(double) constructor. doubles and floats are not base 10 numbers, so they don’t exactly represent the values we see when we print them; the constructor documentation has a good illustration of this. This is not unique to Java and is true in most languages.
Update:
4castle points out that the above won’t work for nonzero numbers with a magnitude less than 1, since BigDecimal doesn’t consider the zero integer part a significant digit. You can work around that:
double truncate(double value) {
int precision = 10;
if (value != 0 && Math.abs(value) < 1) {
precision--;
}
BigDecimal b = new BigDecimal(String.valueOf(value));
b = b.round(new MathContext(precision));
return b.doubleValue();
}
Update 2:
BackSlash points out that it’s more complex than the above for nonzero numbers between -1 and 1. I’m sure it’s possible to calculate the exact precision needed, but I think it’s simpler to just eliminate the issue by adding 1 for rounding purposes:
double truncate(double value) {
BigDecimal b = new BigDecimal(String.valueOf(value));
MathContext context = new MathContext(10);
if (value > 0 && value < 1) {
b = b.add(BigDecimal.ONE);
b = b.round(context);
b = b.subtract(BigDecimal.ONE);
} else if (value < 0 && value > -1) {
b = b.subtract(BigDecimal.ONE);
b = b.round(context);
b = b.add(BigDecimal.ONE);
} else {
b = b.round(context);
}
return b.doubleValue();
}
Working on this problem, and also did a few reference to similar solutions. One thing I am confuse is, why we break the loop as long as there is one repetitive number? Is it possible the number repeat for 2-3 times and then changed to another different number? Thanks.
I mean this part specifically,
if (map.containsKey(num)) {
int index = map.get(num);
res.insert(index, "(");
res.append(")");
break;
}
The problem,
Given two integers representing the numerator and denominator of a fraction, return the fraction in string format.
If the fractional part is repeating, enclose the repeating part in parentheses.
For example,
Given numerator = 1, denominator = 2, return "0.5".
Given numerator = 2, denominator = 1, return "2".
Given numerator = 2, denominator = 3, return "0.(6)".
public class Solution {
public String fractionToDecimal(int numerator, int denominator) {
if (numerator == 0) {
return "0";
}
StringBuilder res = new StringBuilder();
// "+" or "-"
res.append(((numerator > 0) ^ (denominator > 0)) ? "-" : "");
long num = Math.abs((long)numerator);
long den = Math.abs((long)denominator);
// integral part
res.append(num / den);
num %= den;
if (num == 0) {
return res.toString();
}
// fractional part
res.append(".");
HashMap<Long, Integer> map = new HashMap<Long, Integer>();
map.put(num, res.length());
while (num != 0) {
num *= 10;
res.append(num / den);
num %= den;
if (map.containsKey(num)) {
int index = map.get(num);
res.insert(index, "(");
res.append(")");
break;
}
else {
map.put(num, res.length());
}
}
return res.toString();
}
}
thanks in advance,
Lin
The code doesn't stop when it sees a digit repeated. It stops when it notes that it has reached a state which it was already in. If it reaches the same state again, it means that we are about to repeat a division that we have already done, which means that the dividend and remainder are going to be the same, and we are going to do the same series of steps we have already done.
When that happens, it means a repetition, and it stops and adds the parentheses.
For example, let's divide 123 by 999. This should give us the repeating decimal 0.123123123..., so the output should be 0.(123).
123 / 999 is 0. The remainder is 123. We start with 0.
Multiply the remainder by 10. Now we have 1230 / 999. Dividend is 1, remainder is 231. Now we have 0.1
Multiply the remainder by 10. Now we have 2310 / 999. Dividend is 2, remainder is 312. Now we have 0.12
Multiply the remainder by 10. Now we have 3120 / 999. Dividend is 3, remainder is 123. Now we have 0.123
Multiply the remainder by 10. Now we have 1230 / 999... wait, we have already done that! That means that as we continue to divide, we'll get to that number again and again. Stop and put parentheses around the repeating part.
The map is there to tell us which numbers we have already divided, and at which index in the StringBuilder. When we find a number we have already divided, we use that index to know where to insert the parenthesis.
Clearly it's possible to have a decimal number with two or more decimals recurring and then a different decimal. For example 449/1000 = 0.449
Here is my solution in Java to this problem:
/**
* Given two integers a and b, return the result as a String.
* Display the repeating part of the fraction in parenthesis.
*
* Runs in O(b)
*
* #author Raed Shomali
*/
public class Divider {
private static final String DOT = ".";
private static final String ERROR = "ERROR";
private static final String LEFT_PARENTHESIS = "(";
private static final String RIGHT_PARENTHESIS = ")";
public static String divide(final int a, final int b){
if (b == 0) {
return ERROR;
}
int value = a / b;
int remainder = a % b;
return String.valueOf(value) + DOT + divider(remainder, b);
}
private static String divider(final int a, final int b) {
final Map<Integer, Integer> remainderIndexMap = new HashMap<>();
final List<Integer> values = new ArrayList<>();
int value;
int remainder = a;
while (!remainderIndexMap.containsKey(remainder)) {
remainderIndexMap.put(remainder, values.size());
remainder *= 10;
value = remainder / b;
remainder = remainder % b;
values.add(value);
}
final int index = remainderIndexMap.get(remainder);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < index; i++) {
result.append(values.get(i));
}
result.append(LEFT_PARENTHESIS);
for (int i = index; i < values.size(); i++) {
result.append(values.get(i));
}
result.append(RIGHT_PARENTHESIS);
return result.toString();
}
}
Basically, the idea is simple. Using the same long division technique, you know when to stop when you have already seen the same remainder value.
Here are some test cases to show a few different scenarios:
divide(0, 0) // "ERROR"
divide(1, 2) // "0.5(0)"
divide(0, 3) // "0.(0)"
divide(10, 3) // "3.(3)"
divide(22, 7) // "3.(142857)"
divide(100, 145) // "0.(6896551724137931034482758620)"
If you are interested in a solution written in Go, you can find it here https://github.com/shomali11/util
This problem is actually very simple to solve if we use the long division technique that we learnt in 4th grade.
Suppose you need to divide 92 by 22. How do you do it using the long division method. How do you detect a repeating pattern?
Simple, you know you have a repeating pattern of decimals when you encounter a previously encountered reminder. You'll need to store the reminders and the corresponding index of the result in a dictionary and using the same you could detect/print repeating decimals. Working python code below.
def divide(numerator, denominator):
sign, res, lead = '', '', ''
if (numerator < 0) ^ (denominator < 0) and numerator != 0:
sign = '-'
numerator = abs(numerator)
denominator = abs(denominator)
remainders = defaultdict(list)
if numerator < denominator:
lead = '0'
_x = str(numerator)
r = 0
i = 0
j = 0
while True:
if i < len(_x):
d = int(str(r)+_x[i])
q = d // denominator
if not (q == 0 and len(res) == 0):
res += str(q)
r = d - (q * denominator)
i += 1
elif i >= len(_x) and j <= 9223372036854775807:
if r == 0:
return sign+lead+res
if j == 0:
remainders[r] = [True, len(res)+1]
res += '.'
d = int(str(r) + '0')
q = d // denominator
res += str(q)
r = d - (q * denominator)
if remainders[r] and remainders[r][0]:
res = res[0:remainders[r][1]] + '(' + res[remainders[r][1]:] + ')'
return sign+lead+res
remainders[r] = [True, len(res)]
j += 1
else:
return sign+lead+res
I am trying to represent the number .0002 as 2.0 x 10 ^ -4.
Here is what I have so far
public static String toScientificNotation(double n) {
int exponent = 0;
if( n < 1){
String doubleValue = Double.toString(Math.abs(n));
int format = doubleValue.indexOf(".");
int decimalPlacesToMove = (doubleValue.length() - (format - 1));
}
No matter what I try i get E in the output. If someone can give me a pseudo code. It would be a great help. I cannot use BigDecimal or anything other than double.
I reworked your method into the following; you can use it as a basis/skeleton to convert the double into the scientific notation you want, avoiding the E altogether. You can expand on it by creating implementations for n > 1 and n < 0
private static String toScienticNotation(double n) {
String result = "";
if (n < 1 && n > 0) {
int counter = 0;
double answer = n;
while (answer < 1) {
answer = answer * 10;
counter--;
}
result = String.valueOf(answer) + " x 10 ^ "
+ String.valueOf(counter);
}
return result;
}
It works by multiplying the input n by 10, counter number of times, until n is greater than 1. This is a substitute formula to manually discover the number of decimal points rather than using the String methods.
The method you were using would work fine, but there's an easier way using formatter:
import java.util.*;
import java.text.*;
import java.math.*;
class Main{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
NumberFormat formatter = new DecimalFormat();
double d = input.nextDouble();
formatter = new DecimalFormat("#.######E0");
String x = formatter.format(d);
System.out.println(x.replace("E","*10^");
}
}
This will print the scientific notation in the decimal format of #.######E0
For example:
If 200 was inputted, the system would return 2 * 10^2.
Here is a method which (hopefully) converts all kinds of doubles to their [-]Factor * 10 ^ [-]Exponent notation. It's explained inside the code.
edit: There is a very elegant solution by UnknownOctopus. Still I will leave this here as it does not use any formatters or such, just doubles and Strings - I understood the question wrongly and assumed that only such primitives were allowed.
public class Main{
/**
* Converts a double to a base10 notation String.
*
* Each String is formatted like this:
*
* [-]Factor * 10 ^ [-]Exponent
*
* where both, Factor and Exponent, are integer values.
*
* #param number the number to convert
* #return a base10 notation String.
*/
public static String toScientificNotation(double number) {
String s = String.valueOf(number);
int indexPZero = s.indexOf(".0"); // mostly to check if .0 is the end
int exponent = 0; // simplest case: *10^0
// Check if the String ends with exactly .0
if (indexPZero == s.length() - 2) {
// If the string also has 0s in front of the period, shift those to the
// right
while(s.contains("0.")) {
number /= 10;
exponent += 1;
s = String.valueOf(number);
}
// if the string ends in .0 and has no zeros in front of the period we
// can format it:
return String.valueOf(number) + " * 10 ^ " + exponent;
}
// If the String does not end in .0, we need to shift to the left.
// Additionall
while (indexPZero != s.length() -2) {
// in case we suddenly reach the scientific notation just substitute it
s = s.toLowerCase();
if (s.contains("e")) {
return s.substring(0,
s.indexOf("e")) + " * 10 ^ " + s.substring(s.indexOf("e")+1);
}
// otherwise shift left and reduce the exponent
number *= 10;
exponent -= 1;
s = String.valueOf(number);
indexPZero = s.indexOf(".0");
}
// If we end up here, just write out the number and the exponent.
return String.valueOf(number) + " * 10 ^ " + exponent;
}
public static void main(String... args) {
double[] vals = { 1, 0.2, 23.4, -32.00004, 0.0002, 10.0 };
for(double val : vals) {
System.out.println(val + " becomes " + toScientificNotation(val));
}
}
}
Output:
1.0 becomes 1.0 * 10 ^ 0
0.2 becomes 2.0 * 10 ^ -1
23.4 becomes 234.0 * 10 ^ -1
-32.00004 becomes -3200004.0 * 10 ^ -5
2.0E-4 becomes 2.0 * 10 ^ -4
10.0 becomes 1.0 * 10 ^ 1
Numbers are being stored in a database (out of my control) as floats/doubles etc.
When I pull them out they are damaged - for example 0.1 will come out (when formatted) as 0.100000001490116119384765625.
Is there a reliable way to recover these numbers?
I have tried new BigDecimal(((Number) o).doubleValue()) and BigDecimal.valueOf(((Number) o).doubleValue()) but these do not work. I still get the damaged result.
I am aware that I could make assumptions on the number of decimal places and round them but this will break for numbers that are deliberately 0.33333333333 for example.
Is there a simple method that will work for most rationals?
I suppose I am asking is there a simple way of finding the most minimal rational number that is within a small delta of a float number?.
you can store the numbers in the database as String and on the retrieval just parseDouble() them. This way the number wont be damaged, it will be same as you store there.
is there a simple way of finding a rational number that is within 0.00001 of a float number?.
This is called rounding.
double d = ((Number) o).doubleValue();
double d2 = Math.round(d * 1e5) / 1e5;
BigDecimal bd = BigDecimal.valueOf(d2);
or you can use BigDecimal to perform the rounding (I avoid using BigDecimal as it is needelessly slow once you know how to use rounding of doubles)
double d = ((Number) o).doubleValue();
BigDecimal bd = BigDecimal.valueOf(d).setScale(5, RoundingMode.HALF_UP);
Note: never use new BigDecimal(double) unless you understand what it does. Most likely BigDecial.valueOf(double) is what you wanted.
Here's the bludgeon way I have done it - I would welcome a more elegant solution.
I chose an implementation of Rational that had a mediant method ready-made for me.
I refactored it to use long instead of int and then added:
// Default delta to apply.
public static final double DELTA = 0.000001;
public static Rational valueOf(double dbl) {
return valueOf(dbl, DELTA);
}
// Create a good rational for the value within the delta supplied.
public static Rational valueOf(double dbl, double delta) {
// Primary checks.
if ( delta <= 0.0 ) {
throw new IllegalArgumentException("Delta must be > 0.0");
}
// Remove the integral part.
long integral = (long) Math.floor(dbl);
dbl -= integral;
// The value we are looking for.
final Rational d = new Rational((long) ((dbl) / delta), (long) (1 / delta));
// Min value = d - delta.
final Rational min = new Rational((long) ((dbl - delta) / delta), (long) (1 / delta));
// Max value = d + delta.
final Rational max = new Rational((long) ((dbl + delta) / delta), (long) (1 / delta));
// Start the fairey sequence.
Rational l = ZERO;
Rational h = ONE;
Rational found = null;
// Keep slicing until we arrive within the delta range.
do {
// Either between min and max -> found it.
if (found == null && min.compareTo(l) <= 0 && max.compareTo(l) >= 0) {
found = l;
}
if (found == null && min.compareTo(h) <= 0 && max.compareTo(h) >= 0) {
found = h;
}
if (found == null) {
// Make the mediant.
Rational m = mediant(l, h);
// Replace either l or h with mediant.
if (m.compareTo(d) < 0) {
l = m;
} else {
h = m;
}
}
} while (found == null);
// Bring back the sign and the integral.
if (integral != 0) {
found = found.plus(new Rational(integral, 1));
}
// That's me.
return found;
}
public BigDecimal toBigDecimal() {
// Do it to just 4 decimal places.
return toBigDecimal(4);
}
public BigDecimal toBigDecimal(int digits) {
// Do it to n decimal places.
return new BigDecimal(num).divide(new BigDecimal(den), digits, RoundingMode.DOWN).stripTrailingZeros();
}
Essentially - the algorithm starts with a range of 0-1. At each iteration I check to see if either end of the range falls between my d-delta - d+delta range. If it does we've found an answer.
If no answer is found we take the mediant of the two limits and replace one of the limits with it. The limit to replace is chosen to ensure the limits surround d at all times.
This is essentially doing a binary-chop search between 0 and 1 to find the first rational that falls within the desired range.
Mathematically I climb down the Stern-Brocot Tree choosing the branch that keeps me enclosing the desired number until I fall into the desired delta.
NB: I have not finished my testing but it certainly finds 1/10 for my input of 0.100000001490116119384765625 and 1/3 for 1.0/3.0 and the classic 355/113 for π.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
Is there a library that will convert a Double to a String with the whole number, followed by a fraction?
For example
1.125 = 1 1/8
I am only looking for fractions to a 64th of an inch.
Your problem is pretty simple, because you're assured the denominator will always divide 64. in C# (someone feel free to translate a Java version):
string ToMixedFraction(decimal x)
{
int whole = (int) x;
int denominator = 64;
int numerator = (int)( (x - whole) * denominator );
if (numerator == 0)
{
return whole.ToString();
}
while ( numerator % 2 == 0 ) // simplify fraction
{
numerator /= 2;
denominator /=2;
}
return string.Format("{0} {1}/{2}", whole, numerator, denominator);
}
Bonus: Code Golf
public static string ToMixedFraction(decimal x) {
int w = (int)x,
n = (int)(x * 64) % 64,
a = n & -n;
return w + (n == 0 ? "" : " " + n / a + "/" + 64 / a);
}
One problem you might run into is that not all fractional values can be represented by doubles. Even some values that look simple, like 0.1. Now on with the pseudocode algorithm. You would probably be best off determining the number of 64ths of an inch, but dividing the decimal portion by 0.015625. After that, you can reduce your fraction to the lowest common denominator. However, since you state inches, you may not want to use the smallest common denominator, but rather only values for which inches are usually represented, 2,4,8,16,32,64.
One thing to point out however, is that since you are using inches, if the values are all proper fractions of an inch, with a denominator of 2,4,8,16,32,64 then the value should never contain floating point errors, because the denominator is always a power of 2. However if your dataset had a value of .1 inch in there, then you would start to run into problems.
How about org.apache.commons.math ? They have a Fraction class that takes a double.
http://commons.apache.org/math/api-1.2/org/apache/commons/math/fraction/Fraction.html
You should be able to extend it and give it functionality for the 64th. And you can also add a toString that will easily print out the whole number part of the fraction for you.
Fraction(double value, int
maxDenominator) Create a fraction
given the double value and maximum
denominator.
I don't necessarily agree, base on the fact that Milhous wants to cover inches up to 1/64"
Suppose that the program demands 1/64" precision at all times, that should take up 6 bits of the mantissa. In a float, there's 24-6 = 18, which (if my math is right), should mean that he's got a range of +/- 262144 + 63/64"
That might be enough precision in the float to convert properly into the faction without loss.
And since most people working on inches uses denominator of powers of 2, it should be fine.
But back to the original question, I don't know any libraries that would do that.
Function for this in a C-variant called LPC follows. Some notes:
Addition to input value at beginning is to try to cope with precision issues that otherwise love to wind up telling you that 5 is 4 999999/1000000.
The to_int() function truncates to integer.
Language has a to_string() that will turn some floats into exponential notation.
string strfrac(float frac) {
int main = to_int(frac + frac / 1000000.0);
string out = to_string(main);
float rem = frac - to_float(main);
string rep;
if(rem > 0 && (to_int(rep = to_string(rem)) || member(rep, 'e') == Null)) {
int array primes = ({ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 });
string base;
int exp;
int num;
int div;
if(sscanf(rep, "%se%d", base, exp) == 2) {
num = to_int(replace(base, ".", ""));
div = to_int(pow(10, abs(exp)));
} else {
rep = rep[2..];
num = to_int(rep);
div = to_int(pow(10, strlen(rep)));
}
foreach(int prime : primes) {
if(prime > num)
break;
while((num / prime) * prime == num && (div / prime) * prime == div) {
num /= prime;
div /= prime;
}
}
out += " " + num + "/" + div;
}
return out;
}
i wrote this for my project i hope it could be usefull:
//How to "Convert" double to fraction("a/b") - kevinlopez#unitec.edu
private boolean isInt(double number){
if(number%2==0 ||(number+1)%2==0){
return true;
}
return false;
}
private String doubleToFraction(double doub){
//we get the whole part
int whole = (int)doub;
//we get the rest
double rest = doub - (double)whole;
int numerator=1,denominator=1;
//if the whole part of the number is greater than 0
//we'll try to transform the rest of the number to an Integer
//by multiplying the number until it become an integer
if(whole >=1){
for(int i = 2; ; i++){
/*when we find the "Integer" number(it'll be the numerator)
* we also found the denominator(i,which is the number that transforms the number to integer)
* For example if we have the number = 2.5 when it is multiplied by 2
* now it's 5 and it's integer, now we have the numerator(the number (2.5)*i(2) = 5)
* and the denominator i = 2
*/
if(isInt(rest*(double)i)){
numerator = (int)(rest*(double)i);
denominator = i;
break;
}
if(i>10000){
//if i is greater than 10000 it's posible that the number is irrational
//and it can't be represented as a fractional number
return doub+"";
}
}
//if we have the number 3.5 the whole part is 3 then we have the rest represented in fraction 0.5 = 1/2
//so we have a mixed fraction 3+1/2 = 7/2
numerator = (whole*denominator)+numerator;
}else{
//If not we'll try to transform the original number to an integer
//with the same process
for(int i = 2; ; i++){
if(isInt(doub*(double)i)){
numerator = (int)(doub*(double)i);
denominator = i;
break;
}
if(i>10000){
return doub+"";
}
}
}
return numerator+"/"+denominator;
}
My code looks like this.
public static int gcd(int a, int b)
{
if (b == 0)
return a;
else
return gcd(b, a % b);
}
public static String doubleToStringFraction(Double d)
{
StringBuffer result = new StringBuffer(" " + ((int) Math.floor(d)));
int whole = (int) ((d - Math.floor(d)) * 10000);
int gcd = gcd(whole, 10000);
result.append(" " + (whole / gcd) + "/" + 10000 / gcd + " ");
return result.toString();
}
As several others have poited out, fractions of 64 can be precicely represented by IEEE-floats. This means we can also convert to a fraction by moving and masking bits.
This is not the place to explain all details of floating point representations, please refer to wikipedia for details.
Briefly: a floating point number is stored as (sign)(exp)(frac) where sign is 1 bit, exp is 11 bits and frac is the fraction part (after 1.) and is 52 bits. This is enterpreted as the number:
(sign == 1 ? -1 : 1) * 1.(frac) * 2^(exp-1023)
Thus, we can get the 64th by moving the point accoring to the exponent and masking out the 6 bits after the point. In Java:
private static final long MANTISSA_FRAC_BITMAP = 0xfffffffffffffl;
private static final long MANTISSA_IMPLICIT_PREFIX = 0x10000000000000l;
private static final long DENOM_BITMAP = 0x3f; // 1/64
private static final long DENOM_LEN = 6;
private static final int FRAC_LEN = 52;
public String floatAsFrac64(double d) {
long bitmap = Double.doubleToLongBits(d);
long mantissa = bitmap & MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX;
long exponent = ((bitmap >> FRAC_LEN) & 0x7ff) - 1023;
boolean negative = (bitmap & (1l << 63)) > 0;
// algorithm:
// d is stored as SE(11)F(52), implicit "1." before F
// move point to the right <exponent> bits to the right:
if(exponent > FRAC_LEN) System.out.println("warning: loosing precision, too high exponent");
int pointPlace = FRAC_LEN-(int)exponent;
// get the whole part as the number left of the point:
long whole = mantissa >> pointPlace;
// get the frac part as the 6 first bits right of the point:
long frac = (mantissa >> (pointPlace-DENOM_LEN)) & DENOM_BITMAP;
// if the last operation shifted 1s out to the right, we lost precision, check with
// if any of these bits are set:
if((mantissa & ((MANTISSA_FRAC_BITMAP | MANTISSA_IMPLICIT_PREFIX) >> (pointPlace - DENOM_LEN))) > 0) {
System.out.println("warning: precision of input is smaller than 1/64");
}
if(frac == 0) return String.format("%d", whole);
int denom = 64;
// test last bit, divide nom and demon by 1 if not 1
while((frac & 1) == 0) {
frac = frac >> 1;
denom = denom >> 1;
}
return String.format("%d %d/%d", whole, frac, denom);
}
(this code can probably be made shorter, but reading bit-flipping-code like this is hard enough as it is...)
I create simply Fraction library.
The library is available here: https://github.com/adamjak/Fractions
Example:
String s = "1.125";
Fraction f1 = Fraction.tryParse(s);
f1.toString(); // return 9/8
Double d = 2.58;
Fraction f2 = Fraction.createFraction(d);
f2.divide(f1).toString() // return 172/75 (2.29)
To solve this problem (in one of my projects), I took the following steps:
Built a dictionary of decimal/fraction strings.
Wrote a function to search the dictionary for the closest matching fraction depending on the "decimal" part of the number and the matching criteria.