Calculating a BigDecimal results in inaccurate answer - java

So I just wanted to screw around to see if I can make it so I can calculate E, but instead have it so I can have dynamic degrees of precision. While I did technically accomplish it, no matter what int i put in for the variable PRECISION, the last few numbers are always different from what the actual value of E is suppose to be. I'm not entirely sure why, but help would be appreciated.
import java.math.BigDecimal; //To use for calculating E
public class ComputeE {
public static double calcDenominator(int n)
{
double denominator = 1.0; //Start the BigInt with 1
for(int i = 1; i < n; i++) // Run n-1 amount of times
{
denominator = denominator * i; // Multiply BigInteger by the BigInteger obtained with the int value i
}
return denominator;
}
public static void main(String[] args) {
BigDecimal e = new BigDecimal(0.0);
int PRECISION = 15;
int iterations = 0;
for(int i = 0; i < PRECISION; i++)
{
iterations++;
BigDecimal numerator = new BigDecimal(1.0); // to divide, we need two BigDecimals, the numerator is 1
BigDecimal factorial = new BigDecimal(calcDenominator(i)); // the denominator is i! which we get from calling the factorial method
factorial = numerator.divide(factorial, PRECISION, BigDecimal.ROUND_UNNECESSARY); // compute 1/i!, note divide is overloaded, this version is used to
// ensure a limit to the iterations when division is limitless like 1/3
e = e.add(factorial); // add the latest 1/i! to e
}
System.out.println("Computed value of e : " + e);
System.out.println("Expected value of e : " + Math.E);
}
}

Rounding is necessary here. Use something like HALF_EVEN. Even better, use the enum value RoundingMode.HALF_EVEN, because the integer constants for rounding mode are deprecated.
In calcDenominator, change your for loop condition to i <= n, or else you'll add 1 one too many times in main and you'll get a value that's 1 too high.
You can use BigDecimal.ONE to initialize numerator. This doesn't affect the result, but why create an unnecessary object? Same comment on the initialization of e, except with BigDecimal.ZERO.
You are using the first PRECISION terms of an infinite series (Maclaurin Series) that approximates e, an irrational number. There is an error term when you cut off the for loop, and that is expected mathematically. With the above changes, and bumping PRECISION to 50, I get the following, which looks sufficiently precise.
Computed value of e : 2.71828182845904523536028747135266249775496954201584
Expected value of e : 2.718281828459045
It is precise, despite using the double constructor for BigDecimal because the significant digits for a double start with the first non-zero bit, so even if you're calculating 1/n! for large n, the significant digits are good enough for adding to the existing approximation for e.

Related

For two given positive numbers a and b. Find a raised to b. Output your number modulus 10^9+7

https://practice.geeksforgeeks.org/problems/abset-2/0/ , this is a gfg question where i am asked to output my number (a ^ b) modulus 10^9+7.
so here is my first code;
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t--!=0){
int a = sc.nextInt();
int b = sc.nextInt();
int result = 1;
for(int i = 0; i<b; i++){
result = result*a;
}
System.out.println(result%1000000007);
}
}
and it is not giving the correct output for 99^928. Then i changed the data type of result into long even that's giving a negative number. Then i had to change my code like this and it worked
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t--!=0){
int a = sc.nextInt();
int b = sc.nextInt();
long result = 1;
for(int i = 0; i<b; i++){
result = (result*a);
result = result%1000000007;
}
System.out.println(result);
}
here my question is when i put the result%1000000007 in the for loop how it worked, according to the problem was not i supposed to output the ultimate result modulus 10^9+7?
int and long have maximum values. Depending on a and b a^b exceeds that maximum and overflows.
The modulo operation in the end would then act on the wrong overflow value and the result would be off.
The thing with modulo is that you can apply modulo during your computation basically whenever you want without changing the result. (a + b) mod m has the same value as (a mod m + b mod m) mod m and similarly (a * b) mod m is the same as (a mod m * b mod m) mod m, that is simply how the modulo operator works. Simply play around with a few small values of a b and m on paper to see that the rules work.
It is very typical for assignments involving HUGE values to be computable only if you add some mod m steps somewhere in the mix (as long as they make sense).
99^928 is a large number with 1852 digits. The primitive data types int and long in Java don't have the storage capacity for such a number: the largest value you can store in an int is 2147483647 and the largest you can store in a long is 9223372036854775807. Operations that return larger values wrap around, returning a value that is correct modulo a power of 2, but completely unusable for most practical purposes.
If you printed out the intermediate values, you would see the result wrap around already at 99^10:
99^9 = 913517247483640899
99^10 = -1795512867667309079
This means if you wait to do the modulo operation until the last moment, you will be taking the modulo of an incorrect intermediate result. You must control how large the values get by using modulo already in the intermediate steps.
Note that Java does have classes for dealing with integers bigger than fits in a long: java.math.BigInteger. It even has a convenient and fast method for the "modular power" operation you are implementing:
BigInteger base = BigInteger.valueOf(1000000007);
BigInteger result = BigInteger.valueOf(a).modPow(BigInteger.valueOf(b), base);

