Java Double calculation between decimal place [duplicate] - java

This question already has answers here:
How to avoid floating point precision errors with floats or doubles in Java?
(12 answers)
Closed 9 years ago.
private void fillInState() {
state [0][0] = 0.6;
state [0][1] = 0.4;
state [1][0] = 0.3;
state [1][1] = 0.6;
state [1][2] = 0.1;
state [2][0] = 0.7;
state [2][2] = 0.3;
}
private void fillInNext() {
next [0][0] = 1.0;
}
public void chain (int time) {
for(int i=0; i<time;i++) {
for( int j=0; j<3;j++) {
double temp = 0;
for(int k=0;k<3;k++) {
temp = state[k][j] * next [k][i] + temp;
if(k==2) {
next[j][i+1]=temp;
}
}
}
}
}
The expected answer should be:
1.0 0.6 0.48
0.0 0.4 0.48
0.0 0.0 0.04
But the answer in blueJ is:
1.0 0.6 0.48
0.0 0.4 0.48
0.0 0.0 0.04000000000000001
Anyone knows what happened? Is that about the Double class or blueJ?

This is simply due to floating-point errors. You can always format the output using something like printf:
double d = 0.04000000000000001;
System.out.println(d);
System.out.printf("%.2f%n", d);
0.04000000000000001
0.04
See Oracle's tutorial Formatting Numeric Print Output.

This is because of how floating point arithmetic works. Try looking at this for some detail. Basically it is not possible to represent arbitrary Decimal numbers in a finite number of binary digits using the normal floating point representation.
If you need your arithmetic to match closely with Decimals then use BigDecimal.
Often you can just ignore these errors by rounding the result you output to screen etc.

Related

How do you use double to get the expected result

I was trying out subtracting numbers in java, and this gives me unexpected result
public class FloatWeird
{
public static void main(String[] args)
{
double n = 1;
for(int i = 0;i<10;i++)
{
System.out.println(n);
n = n - 0.10;
}
}
}
Result
1.0
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
I have gone through a few forums and understand that using the BigDecimal class is one solution. However, is there a way to correct it in a simpler way using double as above?
I suggest you use appropriate rounding.
System.out.printf("%.1f%n", n);
When ever you print a double you have to consider what the appropriate round is.
You can also round the result as you calculate which is what BigDecimal does.
n = n - 0.10;
n = Math.round(n * 10) / 10.0;
This will reduce cumulative error.
Another approach is to work in a different unit, e.g. instead of dollar you use cents. This means you can use an int or long and only convert
to dollars for presentation.
long n = 100;
for(int i = 0; i < 10; i++) {
System.out.println(n / 100.0);
n = n - 10;
}
this prints http://ideone.com/Uf70jC
1.0
0.9
0.8
0.7
0.6
0.5
0.4
0.3
0.2
0.1
Note: even if you use BigDecimal, this still have to do this except the API can help you determine at what point you should do this.

Error in Multiplication of floating point numbers [duplicate]

