using BigDecimal for very large numbers - java

I have this code:
while (counter <= t)
{
a = a * counter;
c = c * r + a;
counter++;
}
and I have t with the value 101835.
The output should be 7.1*10^438, but NetBeans shows
the output as infinity.
Is there anything that will make the output as a decimal number?

Yes, the only thing which you should use here is BigDecimal class. It'll easily handle that complex value without burdening you.
The maximum supported double value is only around 1.7 * 10^ 308 as given by Double.MAX_VALUE specified for Java.

A double as defined by IEEE-754 can't represent such a number, it's too large. The bounds are approximately between -10308 and 10308.
You need to use a BigDecimal to represent it: a number with an arbitrary number of bytes to represent numbers.
Better way to implement this:
double c = 0.0005d;//initialize c
double r = 0.01d; //initialize r
double a = 0.0006d;//initialize a
BigDecimal abd = new BigDecimal(a); //BigDecimal for a
BigDecimal cbd = new BigDecimal(c); //BigDecimal for c
BigDecimal rbd = new BigDecimal(r); //BigDecimal for r
for (int counter = 1; counter <= t; counter++) {//perhaps other offset for counter?
abd = abd.multiply(new BigDecimal(counter));
cbd = cbd.multiply(rbd).add(abd);
}
A potential problem with this approach is that the precision is too high: Java will calculate all operations exactly resulting in numbers that have thousands of digits. Since every operation blows up the number of digits, within a few iterations, simple addition and multiplication operations become unfeasible.
You can solve this by defining a precision using the optional MathContext parameter: it determines on how precise the result should be. You can for instance use MathContext.DECIMAL128:
int t = 101835;
double c = 0.0005d;//initialize c
double r = 0.01d; //initialize r
double a = 0.0006d;//initialize a
BigDecimal abd = new BigDecimal(a); //BigDecimal for a
BigDecimal cbd = new BigDecimal(c); //BigDecimal for c
BigDecimal rbd = new BigDecimal(r); //BigDecimal for r
for (int counter = 1; counter <= t; counter++) {//perhaps other offset for counter?
abd = abd.multiply(new BigDecimal(counter),MathContext.DECIMAL128);
cbd = cbd.multiply(rbd,MathContext.DECIMAL128).add(abd,MathContext.DECIMAL128);
}
System.out.println(abd);
System.out.println(cbd);
This gives:
abd = 3.166049846031012773846494375835059E+465752
cbd = 3.166050156931013454758413539958330E+465752
This is approximately correct, after all the result of a should be:
Which is approximately correct according to Wolfram Alpha.
Furthermore I would advice to use a for and not a while if it is a for loop. Since while tends to create another type of infinity: an infinite loop ;).

Related