how to create an Exp(-x^2) function?

I am using the "think java" book and I am stuck on exercise 7.6. The goal here is to write a function that can find . It gives you a couple hints:
One way to evaluate is
to use the infinite series expansion:
In other words, we need to add up a series of terms where the ith term
is equal to
Here is the code I came up with, but it is horribly wrong (when compared to Math.exp) for anything other than a power of 1. I don't understand why, as far as I can tell the code is correct with the formula from the book. I'm not sure if this is more of a math question or something related to how big of a number double and int can hold, but I am just trying to understand why this doesn't work.
public static void main(String[] args) {
System.out.println("Find exp(-x^2)");
double x = inDouble("Enter x: ");
System.out.println("myexp(" + -x*x + ") = " + gauss(x, 20));
System.out.println("Math.exp(" + -x*x + ") = " + Math.exp(-x*x));
}
public static double gauss(double x, int n) {
x = -x*x;
System.out.println(x);
double exp = 1;
double prevnum = 1;
int prevdenom = 1;
int i = 1;
while (i < n) {
exp = exp + (prevnum*x)/(prevdenom*i);
prevnum = prevnum*x;
prevdenom = prevdenom*i;
i++;
}
return exp;
} // I can't figure out why this is so inacurate, as far as I can tell the math is accurate to what the book says the formula is
public static double inDouble(String string) {
Scanner in = new Scanner (System.in);
System.out.print(string);
return in.nextDouble();
}
I am about to add to the comment on your question. I do this because I feel I have a slightly better implementation.
Your approach
Your approach is to have the function accept two arguments, where the second argument is the number of iterations. This isn't bad, but as #JamesKPolk pointed out, you might have to do some manual searching for an int (or long) that won't overflow
My approach
My approach would use something called the machine epsilon for a data type. The machine epsilon is the smallest number of that type (in your case, double) that is representable as that number. There exists algorithm for determining what that machine epsilon is, if you are not "allowed" to access machine epsilon in the Double class.
There is math behind this:
The series representation for your function is
Since it is alternating series, the error term is the absolute value of the first term you choose not to include (I leave the proof to you).
What this means is that we can have an error-based implementation that doesn't use iterations! The best part is that you could implement it for floats, and data types that are "more" than doubles! I present thus:
public static double gauss(double x)
{
x = -x*x;
double exp = 0, error = 1, numerator = 1, denominator = 1;
double machineEpsilon = 1.0;
// calculate machineEpsilon
while ((1.0 + 0.5 * machineEpsilon) != 1.0)
machineEpsilon = 0.5 * machineEpsilon;
int n = 0; //
// while the error is large enough to be representable in terms of the current data type
while ((error >= machineEpsilon) || (-error <= -machineEpsilon))
{
exp += error;
// calculate the numerator (it is 1 if we just start, but -x times its past value otherwise)
numerator = ((n == 0) ? 1 : -numerator * x);
// calculate the denominator (denominator gets multiplied by n)
denominator *= (n++);
// calculate error
error = numerator/denominator;
}
return exp;
}
Let me know how this works!

