I have got a Problem, I am developing an Application which should be able to do some mathematic calculations. These calculations have to be exact (or rather not obviously wrong)
But this simple Code
double a = 3.048d;
double b = 1000d;
double c = a / b;
gives me a wrong result c is not 0.003048 as expected instead it is 0.0030480000000000004 which is obviously wrong.
double d = 3.048 / 1000;
this second code-snipet gives the correct result.
I am aware that all floatingpoint arithmetic is not exact when calculating with computers but I don't know how to solve this problem.
thanks in advance!
Ludwig
Developing for:
- Android 2.2
Testdevice:
- HTC Desire
What you need to use for exact percision is the BigDecimal object:
BigDecimal a = new BigDecimal("3.048");
BigDecimal b = new BigDecimal(1000);
BigDecimal c = a.divide(b);
System.out.println(c); //0.003048
This is a consequence of the IEEE 754 floating point representation, not an error. To deal with it, round your result to an appropriate precision.
Use a BigDecimal for precise floating point calculations. Setting the scale allows you to specify precisely how far out you want to go for output.
import java.math.BigDecimal;
class Test{
public static void main(String[] args){
BigDecimal a = new BigDecimal("3.048");
BigDecimal b = new BigDecimal(1000);
BigDecimal c = a.divide(b).setScale(6);
System.out.println(c); //0.003048
}
}
Use BigDecimal for such precise allocations.
Btw the result for d is obviously right, because double has a machine encoding which cannot store the result, which you perceive as correct.
Related
How can I get the correct answer when I need to use BigDecimal without losing precision.
BigDecimal a = new BigDecimal(0.5);
BigDecimal b = new BigDecimal(30);
BigDecimal c = new BigDecimal(18000);
a.divide(b).multiply(c);
How could I get the exact 300 in this case?
Thanks!
You can use the MathContext parameter in the divide method for this.
For example, a.divide(b, MathContext.DECIMAL128).multiply(c); will give you the precision you need (with an error of magnitude 1e-32). If you do not want to lose any precision, you can use MathContext.UNLIMITED, but this will result in a non-terminating decimal expansion.
In your case specifically, you can also try to rewrite the equation to prevent any rounding from happening: a / b * c = c / b * a.
I am making a phi (golden ratio) calculator, and I'm having an issue with the precision of the answers I get.
I realized that there seems to be a fixed number of digits that my answers can be up to, and afterwards it just truncates. At first I thought it was an issue with doubles, so I changed to BigDecimals. Yet the problem still persists.
Here was my original double logic:
public static final double PHI = 1.6180339887498948482045;
b = Double.parseDouble(field.getText());
a = b * PHI;
aPlusB = a + b;
System.out.println(a.toString());
Here's the code for my BigDecimal Logic:
public static final double PHI = 1.6180339887498948482045;
BigDecimal phi = new BigDecimal(calculationHolder.PHI);
MathContext context = new MathContext(15, RoundingMode.HALF_UP);
BigDecimal a = new BigDecimal(BigInteger.ZERO);
BigDecimal b = new BigDecimal(BigInteger.ZERO);
BigDecimal aPlusB = new BigDecimal(BigInteger.ZERO);
b = new BigDecimal(field.getText());
a = b.multiply(phi, context);
aPlusB = a.add(b, context);
System.out.println(a.toString());
Now if I were to make b = 1:
my double logic would return 1.618033988749895(many digits short of the real value it should be).
If I were to use my BigDecimal logic, it would return 1.61803398874989 (even less precise)
If I were to use a really large number like 123456789123456 for b,
My double logic would return 199757280943680.16, and by BigDecimal logic returns 199757280943680 (even less precise, and not even any decimals).
I'm confused about this behavior. It seems like, if anything, the BigDecimal logic is giving me even less precise answers, and I don't know why.
Could anyone please shed some light on this?
You specified 15 decimal digits of precision here:
new MathContext(15, RoundingMode.HALF_UP);
You got 15 decimal digits of precision here:
1.61803398874989
You got exactly what you asked for. Do you understand what the first constructor parameter does?
I was expecting this code:
double valore = 20.775;
BigDecimal c = new BigDecimal(valore);
c = c.setScale(2, RoundingMode.HALF_UP);
System.out.println(c.doubleValue());
to return 20.78, but it's returning 20.77 instead.
Is it an error? Or am I missing something?
Everything is correct. You can read some high-level details in another answer on SO or read more in documentation to BigDecimal
It is not common to use BigDecimal constructor with double param, because it will exactly represent what is inside double.
So when you write: new BigDecimal(20.775) you are not necessarily have 20.775 as a result (rather you will have something like 20.77499999999999857891452847979962825775146484375)
For you to test:
1) testing BigDecimal representation
a) System.out.println(new BigDecimal(20.775)); => 20.77499999999999857891452847979962825775146484375
b) System.out.println(new BigDecimal("20.775")); => 20.775
2) test rounding with different BigDecimal constructors:
a) new BigDecimal(20.775) after rounding half up will show 20.77.
b) new BigDecimal("20.775") after rounding half up will show 20.78.
c) new BigDecimal(String.valueOf(20.775) after rounding half up will show 20.78.
So as a conclusion:
Do not use BigDecimal constructor with double param.
Instead use BigDecimal constructor with String param.
Hope it helps
In Java, I would like 0.101d, 0.109999999d, and 0.11000d to all be functionally equivalent. I have attempted to use BigDecimal and a MathContext with 2 digits of precision and RoundingMode.CEILING to do this, but my unit test shows that 0.11000 rounds to 0.12. I want 0.110000d to Round to 0.11.
private static MathContext targetMathContext = new MathContext(2, RoundingMode.CEILING);
public static double roundedTarget(double d) {
BigDecimal bd = new BigDecimal(d,targetMathContext);
return bd.doubleValue();
}
JUnit:
double c = 0.445d;
double s = 0.5d;
double p = (s-c)/s; // 0.1099999..... in dfp
double rgpp = roundedTarget(p); // 0.11
double rgppp = roundedTarget(rgpp); // 0.12
// operation is not idempotent as f(x) != f(f(x)) :(
Assert.assertEquals("These values should be equal",rgpp,rgppp);
Solution:
public static double roundedTarget(double d) {
return BigDecimal.valueOf(d)
.setScale(2,BigDecimal.ROUND_CEILING)
.doubleValue();
}
I'm reluctant to call this operation idempotent, since the input of your first application of the function would be different than the next (since the result you get would change the value of x).
In either event, main issue is that you're using doubles in one spot (and introducing floating-point inaccuracies), and BigDecimal in another (if used correctly, is less impacted by those inaccuracies).
The easiest thing to do would be to set a scale of 2 decimal places on your doubles, and then round them however you like. As an example, all of these values satisfy the conditions you mention you want in your comments.
BigDecimal firstDecimal = BigDecimal.valueOf(0.101).setScale(2, RoundingMode.CEILING);
BigDecimal secondDecimal = BigDecimal.valueOf(0.10999).setScale(2, RoundingMode.CEILING);
BigDecimal thirdDecimal = BigDecimal.valueOf(0.110000).setScale(2, RoundingMode.CEILING);
BigDecimal fourthDecimal = BigDecimal.valueOf(0.1101).setScale(2, RoundingMode.CEILING);
System.out.println(firstDecimal); // 0.11
System.out.println(secondDecimal); // 0.11
System.out.println(thirdDecimal); // 0.11
System.out.println(fourthDecimal); // 0.12
The main takeaway here is: if you're going to use BigDecimal, be consistent with it throughout. There's no real reason to interlace or interweave working with raw doubles and BigDecimal, as it will only lead to headaches like this.
I wanted to know how I can generate pi to the nth digit. I have a couple of basic ideas.
Use Math.PI and increase the precision (if that's possible)
Use Euler's formula to generate pi but even here, I would need to increase the precision (I think)
There is also Srinivasa Ramanujan's formula for generating PI which is known for it's rapid convergence. This formula seems difficult to implement. I believe, I would have to also increase deicmal precision here.
So in short, either way, I would need to increase the precision of BigDecimal depending on what the nth digit is. How would I go about increasing the precision of BigDecimal to nth digit? Also, if there is a better and faster of doing this, can you please point me in the correct direction.
EDIT: I just want to generate PI. I don't want to use for calculations. and this is a question about how I can use BigDecimal to implement my ideas of generating PI.
Math.PI is of type double. That means about 15 decimal digits of precision, and that is all the data you have; nothing will magically make additional digits of PI appear.
BigDecimal has arbitrary precision. setScale() allows you to create BigDecimal objects with as much precision as you want and most of the arithmetic methods will automatically increase precision as required, but of course the more precision, the slower all calculations will be.
The most difficult part of implementing Ramanujan's formula will ironically be the sqrt(2) in the constant factor, because there is not built-in sqrt() for BigDecimal, so you'll have to write your own.
You need to use MathContext to increase the precision of the BigDecimal
e.g.
MathContext mc = new MathContext(1000);
BigDecimal TWO = new BigDecimal(2, mc);
It's important that ALL the BigDecimals you use in your calculations use that MathContext.
Heron's method should give you 1000 digits precision with only 10 iterations and a million digits with 20 iterations so it's certainly good enough.
Also, create all the constant BigDecimals like e.g. 26390 only once at the start of your program.
You can use this code
import java.math.BigDecimal;
import java.math.RoundingMode;
public final class Pi {
private static final BigDecimal TWO = new BigDecimal("2");
private static final BigDecimal FOUR = new BigDecimal("4");
private static final BigDecimal FIVE = new BigDecimal("5");
private static final BigDecimal TWO_THIRTY_NINE = new BigDecimal("239");
private Pi() {}
public static BigDecimal pi(int numDigits) {
int calcDigits = numDigits + 10;
return FOUR.multiply((FOUR.multiply(arccot(FIVE, calcDigits)))
.subtract(arccot(TWO_THIRTY_NINE, calcDigits)))
.setScale(numDigits, RoundingMode.DOWN);
}
private static BigDecimal arccot(BigDecimal x, int numDigits) {
BigDecimal unity = BigDecimal.ONE.setScale(numDigits,
RoundingMode.DOWN);
BigDecimal sum = unity.divide(x, RoundingMode.DOWN);
BigDecimal xpower = new BigDecimal(sum.toString());
BigDecimal term = null;
boolean add = false;
for (BigDecimal n = new BigDecimal("3"); term == null ||
term.compareTo(BigDecimal.ZERO) != 0; n = n.add(TWO)) {
xpower = xpower.divide(x.pow(2), RoundingMode.DOWN);
term = xpower.divide(n, RoundingMode.DOWN);
sum = add ? sum.add(term) : sum.subtract(term);
add = ! add;
}
return sum;
}
}
resource