Java Fraction Base Conversion [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I want to convert a source base number to a destination base number, but I have a problem with fractions. When I trying to convert 10.234 (base10) to base 7 = 13.14315 it works perfectly, or aaaaa.0 (base16) to base 24 = 22df2 it also works.
But when I try to convert aaaaa.cdefb0 (base16) to base 24 = 22df2.j78da it doesn't work. I can't calculate fraction part and I get 22df2 as the answer
my code for base conversion :
private static String baseConversion(String number,
int sBase, int dBase) {
return Long.toString(
Long.parseLong(number, sBase),
dBase);
}
my code for fraction conversion :
private static String fractionConversion(double fraction, int dBase) {
StringBuilder output = new StringBuilder(".");
for (int i = 0; i < PRECISION; i++) {
fraction *= dBase;
output.append(Long.parseLong(Integer.toString((int) fraction)));
fraction -= Long.parseLong(Integer.toString((int) fraction));
}
return output.toString();
}
In your code below you should be appending a symbol for the base, not a number. Use the number to index into an array of symbols for the base.
And I recommend using integers for your remainder and product computations as you will eventually lose precision using floating point values.
for (int i = 0; i < PRECISION; i++) {
fraction *= dBase;
// what are you appending here? It should be a symbol
output.append(Long.parseLong(Integer.toString((int) fraction)));
fraction -= Long.parseLong(Integer.toString((int) fraction));
}
Here's a more complete example.
static String symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// assumes a string in the form of .293093
// number of places
// destination radix.
public static String expand(String decimalFraction, int places, int dr) {
decimalFraction = decimalFraction.substring(1); // ignore decimal point
int numerator = Integer.parseInt(decimalFraction);
int denominator = (int) Math.pow(10, decimalFraction.length());
StringBuilder sb = new StringBuilder(".");
for (int i = 0; i < places; i++) {
numerator *= dr;
sb.append(symbols.charAt((int) (numerator / denominator)));
numerator %= denominator;
if (numerator == 0) {
break;
}
}
return sb.toString();
}
I earnestly cannot recommend doing this, but I'll answer the question anyhow.
I would simply split the two numbers and treat them separately. First and foremost, how fractions work outside of Base 10 is not a 1-for-1 conversion of the trailing number. .25 in base 10 is not .11001 in binary, it's .01.
Every decimal place in your number represents a new magnitude; in base 10 it's values of 10^-1, 10^-2, and so on. When you change bases, you still change magnitudes but at different rates: 2^-1, etc.
.25 is thus analogous to 2/10 + 5/100 in base 10, and 0/2 + 1/4 in base 2. This leads to a new problem, where if the divisor of a fraction isn't a power of your new base, you usually get an irrational number. 1/20 is .05 in decimal, but in base 2 it's:
0.00 0011 0011 0011 //endless
This more or less leads to why fractional numbers are not normally converted between bases. You will lose precision and it's not a small task. But essentially the algorithmic conversation is the same as for whole numbers, but instead of dividing the number by the base and using the remainder as your output, you multiply by the base and use the division as your output.
int decimalPart = ...; //for example, "375", represents .375 or 3/8
int magnitude = Math.pow(10, ("" + decimalPart).length());
int newBase = 2; //again, bases that don't divide into each other will be messy, like 7 and 10
StringBuilder out = new StringBuilder();
//The below should be limited for precision, or you may loop forever
while (decimalPart > 0) {
decimalPart *= newBase;
out.append(decimalPart / magnitude);
decimalPart %= magnitude.
}
String result = sb.toString();
//"375" -> "011"

Recurring fraction to decimal on BigDecimal

There are many ways to convert a rational number into a decimal with a recurring part (in other words, 10/3=3.(3), where (3) indicates that it repeats forever). But these only work if the numerator and denominator are integers. What can we do if the numerator or denominator is a double? For example, how can we find that
1/0.3 = 3.(3)
UPDATE:
This works but only for int numbers.
http://www.programcreek.com/2014/03/leetcode-fraction-to-recurring-decimal-java/
Let split the problem in two pieces:
Convert 1/0.3 to N/M form
Convert N/M to a.b(c) form
Lets convert 0.3 to M/N form (which give us 3/10).
String input = "123.456";
String[] parts = input.split("\\.");
String whole = parts[0];
String fraction = parts[1];
int wholeInt = Integer.parseInt(whole);
int fractionInt = Integer.parseInt(fraction);
int multiplier = pow10(fraction.length());
int n = wholeInt * multiplier + fractionInt;
int m = multiplier;
System.out.println(n + "/" + m);
I used function pow10 which simply returns 10 power input.
Now we need divide 1 by 10/3 it is easy N1/M1 divided by N2/M2 it is simply (N1*M2)/(N2*M1).
We get our result in form N/M now (we also need to normalize it by dividing both part by GCD(N, M)
Now we ready to solve main problem.
First of all get whole part
int whole = n/m;
then find fraction and repeating part
int current = n%m;
StringBuilder sb = new StringBuilder();
List<Integer> controlSet = new ArrayList<>();
while((!controlSet.contains(current))){
int currentDigit = current *10 / m;
sb.append(currentDigit);
controlSet.add(current);
current = current *10 - m * currentDigit;
}
String fraction = sb.toString().substring(0, controlSet.indexOf(current));
String repeat = sb.toString().substring(controlSet.indexOf(current));
Here we just divide in loop getting result number by number.
Main trick then number starts to repeat when we meet current that we already use.
Now you need to take all parts together. Implement GCD (lots of implementation over internet).

Extra precision required for the implementation of trig functions with BigDecimal

Introduction
I am interested in writing math functions for BigDecimal (actually, also for
my own BigDecimal type written in Delphi,
but that is irrelevant here -- in this question, I use Java's BigDecimal because more people know it and
my BigDecimal is very similar. The test code below is in Java and works fine and works equally well in the Delphi
translation).
I know that BigDecimal is not fast, but it is pretty accurate. I do not want to use some existing Java BigDecimal math library, especially not
because this is for my own BigDecimal type (in Delphi) as well.
As a nice example of how to implement trig functions, I found the following simple example (but I forgot where, sorry). It obviously uses
MacLaurin series to calculate the cosine of a BigDecimal, with a given precision.
Question
This precision is exactly my problem. The code below uses an extra precision of 5 to calculate the result and only in the end, it rounds that down to the desired precision.
I have a feeling that an extra precision of 5 is fine for, say, a target precision up to 50 or even a little more, but not for BigDecimals with a much higher precision (say, 1000 digits or more). Unfortunately, I couldn't find a way to verify this (e.g. with an online extremely accurate calculator).
Finally, my question: am I right -- that 5 is probably not enough for larger numbers -- and if I am, how can I calculate or estimate the extra precision required?
Example code calculates cos(BigDecimal):
public class BigDecimalTrigTest
{
private List _trigFactors;
private int _precision;
private final int _extraPrecision = 5; // Question: is 5 enough?
public BigDecimalTrigTest(int precision)
{
_precision = precision;
_trigFactors = new Vector();
BigDecimal one = new BigDecimal("1.0");
BigDecimal stopWhen = one.movePointLeft(precision + _extraPrecision);
System.out.format("stopWhen = %s\n", stopWhen.toString());
BigDecimal factorial = new BigDecimal(2.0);
BigDecimal inc = new BigDecimal(2.0);
BigDecimal factor = null;
do
{
factor = one.divide(factorial, precision + _extraPrecision,
BigDecimal.ROUND_HALF_UP); // factor = 1/factorial
_trigFactors.add(factor);
inc = inc.add(one); // factorial = factorial * (factorial + 1)
factorial = factorial.multiply(inc);
inc = inc.add(one); // factorial = factorial * (factorial + 1)
factorial = factorial.multiply(inc);
} while (factor.compareTo(stopWhen) > 0);
}
// sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ... = Sum[0..+inf] (-1^n) * (x^(2*n + 1)) / (2*n + 1)!
// cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + x^8/8! - ... = Sum[0..+inf] (-1^n) * (x^(2*n)) / (2*n)!
public BigDecimal cos(BigDecimal x)
{
BigDecimal res = new BigDecimal("1.0");
BigDecimal xn = x.multiply(x);
for (int i = 0; i < _trigFactors.size(); i++)
{
BigDecimal factor = (BigDecimal) _trigFactors.get(i);
factor = factor.multiply(xn);
if (i % 2 == 0)
{
factor = factor.negate();
}
res = res.add(factor);
xn = xn.multiply(x);
xn = xn.multiply(x);
xn = xn.setScale(_precision + _extraPrecision, BigDecimal.ROUND_HALF_UP);
}
return res.setScale(_precision, BigDecimal.ROUND_HALF_UP);
}
public static void main(String[] args)
{
BigDecimalTrigTest bdtt = new BigDecimalTrigTest(50);
BigDecimal half = new BigDecimal("0.5");
System.out.println("Math.cos(0.5) = " + Math.cos(0.5));
System.out.println("this.cos(0.5) = " + bdtt.cos(half));
}
}
Update
A test with Wolfram Alpha for cos(.5) to 10000 digits (as #RC commented) gives the same result as my test code for the same precision. Perhaps 5 is enough as extra precision. But I need more tests to be sure.
You can reduce numbers not in -pi>x>=pi to that range. The Taylor expansion for sin(x) gets less accurate as abs(x) increases, so reducing x down to this range will increase your accuracy for large numbers.

How to add decimal point before x digits in Java

Lets suppose I have a value 12345678 and a number say x=2, and I want the final output as 123456.78 and if the value of x is 4, the final output would be 1234.5678.
Please tell how would I can achieve this?
Given that you're dealing with shifting a decimal point, I'd probably use BigDecimal:
long integral = 12345678L;
int x = 4; // Or 2, or whatever
BigDecimal unscaled = new BigDecimal(integral);
BigDecimal scaled = unscaled.scaleByPowerOfTen(-x);
System.out.println(scaled); // 1234.5678
BigInteger d = new BigInteger("12345");
BigDecimal one = new BigDecimal(d, 3);//12.345
BigDecimal two = new BigDecimal(d, 2);//123.45
Try this out :
String data = "12345678";
StringBuilder builder = new StringBuilder(data);
int x = 4;
builder.insert(builder.length() - x, ".");
System.out.println(builder);
Divide the value by 10 raise to the power x.

Java rounding issues

I am using the following code to round the float value which is given as input. But i cant get it right. If i give $80 i should get $80.00 and if i give $40.009889 i should get $40.01. How do i do this ?
public class round {
public static float round_this(float num) {
//float num = 2.954165f;
float round = Round(num,2);
return round;
}
private static float Round(float Rval, int Rpl) {
float p = (float)Math.pow(10,Rpl);
Rval = Rval * p;
float tmp = Math.round(Rval);
return (float)tmp/p;
}
}
This is why you don't use floats for money, because you get stuck in this game of 'Garbage In, Data Out'. Use java.math.BigDecimal instead. BigDecimal lets you specify a fixed-decimal value that isn't subject to representation problems.
(When I say don't use floats that includes doubles, it's the same issue.)
Here's an example. I create two BigDecimal numbers. For the first one I use the constructor that takes a floating-point number. For the first one I use the constructor that takes a string. In both cases the BigDecimal shows me what the number is that it holds:
groovy:000> f = new BigDecimal(1.01)
===> 1.0100000000000000088817841970012523233890533447265625
groovy:000> d = new BigDecimal("1.01")
===> 1.01
See this question for more explanation, also there's another question with a good answer here.
From The Floating-Point Guide:
Why don’t my numbers, like 0.1 + 0.2
add up to a nice round 0.3, and
instead I get a weird result like
0.30000000000000004?
Because internally, computers use a
format (binary floating-point) that
cannot accurately represent a number
like 0.1, 0.2 or 0.3 at all.
When the code is compiled or
interpreted, your “0.1” is already
rounded to the nearest number in that
format, which results in a small
rounding error even before the
calculation happens.
Use BigDecimal class instead of float.
And use Java code like this:
BigDecimal bd = new BigDecimal(floatVal);
bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
I wouldn't use float, I suggest using double or int or long or (if you have to) BigDecimal
private static final long[] TENS = new long[19];
static {
TENS[0] = 1;
for (int i = 1; i < TENS.length; i++) TENS[i] = TENS[i - 1] * 10;
}
public static double round(double x, int precision) {
long tens = TENS[precision];
long unscaled = (long) (x < 0 ? x * tens - 0.5 : x * tens + 0.5);
return (double) unscaled / tens;
}
This does not give a precise answer for all fractions as that is not possible with floating point, however it will give you an answer which will print correctly.
double num = 2.954165;
double round = round(num, 2);
System.out.println(round);
prints
2.95
This would do it.
public static void main(String[] args) {
double d = 12.349678;
int r = (int) Math.round(d*100);
double f = r / 100.0;
System.out.println(f);
}
You can short this method, it's easy to understand that's why I have written like this

Categories

Resources