Rounding Off to 2 decimal places giving issues while summing up

Getting the following sets of numbers -->
[5.1429,5.1429,5.1429,5.1429,5.1429,5.1429,5.1426]
[0.8333,0.8333,0.8333,0.8333,0.8333,0.8335]
When added, these give a whole number.
Now we have to display these numbers after rounding to 2 decimal places which look like this -->
[5.14, 5.14, 5.14, 5.14, 5.14, 5.14, 5.14]
[0.83, 0.83, 0.83, 0.833, 0.83, 0.83]
Which is not summing up to a whole number. This is causing the whole total to come something like 99.96 or 101.01.
Is there a way we can round the numbers so that the total comes to a whole number.
I am not looking for a way to round off the sum. i am looking for a way to manipulate the already rounded numbers (like 5.14,5.14.. etc) so that they give a whole number. (if there is a way.. that is :) )
You would introduce the smallest absolute rounding error - while keeping the total sum intact - if you sort the inputs by decreasing third decimal and round everything down, except for just enough numbers to reach the target.
As a simple example:
input
0.132, 0.226, 0.257, 0.385 // sums up to 1.00
sort by 3rd decimal (descending)
0.257, 0.226, 0.385, 0.132
round everything down
0.25, 0.22, 0.38, 0.13 // sums up to 0.98
round up just enough to reach a whole number
0.26, 0.23, 0.38, 0.13 // sums up to 1.00
In code (untested):
public void printRounded(double[] ds) {
// create wrapper objects
int n = ds.length;
Wrapper[] ws = new Wrapper[n];
for (int i = 0; i < n; i++)
ws[i] = new Wrapper(i, (int)(ds[i] * 1000) % 10, (int)(ds[i] * 100));
// sort by third decimal, descending
Arrays.sort(ws, new Comparator<Wrapper>() {
public int compare(Wrapper o1, Wrapper o2) {
return o2.thirdDecimal.compareTo(o1.thirdDecimal);
}
});
// find number of elements that must be rounded up and increment
int sum = 0;
for (int i = 0; i < n; i++)
sum += ws[i].prefix;
int numberToIncrement = 100 - (sum % 100);
for (int i = 0; i < numberToIncrement ; i++)
ws[i].prefix++;
// sort back to input order
Arrays.sort(ws, new Comparator<Wrapper>() {
public int compare(Wrapper o1, Wrapper o2) {
return o1.index.compareTo(o2.index);
}
});
// print values
for (int i = 0; i < n; i++) {
System.out.println(ws[i].prefix / 100 + "." ws[i].prefix % 100);
}
}
private class Wrapper {
public Wrapper(int index, int thirdDecimal, int prefix) {
this.index = index;
this.thirdDecimal = thirdDecimal;
this.prefix = prefix;
}
public int index;
public int thirdDecimal;
public int prefix;
}
Instead of using the custom formatting you could of course convert the ints back to double and use standard formatting.
If I round the result, I get a whole number.
public static void main(String... ignored) {
sum(5.1429, 5.1429, 5.1429, 5.1429, 5.1429, 5.1429, 5.1426);
sum(0.8333, 0.8333, 0.8333, 0.8333, 0.8333, 0.8335);
}
private static void sum(double... xs) {
double sum = 0;
for (double x : xs) {
sum += x;
}
System.out.printf("sum was %.2f%n", sum);
}
BTW: If you know you expect a whole number, you should round to a whole number, not two decimal places.
There's no way to do this unless you abandon the firm requirement
for rounding your numbers (to 2 decimal places) and allow for
(a) truncating your numbers (after the 2nd decimal place);
(b) rounding up/down when actually you need to round down/up.
If you allow for these operations (on some of the numbers) instead of
rounding all of them by the book, then most probably you can come up
with some algorithm which would give you the same (integral) sum even
after the rounding/truncation operations is applied on the original
numbers. But again, this is not really rounding (as we know it from math).
One way you could do it would be to round to two decimal places then take each number an multiply it by a hundred and divide it by the sum of the numbers. This should get you closer to 100 and if you iterate over that enough times your result should approach 100. So basically (pseudcode)
lsum = 0
vecNums = //previous numbers
while abs(nsum -100) >.01
for i in vecNums
i = (i*100)/sum(vecNums)
return vecNums
You could just sum them up as you did, and then round the final sum as well.
You can take a look at the Math.round(double a) method
System.out.println(Math.round(12.51));//Yields 13
System.out.println(Math.round(12.49));//Yields 12
System.out.println(Math.round(12.50));//Yields 13
from Round off in JAVA

