I'm working on a lesson to create a vacation plan. When I write code like
double money1 = money / days;
money1 = money*100;
money1 = (int) money1/ 100.0;
IntelliJ underlines money / days and notifies me that it's redundant.
Why does it happen? How can I make it not redundant?
You're immediately reassigning the variable, so your calculation is lost.
Observe:
double money1 = money / days;
money1 = money*100; // <- Quotient is discarded and overwritten
money1 = (int) money1/ 100.0; <- Prior product is discarded and overwritten
This is what IntelliJ is warning you about; this smells very much like a bug. I can't reliably tell you which of these statements to change since they do very different things, but this is something you should look to debug in your logical flow.
Related
This question already has answers here:
Why not use Double or Float to represent currency?
(16 answers)
Closed 3 years ago.
I'm trying to split a bill and need to calculate how much each person would owe if the bill was split in even amounts. I know one amount will be different than the rest to account for the lost cents.
Assume 3 people try to split a bill for 200. 200 / 3 is 66.6666666667. What I planned on doing was charging the 2 first people 66.67 and the last gets lucky with the 66.66 bill.
At the minute, I have this so far:
private String calculateAmountToPay(String noOfParticipants, String owed) {
double amountOwed = Double.parseDouble(owed);
int noOfMembers = Integer.parseInt(noOfParticipants);
DecimalFormat amountFormat = new DecimalFormat("#.##");
amountFormat.setRoundingMode(RoundingMode.CEILING);
return amountFormat.format((amountOwed/(double)noOfMembers) / 100);
}
But this always will return 66.67. Is there a way that I can get it to only round up if there is a number greater than 2 decimal places, if not, it stays at 66.66 for example?
Maybe I'm approaching this the wrong way. I know currency can be finicky to deal with.
Before even thinking about arithmetic, you need to know that double is not an appropriate data type for use with currency, because it’s imprecise. So, stop using a floating point type (eg double) as the data type for the quantity of dollars and start using a precise type (eg long) as the data type for the quantity of cents.
The steps then to do the calculation would be to immediately convert everything, with rounding, to cents:
double amountOwed = ...;
int noOfMembers = ...;
long centsOwed = Math.round(amountOwed * 100);
long portionCents = Math.round(amountOwed * 100 / noOfMembers);
long errorCents = portionCents * noOfMembers - centsOwed;
Here’s one way to deal with the error:
long lastPortionCents = portionCents - errorCents;
But it’s possible that the error is more than 1 cent, so a better solution would be to spread the error out evenly by subtracting (or adding if the error is negative) 1 cent from the first (or last, or randomly chosen) errorCents diners.
The rest of the solution is then about rending the above, which I leave to the reader.
As a side note, using cents is how banks transmit amounts (at least for EFTPOS anyway).
Regarding basic software design, I would create a separate method that accepts integer cents and people count as its parameters and returns an array of the "split" amounts. Doing this will not only make your code easier to read, but it will compartmentaise the arithmetic operation and thus enable lots of simple tests to more easily be written, so you can know you have all the edge cases that you can think of covered.
You can use BigDecimal with half rounding mode:
private String calculateAmountToPay(String noOfParticipants, String owed) {
double amountOwed = Double.parseDouble(owed);
int noOfMembers = Integer.parseInt(noOfParticipants);
BigDecimal amount= new BigDecimal((amountOwed / (double) noOfMembers) / 100);
return amount.setScale(2, RoundingMode.HALF_UP).toString();
}
You can just do all the computation with basic primitives converting everything to cents (2 decimals precision), and dividing the left over cents over a portion of the members, no need to overcomplicate it with extra sdks/math manipulations. The following is a working example solving this problem entirely, using these suggestions:
public class AmountDivider {
private int totalMembers, luckies, unluckies;
private double totalAmount, amountPerLucky, amountPerUnlucky;
public AmountDivider(int numMembers, double amountOwed) {
totalMembers = numMembers;
totalAmount = amountOwed;
double centsOwed = amountOwed * 100;
int centsPerMember = (int)(centsOwed / totalMembers);
int centsLeft = (int)centsOwed - centsPerMember * totalMembers;
luckies = totalMembers - centsLeft;
amountPerLucky = centsPerMember / 100.0;
unluckies = centsLeft;
amountPerUnlucky = (centsPerMember + 1) / 100.0;
}
public String toString() {
String luckiesStr = String.format("%d lucky persons will pay %.2f", luckies, amountPerLucky);
String unluckiesStr = String.format("%d unlucky persons will pay %.2f", unluckies, amountPerUnlucky);
return String.format("For amount %f divided among %d: \n%s\n%s\n",
totalAmount, totalMembers, luckiesStr, unluckiesStr);
}
public static void main(String[] args) {
System.out.println(new AmountDivider(3, 200));
System.out.println(new AmountDivider(17, 365.99));
}
}
Complete code on GitHub
Hope this helps.
String mins = minsField.getText();
int Mins;
try
{
Mins = Integer.parseInt(mins);
}
catch (NumberFormatException e)
{
Mins = 0;
}
double hours = Mins / 60;
hours.setText(hoursminsfield);
The problem is that Double cannot be dereferenced.
EDIT 4/23/12
double cannot be dereferenced is the error some Java compilers give when you try to call a method on a primitive. It seems to me double has no such method would be more helpful, but what do I know.
From your code, it seems you think you can copy a text representation of hours into hoursminfield by doing
hours.setText(hoursminfield);
This has a few errors:
1) hours is a double which is a primitive type, there are NO methods you can call on it. This is what gives you the error you asked about.
2) you don't say what type hoursminfield is, maybe you haven't even declared it yet.
3) it is unusual to set the value of a variable by having it be the argument to a method. It happens sometimes, but not usually.
The lines of code that do what you seem to want are:
String hoursrminfield; // you better declare any variable you are using
// wrap hours in a Double, then use toString() on that Double
hoursminfield = Double.valueOf(hours).toString();
// or else a different way to wrap in a double using the Double constructor:
(new Double(hours)).toString();
// or else use the very helpful valueOf() method from the class String which will
// create a string version of any primitive type:
hoursminfield = String.valueOf(hours);
ORIGINAL ANSWER (addressed a different problem in your code):
In double hours = Mins / 60; you are dividing two ints. You will get the int value of that division, so if
Mins = 43;
double hours = Mins / 60;
// Mins / 60 is an int = 0. assigning it to double hours makes
// hours a double equal to zero.
What you need to do is:
double hours = Mins / ((double) 60);
or something like that, you need to cast some part of your division to a double in order to force the division to be done with doubles and not ints.
You haven't specified the language but, if it's Java, there's a big difference between the basic type double and the class Double.
In any case, your setText seems the wrong way around. The setText method would belong to the data field, not the data you're trying to put in there:
hoursminsfield.setText (hours);
In other words, you want to set the text of the field, using the double you just calculated. Whether you can pass a double is a different matter which may need to be examined.
Another thing:
double hours = Mins / 60;
will, if Mins is an integer`, give you an integer value which you then put into a double. That means it will be truncated. If you want to ensure you keep precision following the division, you can use something like:
double hours = (double) Mins / 60.0;
(though it may work with only one of those changes, I prefer to make all terms explicit).
How about this way
double hours = Mins / 60.0
I always use the above statement to get the double value
I would like to round a double in java. I would like it rounded like this:
24.04 should give 24.05
24.07 should give 24.05
24.02 = 24.00
24.08 = 24.10
I try to use round like this:
amount = amount *10;
amount = Math.round(amount)/10;
But, like you can see, it's not work.
Thank you for the answers!
Math.round(amount * 20) / 20f is one way.
Please not that rounding using float can generate errors due to the internal binary representation of float (same for double).
So an operation like Math.round(amount * 20) / 20f for an amount 23.01 can give as result 23.000000003 (is an example I didn't checked it, but is simple to find a problem using few tests).
Use BigDecimal insteads.
I would do it like this:
double[] amounts = new double[]{24.04, 24.07, 24.02, 24.08};
for(double d : amounts){
double d2 = Math.round(d * 20) / 20.0;
System.out.println(String.format("%.2f -> %.2f",d,d2));
}
Result is this:
24,04 -> 24,05
24,07 -> 24,05
24,02 -> 24,00
24,08 -> 24,10
[EDIT]
As other guys suggested there might be some precision error. It most likely will never be visible if you print it as suggested in my example. But it depends what do you need the number for....
The formula that I want to convert in java is
Over limit Cash Deposit Fee = [(Input $ cash deposit) – ($ Cash deposits included in plan)] / 1000 * (price/each extra $1000 deposited)
The code that I am writing is
int inputCash = 50;
int cashDepsitFromPlan = 40;
int cashDepositOverLimitFee = 2.5;
cashDepositOverLimit = (double) ((inputCash -cashDepsitFromPlan) / 1000) * ???;
how do I find ???(price/each extra $1000 deposited)
If you're working with floating point numbers, you may want to reconsider the use of the int data type.
This, for a start, is going to cause all sorts of grief:
int cashDepositOverLimitFee = 2.5;
You're better off using double for everything.
In terms of finding the unknown variable here, that's something specific to your business rules, which aren't shown here.
I'd hazard a guess that the price/$1000 figure is intimately related to your cashDepositOverLimitFee variable such as it being $2.50 for every extra $1000.
That would make the equation:
inputCash - cashDepsitFromPlan
cashDepositOverLimit = ------------------------------ * cashDepositOverLimitFee
1000
which makes sense. The first term on the right hand side is the number of excess $1000 lots you've deposited over and above the plan. You would multiply that by a fee rate (like $2.50 or 2.5%) to get the actual fee.
However, as stated, we can't tell whether it's $2.50 or 2.5% based on what we've seen. You'll have to go back to the business to be certain.
You have to algebraically manipulate the equation to solve for that.
cashDepositOverLimitFee = (double) ((inputCash -cashDepsitFromPlan) / 1000) * ???
cashDepositOverLimitFee*1000 = (double) (inputCash -cashDepsitFromPlan) * ???
(cashDepositOverLimitFee*1000) / (double) (inputCash -cashDepsitFromPlan) = ???
??? = (cashDepositOverLimitFee*1000) / (double) (inputCash -cashDepsitFromPlan)
Note that the (double) cast must remain in order to ensure a floating point result.
This question already has answers here:
Retain precision with double in Java
(24 answers)
Closed 4 years ago.
Seems like the subtraction is triggering some kind of issue and the resulting value is wrong.
double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;
78.75 = 787.5 * 10.0/100d
double netToCompany = targetPremium.doubleValue() - tempCommission;
708.75 = 787.5 - 78.75
double dCommission = request.getPremium().doubleValue() - netToCompany;
877.8499999999999 = 1586.6 - 708.75
The resulting expected value would be 877.85.
What should be done to ensure the correct calculation?
To control the precision of floating point arithmetic, you should use java.math.BigDecimal. Read The need for BigDecimal by John Zukowski for more information.
Given your example, the last line would be as following using BigDecimal.
import java.math.BigDecimal;
BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
This results in the following output.
877.85 = 1586.6 - 708.75
As the previous answers stated, this is a consequence of doing floating point arithmetic.
As a previous poster suggested, When you are doing numeric calculations, use java.math.BigDecimal.
However, there is a gotcha to using BigDecimal. When you are converting from the double value to a BigDecimal, you have a choice of using a new BigDecimal(double) constructor or the BigDecimal.valueOf(double) static factory method. Use the static factory method.
The double constructor converts the entire precision of the double to a BigDecimal while the static factory effectively converts it to a String, then converts that to a BigDecimal.
This becomes relevant when you are running into those subtle rounding errors. A number might display as .585, but internally its value is '0.58499999999999996447286321199499070644378662109375'. If you used the BigDecimal constructor, you would get the number that is NOT equal to 0.585, while the static method would give you a value equal to 0.585.
double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));
on my system gives
0.58499999999999996447286321199499070644378662109375
0.585
Another example:
double d = 0;
for (int i = 1; i <= 10; i++) {
d += 0.1;
}
System.out.println(d); // prints 0.9999999999999999 not 1.0
Use BigDecimal instead.
EDIT:
Also, just to point out this isn't a 'Java' rounding issue. Other languages exhibit
similar (though not necessarily consistent) behaviour. Java at least guarantees consistent behaviour in this regard.
I would modify the example above as follows:
import java.math.BigDecimal;
BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
This way you avoid the pitfalls of using string to begin with.
Another alternative:
import java.math.BigDecimal;
BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
I think these options are better than using doubles. In webapps numbers start out as strings anyways.
Any time you do calculations with doubles, this can happen. This code would give you 877.85:
double answer = Math.round(dCommission * 100000) / 100000.0;
Save the number of cents rather than dollars, and just do the format to dollars when you output it. That way you can use an integer which doesn't suffer from the precision issues.
See responses to this question. Essentially what you are seeing is a natural consequence of using floating point arithmetic.
You could pick some arbitrary precision (significant digits of your inputs?) and round your result to it, if you feel comfortable doing that.
This is a fun issue.
The idea behind Timons reply is you specify an epsilon which represents the smallest precision a legal double can be. If you know in your application that you will never need precision below 0.00000001 then what he suggests is sufficient to get a more precise result very close to the truth. Useful in applications where they know up front their maximum precision (for in instance finance for currency precisions, etc)
However the fundamental problem with trying to round it off is that when you divide by a factor to rescale it you actually introduce another possibility for precision problems. Any manipulation of doubles can introduce imprecision problems with varying frequency. Especially if you're trying to round at a very significant digit (so your operands are < 0) for instance if you run the following with Timons code:
System.out.println(round((1515476.0) * 0.00001) / 0.00001);
Will result in 1499999.9999999998 where the goal here is to round at the units of 500000 (i.e we want 1500000)
In fact the only way to be completely sure you've eliminated the imprecision is to go through a BigDecimal to scale off. e.g.
System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());
Using a mix of the epsilon strategy and the BigDecimal strategy will give you fine control over your precision. The idea being the epsilon gets you very close and then the BigDecimal will eliminate any imprecision caused by rescaling afterwards. Though using BigDecimal will reduce the expected performance of your application.
It has been pointed out to me that the final step of using BigDecimal to rescale it isn't always necessary for some uses cases when you can determine that there's no input value that the final division can reintroduce an error. Currently I don't know how to properly determine this so if anyone knows how then I'd be delighted to hear about it.
So far the most elegant and most efficient way to do that in Java:
double newNum = Math.floor(num * 100 + 0.5) / 100;
Better yet use JScience as BigDecimal is fairly limited (e.g., no sqrt function)
double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999
Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;
Although you should not use doubles for precise calculations the following trick helped me if you are rounding the results anyway.
public static int round(Double i) {
return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}
Example:
Double foo = 0.0;
for (int i = 1; i <= 150; i++) {
foo += 0.00010;
}
System.out.println(foo);
System.out.println(Math.round(foo * 100.0) / 100.0);
System.out.println(round(foo*100.0) / 100.0);
Which prints:
0.014999999999999965
0.01
0.02
More info: http://en.wikipedia.org/wiki/Double_precision
It's quite simple.
Use the %.2f operator for output. Problem solved!
For example:
int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);
The above code results in a print output of:
877.85
The %.2f operator defines that only TWO decimal places should be used.