I am doing a school assignment in which I solve an equation involving coordinates in a circle (r^2 = x^2 + y^2) were r = 1, and you increment through x values solving for y. I am getting a repeating decimals even though I only incrementing in tenths. I have no idea why and have tried it in a few different ways. Here is the code.
double r = 1;
double rSqr;
double x = 1;
double xSqr;
double y;
double ySqr;
double inc = 0.1;
int count = 0;
while(x > -r)
{
x = r - (count * inc);
rSqr = Math.pow(r, 2);
xSqr = Math.pow(x, 2);
ySqr = rSqr - xSqr;
y = Math.sqrt(ySqr);
count++;
System.out.println(x + " " + y);
}
and the output is this
1.0 0.0
0.9 0.4358898943540673
0.8 0.5999999999999999
0.7 0.714142842854285
0.6 0.8
0.5 0.8660254037844386
0.3999999999999999 0.9165151389911681
0.29999999999999993 0.9539392014169457
0.19999999999999996 0.9797958971132712
0.09999999999999998 0.99498743710662
0.0 1.0
-0.10000000000000009 0.99498743710662
-0.20000000000000018 0.9797958971132712
-0.30000000000000004 0.9539392014169457
-0.40000000000000013 0.9165151389911679
-0.5 0.8660254037844386
-0.6000000000000001 0.7999999999999999
-0.7000000000000002 0.7141428428542849
-0.8 0.5999999999999999
-0.9000000000000001 0.43588989435406705
-1.0 0.0
The problem is that double is imprecise. It uses 64 bits to represent decimal numbers; some bits are used for the numeric part, and some for the exponent, but many seemingly simple decimal numbers can not be accurately represented in this way, for example 0.1. See this wiki article for more.
One way around the problem is to display the number using DecimalFormat, which can round the number for presentation purposes. Here's some example code:
public static void main(String[] args) {
DecimalFormat decimalFormat = new DecimalFormat("#0.000");
double d = 1 - .9; // one way to get a repeating decimal floating point number
System.out.println(d);
System.out.println(decimalFormat.format(d));
}
Output:
0.09999999999999998
0.100
It is the IEEE 754 floating point representation.
Use BigDecimal as datatype instead of double to solve your problem.
But take care as BigDecimal is immutable.
BigDecimal r = BigDecimal.ONE;
BigDecimal rSqr;
BigDecimal x = BigDecimal.ONE;
BigDecimal xSqr;
BigDecimal y;
BigDecimal ySqr;
BigDecimal inc = new BigDecimal("0.1");
int count = 0;
while(x.compareTo(r.negate())>0)
{
// i'll let you fill in this part
}

Truncation Error when I try to create a Array of values [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Strange floating-point behaviour in a Java program [duplicate]
(4 answers)
Closed 4 years ago.
I want a create a array of values with a interval of 0.2
I used the code :
public class TrialCode {
public static void main(String[] args) {
float a = -1.0f, b = 0.2f;
for (int i = 0; i <10; i++) {
a = a + b;
System.out.println(a);
}
}
}
Now the output that i am getting is :
-0.8
-0.6
-0.40000004
-0.20000003
-2.9802322E-8
0.19999997
0.39999998
0.59999996
0.79999995
0.99999994
whereas the output I want is
-0.8, -0.6, -0.4, -0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0
What should I do ?
If you don't want floating-point arithmetic, then don't use floating-point types. Float and Double aren't the only non-integral Numbers in the Java core library. What you're doing calls for BigDecimal.
import java.math.BigDecimal;
public class TrialCode {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("-1.0");
BigDecimal b = new BigDecimal("0.2");
for (int i = 0; i < 10; i++) {
a = a.add(b);
System.out.println(a);
}
}
}
Floating point numbers only work up to a certain precision. For float it is 6-7 significant digits, for double it is 15-16 significant digits. And due to the binary representation simple decimal fractions like 0.1 cannot be represented exactly.
You can round the output to get the results you want:
System.out.printf("%.1f", a);
You can use something like
new DecimalFormat("#.#").format(0.19999997); //"0.2"

java for-loop problem

