double displaying very long number - java

I have a simple calculation that uses doubles but I'm getting an unexpected result and I can't understand why?
import java.util.Scanner;
public class VersatileSnitSoft {
/**
* #param args
*/
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
double amount;
System.out.print("What’s the price of a CD-ROM? ");
amount = myScanner.nextDouble();
amount = amount + 25.00;
System.out.print("We will bill $");
System.out.print(amount);
System.out.println(" to your credit card.");
}
}
If I enter 2.99 the result I get is..
We will bill $27.990000000000002 to your credit card.

You cannot represent decimal values precisely whilst operating on doubles (or floats).
Use BigDecimal instead.
Edit (2)
Example here:
BigDecimal amount = new BigDecimal("2.99");
amount = amount.add(new BigDecimal("25.00"));
System.out.println(amount.toString());
Ouput:
27.99

Doubles (floating-point values in general) cannot always represent exactly what we think of intuitively as a precise value. This is because of the way in which floats are stored, and can vary from machine to machine and language to language. When you try to store 2.99, the actual value that is stored may be very slightly different (e.g. 2.990000000000002). This question gives a decent, quick overview of why.
You should therefore (as it says through the link) never use floating-point primitives to represent currency. Either use BigDecimal, or keep track of two integers yourself (e.g. int dollars; and int cents;).

use DecimalFormat to display 2 digit after whole number
DecimalFormat f = new DecimalFormat("##.00");
String amt=f.format(amount);
System.out.print("We will bill $");
System.out.print(amt);
http://www.browxy.com/SubmittedCode/21519

I believe this question has been asked a million times. You can find a good and quick description of it here Why not use Double or Float to represent currency?
If you are just fooling around and you want it to print properly use
String prettyNumber = String.format("%.2f", doubleNumber);
Which is going to print 2 floating points. It will look correct, but it is only correct if precision (in the magnitude of 10^-16 or so) is not of interest to you.

Related

java removing trailing decimal digits causing .0 become .99