Error in calculating PI in Java with BigDecimal

I have to calculate PI to a certain number of decimals (given in variable zecimale), using the Leibniz formula for calculating PI. I don't know why, but not a single addition or subtraction on that BigDecimal isn't modifying the value of PI.
Why is this?
int zecimale = 0;
if (args.length > 0) {
try {
zecimale = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument" + " must be an integer");
System.exit(1);
}
}
long start = System.nanoTime();
double numitor = 1;
BigDecimal numitor1 = new BigDecimal(1/numitor);
BigDecimal pi = new BigDecimal(1);
for(int x = 0; pi.scale() <= zecimale; x++)
{
numitor1 = new BigDecimal(1 / numitor);
if(x % 2 == 0)
{
pi.add(numitor1);
}
else
{
pi.subtract(numitor1);
}
BigDecimal doi = new BigDecimal(2);
numitor = numitor + 2;
System.out.println(x);
System.out.println(pi);
}
BigDecimal patru;
patru = new BigDecimal(4);
pi.multiply(patru);
BigDecimal is immutable so the is no way to change it's value. (In the same way String does) This is why all the methods would operate on a BigDecimal return a new BigDecimal as the result
e.g.
pi = pi.add(numitor1);
The second problem is you are using a double in your calculation, defeating the whole point of using a BigDecimal.
The expression pi.scale() should be ~53 after the first iteration and won't get much higher the way ti is written. scale only tells you how many decimal places there are after the dot, not the accuracy of the solution.
You will get better performance if you calculate two expressions in a single loop.
The last problem you have is that each digit takes 10x longer to evaluate, calculating mroe than 15 digits (more than the precision you can get with double) it will take years to complete.

Counting the number of functions an Object does

I have created this program/scenario.
Multiple Robots are created and then "escape" from the room that has been created. I have also created a counter to count the moves that the robots make and then form an average.
I have created all this it only wants to return 0 for some reason. It's throwing up no errors or the like so I feel like I'm missing something obvious.
Here are both parts of the code:
public static double countingMoves;
.
.
.
public void move() {
super.move();
countingMoves++;
}
public int getRobotMoves() {
return countingMoves;
}
int Counter = EscapeBot.countingMoves/10;
int Counter = EscapeBot.countingMoves/10;
First Point
You're dividing two integers, which as stated in the comments, will result to 0 is the result is < 0. Cast one of these types to a double. This process is called Arithmetic Promotion, where every element in an expression has it's precision increased to the element with the highest precision. E.g:
int / int = int
double / double = double
int / double = double
int + String = String
For your code:
double Counter = EscapeBox.countingMoves/10.0;
Second Point
The Java naming convention states that the first word of variables or methods that are not constants must begin with a lower case letter.
Counter -> counter
Third and hopefully final point
If you look at where you are computing the average, 0.0 is actually correct. You compute the average at the start, before you have any moves.
double Counter = EscapeBot.countingMoves/10.0;
// When computed at the start, this equals:
// double Counter = EscapeBot.countingMoves(0)/10.0 = 0/10.0 = 0.0
Comes before any moves. By putting this at the end of your code, you should get a more accurate reading.

Categories

Resources