I am making a Java program to calculate Simpson's rule for integrals. Here is the code I have. Notice the second column of numbers in the output values of count == 4,9,10,11. They are not numbers that I need, they do not follow the pattern. I need these numbers to be accurate. What is going on and how can I fix it?
public static void main(String[] args)
{
double totalS = 0.0;
int count = 0;
for(double i=0; i< 4; i += 0.4 )
{
count++;
totalS += Sfunction(i, count);
System.out.println(count + " " + i + " " + totalS);
}
}
public static double Sfunction(double f1, int count)
{
double value;
if (f1 == 0.0 || f1 == 4.0)
value = Math.cos(Math.sqrt(f1));
else if ((count % 2) == 1)
value = 2 * Math.cos(Math.sqrt(f1));
else
value = 4 * Math.cos(Math.sqrt(f1));
return value;
}
I get the output of:
1 0.0 1.0
2 0.4 4.226313639540303
3 0.8 5.478244888601832
4 1.2000000000000002 7.30884788480188
5 1.6 7.911122809972827
6 2.0 8.534897589034324
7 2.4 8.578100205110182
8 2.8 8.168723348285942
9 3.1999999999999997 7.736055200662704
10 3.5999999999999996 6.452869366954546
11 3.9999999999999996 5.620575693860261
Each time you go round your loop, you are compounding the error in the inexact addition of 0.4 to i.
Instead, use an integral value for the loop counter, and scale that to get a better approximation to the values:
for ( int count = 0; count < 10; ++count ) {
final double i = 0.4 * count;
System.out.println ( ( count + 1 ) + " " + i );
}
This will not eliminate the floating point error, but it will mean it is not increasing at each iteration. To remove the error from the output, format the output to a reasonable number of decimal places:
for ( int count = 0; count < 10; ++count ) {
final double i = 0.4 * count;
System.out.printf ( "%2d %.1f\n", ( count + 1 ), i );
}
This is a classic floating point problem. If you need accuracy in your decimals, you should be using BigDecimal
This is how floating point numbers work in computers.
You can round the display, but the representation underneath won't change. Use java.text.DecimalNumberFormat to round to two decimal places.
What you are seeing is a result of floating point precision error, the numbers aren't stored like you're probably thinking. You can round the answer to 1 decimal place to get rid of the error...but this is just a result of how doubles are stored in java.
There's some good reading on this topic over here: Why do simple math operations on floating point return unexpected (inaccurate) results in VB.Net and Python?
Your problem is that you are using floating point arithmetic which can only approximate values, but assuming you have infinite precision. You shouldn't do equality tests like this with floating point numbers:
if (f1 == 0.0 || f1 == 4.0)
Any equality test with a floating point number is a code smell. With a float you should always check if it lies within a certain range, for example in the range 3.9999 to 4.0001.
In this specific example though, you also handily have another parameter called count which is an int. You can do equality tests with that. Maybe you can test that instead.
try to print them with only one decimal digit:
System.out.printf("%.1f", Math.E); // prints 2.7
System.out.printf("%.2f", Math.E); // prints 2.72
System.out.printf("%.3f", Math.E); // prints 2.718
or even try to specify the keyword strictfp for your number crunching methods
From your loop condition, it looks like you don't want line 11 to be processed at all. I recommend you use an integer loop index and use it to compute the values you pass to Sfunction. The following should be the equivalent of what you have now (except it leaves out line 11).
double totalS = 0.0;
for( int i = 1; i <= 10; i++ )
{
double f1 = 0.4 * (i - 1);
totalS += Sfunction(f1, i);
System.out.println(i + " " + f1 + " " + totalS);
}
Your problem with print precision can be solved with DecimalFormat, as suggested in other answers.

round BigDecimal to nearest 5 cents