I want to simply have a function that converts a double with as many decimal places into 4 decimal places without rounding.
I have this code that has been working fine but found a random instance where it turned .0 into .99
Here are some sample outputs
4.12897456 ->4.1289
4.5 ->4.5
4.5231->4.5231
5.53->5.53
5.52->5.199 (Wrong conversion, I want it to be 5.52)
private static double get4Donly(double val){
double converted = ((long)(val * 1e4)) / 1e4;
return converted
}
EDIT: This conversion is called thousands of times, so please suggest a method where I dont have to create a new string all the time.
You can use DecimalFormat
import java.text.DecimalFormat;
import java.math.RoundingMode;
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.DOWN);
for (Number n : Arrays.asList(4.12897456, 4.5, 4.5231, 5.53, 5.52)) {
Double d = n.doubleValue();
System.out.println(df.format(d));
}
}
}
RoundingMode.DOWN rounds towards zero, new DecimalFormat("#.####") creates a DecimalFormat instance that formats numbers to a maximum of 4 decimal places. Put those two together and the above code produces the following output, which I believe matches your expectations:
4.1289
4.5
4.5231
5.53
5.52
Doubles just don't work like you think they do.
They are stored in a binary form, not a decimal form. Just like '1 divided by 3' is not representable in a decimal double (0.3333333333 is not enough, it's infinite 3s, so not representable, so you get a rounding error), but '1 divided by 5' is representable just fine, there are numbers that are representable, and numbers that end up rounded when storing things in a double type, but crucially things that seem perfectly roundable in decimal may not be roundable in binary.
Given that they don't match up, your idea of 'eh, I will multiply by 4, turn it to a long, then convert back to a double, then divide by 1000' is not going to let those digits go through unmolested. This is not how you round things, as you're introducing additional loss in addition to the loss you already started out with due to using doubles.
You have 3 solutions available:
Just print it properly
A double cannot be considered to 'have 4 digits after the decimal separator' because a double isn't decimal.
Therefore, it doesn't even make sense to say: Please round this double to at most 4 fractional digits.
That is the crucial realisation. Once you understand that you'll be well along the way :)
What you CAN do is 'please take this double and print it by using no more than 4 digits after the decimal separator'.
String out = String.format("%.4f", 5.52);
or you can use System.printf(XXX) which is short for System.print(String.format(XXX)).
This is probably what you want
forget doubles entirely
For some domains its better to ditch doubles and switch to longs or ints. For example, if you're doing finances, it's better to store the atomic unit as per that currency in a long, and forego doubles instead. So, for dollars, store cents-in-a-long. For euros, the same. For bitcoin, store satoshis. Write custom rendering to render back in a form that is palatable for that currency:
long value = 450; // $4.50
String formatCurrency(long cents) {
return String.format("%s%s%d.%02d", cents < 0 ? "-" : " ", "$", Math.abs(cents) / 100, Math.abs(cents) % 100);
}
Use BigDecimal
This is generally more trouble than it is worth, but it stores every digit, decimally - it represent everything decimal notation can (and it also cannot represent anything else - 1 divided by 3 is impossible in BigDecimal).
I would recommend using the .substring() method by converting the double to a String. It is much easier to understand and achieve since you do not require the number to be rounded.
Moreover, it is the most simple out of all the other methods, such as using DecimalFormat
In that case, you could do it like so:
private static double get4Donly(double val){
String num = String.valueOf(val);
return Double.parseDouble(num.substring(0, 6));
}
However, if the length of the result is smaller than 6 characters, you can do:
private static double get4Donly(double val){
String num = String.valueOf(val);
if(num.length()>6) {
return Double.parseDouble(num.substring(0, 6));
}else {
return val;
}
}

CURRENCY - Round double value ONLY if it has more than 2 decimal places [duplicate]

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.

Java Set double value with all decimal point and get only 2 digit after decimal point

In java , I have confusion about store and get double value.
Currently i am creating software where some accounting part included,i want to store calculated value same as they are calculated like 202.234234234212312 and while in display i want to display it as 202.23 in 2 digit after decimal point.
and in calculation i want to do calculation with 2,4 or 6 digit.
for that i have 2 option
Store value as it is calculated and in getter method of amount field, i can format it like.
private Double amount;
public Double getAmount() {
DecimalFormat df2 = new DecimalFormat(".##");
return Double.valueOf(df2.format(amount));
}
but problem with this method is , it will get formatted number at all time i get amount. i can't get actual stored amount.
i can use 2 separate field for get and set.
private Double amount;
private Double amountFormatted;
public Double getAmount() {
return amount;
}
public Double getAmountFormatted() {
DecimalFormat df2 = new DecimalFormat(".##");
return Double.valueOf(df2.format(amountFormatted));
}
so please provide better way to store and get decimal value, Thank you.
First of all, all calculations involving money should be done in BigDecimal, not double. A double cannot represent quantities like 0.1 exactly.
Secondly, your getAmountFormatted should not return a Double but a String if it is only intended for output purposes.
Use the same amount variable for both methods.
If I understood you correctly, you just want to get different format for same value, you can just have one variable and 2 get fields like this:
private Double amount;
public Double getAmount() {
return amount;
}
public Double getAmountFormatted() {
DecimalFormat df2 = new DecimalFormat(".##");
return Double.valueOf(df2.format(amount));
}
It's not an entity's job to provide formatted data for display: leave formatting to the code that is actually doing the displaying. More to the point, it's not your job as the programmer of the entity class to 1) anticipate every future need for a formatted value, and 2) constantly update the entity class to provide new formatted values that you didn't (nay, couldn't) anticipate.
So just have the getAmount method return the raw unadulterated data:
public class Entity {
private BigDecimal amount;
public BigDecimal getAmount(){
return amount;
}
//...
}
and eliminate getAmountFormatted from the entity. When you want to display the amount formatted to two decimal places, just obtain the raw amount with getAmount() and format it right on the spot:
Entity e = ...;
DecimalFormat df = new DecimalFormat(".##");
System.out.println("The amount is " + df.format(e.getAmount());
Here's a little story that illustrates why I think this is the way to go...
Suppose there's a second amount, otherAmount in your entity, that was included at the time the entity was created because someone thought it might be needed in the future. And suppose that, because the field had no planned use at the time, you didn't bother to create a getOtherAmountFormatted() method to provide a two-decimal-formatted version of otherAmount.
Now the future arrives. The otherAmount field is now being used, and you need to start displaying it with two decimal places along with the original amount. You know you're going to have to add the line
System.out.println("The other amount is " + ...
into the code that's displaying the amounts. Only question is, how do you finish that line. Do you add into your entity class a getOtherAmountFormatted() method and then write:
System.out.println("The other amount is " + e.getOtherAmountFormatted()); // NO!!
Or do you not touch the entity class at all and just write:
System.out.println("The other amount is " + df.format(e.getOtherAmount()); // Yes!!
The entity isn't the class that ultimately needs the formatted version of otherAmount, so it shouldn't have to change just to accommodate some entirely other bit of code that now happens to need a formatted version of otherAmount. Especially when that other bit of code can just get the unformatted amount, which the unmodified entity can already provide, and then do the needed formatting for itself.
Next scenario: in the further future, yet another requirement has come up to print those amount fields, but now with with three decimal places. Are you going to add yet more methods into the entity class to provide three-decimal versions of the amounts (in addition to the ones already providing two-decimal versions)? Or will you not touch the entity class at all and just write, at the point you need those three-decimal formatted values:
DecimalFormat df3 = new DecimalFormat(".###");
// ...
System.out.println("The amount in 3 decimals is " + df3.format(e.getAmount());
System.out.println("The other amount in 3 decimals is " + df3.format(e.getOtherAmount());

Formatting Decimal Number

I am formatting a decimal number and I have the following criteria to format it:
Number should be at most two decimal places (10.1234=>10.12)
If there is only one digit after decimal then it will ends up with an extra 0 (10.5=>10.50)
Thousand separator will be comma (12345.2345 => 12,345.23)
I have written following logic:
double x = Double.parseDouble(value.toString());
String dec = x % 1 == 0 ? new java.text.DecimalFormat("###,###.##").format(x) : new java.text.DecimalFormat("###,###.00").format(x);
Now it is printing:
11111111111110.567=>11,111,111,111,110.57
111111111111110.567=>111,111,111,111,110.56
1111111111111110.567=>1,111,111,111,111,110.60
11111111111111110.567=>11,111,111,111,111,110
111111111111111110.567=>111,111,111,111,111,104
1111111111111111110.567=>1,111,111,111,111,111,170
I don't understand why the behavior changes. How should I print 1111111111111111110.567 as 1,111,111,111,111,111,110.57?
The problem is that you can't represent 1111111111111111110.567 exactly as a double in the first place. (You can't even represent your shortest value exactly, but the inaccuracy will increase significantly as you increase the magnitude.)
A double only has about 17 significant digits of useful data anyway - you're trying to get 22 digits.
Use BigDecimal if you want more precision - but be aware that this will change other things too. What kind of value are you trying to represent, anyway? Natural values (weights, distances etc) are appropriate for double; artificial values (particularly currency values) are appropriate for BigDecimal.
I managed to get this (You have to use BigDecimal):
import java.math.BigDecimal;
import java.text.NumberFormat;
public class Sandbox {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("1111111111111111110.567");
DecimalFormat formatter = new DecimalFormat("###,###.00");
System.out.println(formatter.format(x));
}
}
OUTPUT:
1,111,111,111,111,111,110.57
Resource Links: DecimalFormat and BigDecimal
One more thing, you have to enter the BigDecimal number as a String or else it will cause problems.
BigDecimal x = new BigDecimal(1111111111111111110.567) will output the following.
1,111,111,111,111,111,168.00

How to round an answer to a mathematical equation

All-
I have an app in which the users inputs data such as the cost of a dinner bill and the tip percentage and the number of people. The app than takes the numbers and outputs the total bill cost and the amount each person has to pay. I am almost there but when the user inputs numbers that don't work well I get outputs like $23.576 or $34.999999999. My question is how do I make the app round the two output answers to two decimal places ($55.349 goes to $55.35)?
Thanks in advance!
String roundTwoDecimals(double d) {
DecimalFormat formatter = new DecimalFormat("#.##");
return formatter.format(d);
}
You can use Math.round like so:
double data = 55.349; // Your data value of whatever
int decimalPlaces = 2;
double roundBase = Math.pow(10, decimalPlaces);
data = (double)(Math.round(data * roundBase)) / roundBase;
System.out.println(data); // Prints "55.35"
Keep in mind, however: you should NOT use double when it comes to financial applications. Since yours appears to be small-scale, you should be fine, however, BigDecimal is much easier to use for purposes like these.
How to use BigDecimal: clicky.

Categories

Resources