I'm trying to figure out how to round a monetary amount upwards to the nearest 5 cents. The following shows my expected results
1.03 => 1.05
1.051 => 1.10
1.05 => 1.05
1.900001 => 1.10
I need the result to be have a precision of 2 (as shown above).
Update
Following the advice below, the best I could do is this
BigDecimal amount = new BigDecimal(990.49)
// To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20
def result = new BigDecimal(Math.ceil(amount.doubleValue() * 20) / 20)
result.setScale(2, RoundingMode.HALF_UP)
I'm not convinced this is 100% kosher - I'm concerned precision could be lost when converting to and from doubles. However, it's the best I've come up with so far and seems to work.
Using BigDecimal without any doubles (improved on the answer from marcolopes):
public static BigDecimal round(BigDecimal value, BigDecimal increment,
RoundingMode roundingMode) {
if (increment.signum() == 0) {
// 0 increment does not make much sense, but prevent division by 0
return value;
} else {
BigDecimal divided = value.divide(increment, 0, roundingMode);
BigDecimal result = divided.multiply(increment);
return result;
}
}
The rounding mode is e.g. RoundingMode.HALF_UP. For your examples, you actually want RoundingMode.UP (bd is a helper which just returns new BigDecimal(input)):
assertEquals(bd("1.05"), round(bd("1.03"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.10"), round(bd("1.051"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.05"), round(bd("1.05"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.95"), round(bd("1.900001"), bd("0.05"), RoundingMode.UP));
Also note that there is a mistake in your last example (rounding 1.900001 to 1.10).
I'd try multiplying by 20, rounding to the nearest integer, then dividing by 20. It's a hack, but should get you the right answer.
I wrote this in Java a few years ago: https://github.com/marcolopes/dma/blob/master/org.dma.java/src/org/dma/java/math/BusinessRules.java
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
*/
public static BigDecimal round(BigDecimal value, BigDecimal rounding, RoundingMode roundingMode){
return rounding.signum()==0 ? value :
(value.divide(rounding,0,roundingMode)).multiply(rounding);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 10
*<p>
* HALF_UP<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round up.
* Behaves as for RoundingMode.UP if the discarded fraction is >= 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
* Note that this is the rounding mode commonly taught at school.
*/
public static BigDecimal roundUp(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_UP);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 0
*<p>
* HALF_DOWN<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round down.
* Behaves as for RoundingMode.UP if the discarded fraction is > 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
*/
public static BigDecimal roundDown(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_DOWN);
}
Here are a couple of very simple methods in c# I wrote to always round up or down to any value passed.
public static Double RoundUpToNearest(Double passednumber, Double roundto)
{
// 105.5 up to nearest 1 = 106
// 105.5 up to nearest 10 = 110
// 105.5 up to nearest 7 = 112
// 105.5 up to nearest 100 = 200
// 105.5 up to nearest 0.2 = 105.6
// 105.5 up to nearest 0.3 = 105.6
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Ceiling(passednumber / roundto) * roundto;
}
}
public static Double RoundDownToNearest(Double passednumber, Double roundto)
{
// 105.5 down to nearest 1 = 105
// 105.5 down to nearest 10 = 100
// 105.5 down to nearest 7 = 105
// 105.5 down to nearest 100 = 100
// 105.5 down to nearest 0.2 = 105.4
// 105.5 down to nearest 0.3 = 105.3
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Floor(passednumber / roundto) * roundto;
}
}
In Scala I did the following (Java below)
import scala.math.BigDecimal.RoundingMode
def toFive(
v: BigDecimal,
digits: Int,
roundType: RoundingMode.Value= RoundingMode.HALF_UP
):BigDecimal = BigDecimal((2*v).setScale(digits-1, roundType).toString)/2
And in Java
import java.math.BigDecimal;
import java.math.RoundingMode;
public static BigDecimal toFive(BigDecimal v){
return new BigDecimal("2").multiply(v).setScale(1, RoundingMode.HALF_UP).divide(new BigDecimal("2"));
}
Based on your edit, another possible solution would be:
BigDecimal twenty = new BigDecimal(20);
BigDecimal amount = new BigDecimal(990.49)
// To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20
BigDecimal result = new BigDecimal(amount.multiply(twenty)
.add(new BigDecimal("0.5"))
.toBigInteger()).divide(twenty);
This has the advantage, of being guaranteed not to lose precision, although it could potentially be slower of course...
And the scala test log:
scala> var twenty = new java.math.BigDecimal(20)
twenty: java.math.BigDecimal = 20
scala> var amount = new java.math.BigDecimal("990.49");
amount: java.math.BigDecimal = 990.49
scala> new BigDecimal(amount.multiply(twenty).add(new BigDecimal("0.5")).toBigInteger()).divide(twenty)
res31: java.math.BigDecimal = 990.5
For this test to pass :
assertEquals(bd("1.00"), round(bd("1.00")));
assertEquals(bd("1.00"), round(bd("1.01")));
assertEquals(bd("1.00"), round(bd("1.02")));
assertEquals(bd("1.00"), round(bd("1.024")));
assertEquals(bd("1.05"), round(bd("1.025")));
assertEquals(bd("1.05"), round(bd("1.026")));
assertEquals(bd("1.05"), round(bd("1.049")));
assertEquals(bd("-1.00"), round(bd("-1.00")));
assertEquals(bd("-1.00"), round(bd("-1.01")));
assertEquals(bd("-1.00"), round(bd("-1.02")));
assertEquals(bd("-1.00"), round(bd("-1.024")));
assertEquals(bd("-1.00"), round(bd("-1.0245")));
assertEquals(bd("-1.05"), round(bd("-1.025")));
assertEquals(bd("-1.05"), round(bd("-1.026")));
assertEquals(bd("-1.05"), round(bd("-1.049")));
Change ROUND_UP in ROUND_HALF_UP :
private static final BigDecimal INCREMENT_INVERTED = new BigDecimal("20");
public BigDecimal round(BigDecimal toRound) {
BigDecimal divided = toRound.multiply(INCREMENT_INVERTED)
.setScale(0, BigDecimal.ROUND_HALF_UP);
BigDecimal result = divided.divide(INCREMENT_INVERTED)
.setScale(2, BigDecimal.ROUND_HALF_UP);
return result;
}
public static BigDecimal roundTo5Cents(BigDecimal amount)
{
amount = amount.multiply(new BigDecimal("2"));
amount = amount.setScale(1, RoundingMode.HALF_UP);
// preferred scale after rounding to 5 cents: 2 decimal places
amount = amount.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
return amount;
}
Note that this is basically the same answer as John's.
public static void roundUp()
{
try
{
System.out.println("Enter the currency : $");
Scanner keyboard = new Scanner(System.in);
String myint = keyboard.next();
if (!isEmptyOrBlank(myint).booleanValue())
{
BigDecimal d = new BigDecimal(myint);
System.out.println("Enter the round up factor: $");
String roundUpFactor = keyboard.next();
if (!isEmptyOrBlank(roundUpFactor).booleanValue())
{
BigDecimal scale = new BigDecimal(roundUpFactor);
BigDecimal y = d.divide(scale, MathContext.DECIMAL128);
BigDecimal q = y.setScale(0, 0);
BigDecimal z = q.multiply(scale);
System.out.println("Final price after rounding up to " + roundUpFactor + " is : $" + z);
System.out.println("Want to try with other price Y/N :");
String exit = keyboard.next();
if ((!isEmptyOrBlank(exit).booleanValue()) && ("y".equalsIgnoreCase(exit))) {
roundUp();
} else {
System.out.println("See you take care");
}
}
}
else
{
System.out.println("Please be serious u r dealing with critical Tx Pricing");
}
}
catch (Exception e)
{
System.out.println("Please be serious u r dealing with critical Tx Pricing enter correct rounding off value");
}
}
Tom has the right idea, but you need to use BigDecimal methods, since you ostensibly are using BigDecimal because your values are not amenable to a primitive datatype. Something like:
BigDecimal num = new BigDecimal(0.23);
BigDecimal twenty = new BigDecimal(20);
//Might want to use RoundingMode.UP instead,
//depending on desired behavior for negative values of num.
BigDecimal numTimesTwenty = num.multiply(twenty, new MathContext(0, RoundingMode.CEILING));
BigDecimal numRoundedUpToNearestFiveCents
= numTimesTwenty.divide(twenty, new MathContext(2, RoundingMode.UNNECESSARY));
You can use plain double to do this.
double amount = 990.49;
double rounded = ((double) (long) (amount * 20 + 0.5)) / 20;
EDIT: for negative numbers you need to subtract 0.5

Categories