I'm trying to format a double to just 2 decimal places in Java.
I've read the following post:
How to round a number to n decimal places in Java
For some reason, every one of those methods fails to work on certain numbers I have..
For example:
DecimalFormat df = new DecimalFormat("#.##");
normalizedValue = Double.valueOf(df.format(normalizedValue));
If I print normalizedValue I get result similar to the following:
-78.64000000000001
18.97
59.469999999999985
-63.120000000000005
(Note: some are formatted correctly... some aren't)
So, these methods seem to round, but I need something that will remove all decimals after 2 decimal places... Any suggestions?
Thank you.
The string representation given by DecimalFormat.format(...) does indeed have only 2 digits in your example. But as soon as you reconvert it into a double those accuracy issues occur. A binary base format like double will always show such effects when you try to represent "exakt" decimal fractions. You may change to BigDecimal to get rid of such effects.
Related
Problem
I used BigDecimal.setScale(7, RoundingMode.HALF_UP) to round the number to 7 decimal places, however now if I get a number without any decimal places or with them being fewer then 7 I get useless 0's in the number, for example after rounding 40 that way I'll get 40.0000000.
Question
Is it possible to round numbers a certain number of decimal places using SetScale and get rid of pointless 0's at the same time?
Providing you have already performed your rounding, you can simply use DecimalFormat("0.#").
final double value = 5.1000;
DecimalFormat format = new DecimalFormat("0.#");
System.out.println(format.format(value));
The result here will be 5.1 without the trailing zeroes.
So the other way to work this problem out is using BigDecimals methods such as .stripTrailingZeros().toPlainString()
It is quite lesser code to write, than the other solution, but as said iт the solution could be less comfortable to change level of precision in the future, if you'll need to
We are solving a numeric precision related bug. Our system collects some numbers and spits their sum.
The issue is that the system does not retain the numeric precision, e.g. 300.7 + 400.9 = 701.599..., while expected result would be 701.6. The precision is supposed to adapt to the input values so we cannot just round results to fixed precision.
The problem is obvious, we use double for the values and addition accumulates the error from the binary representation of the decimal value.
The path of the data is following:
XML file, type xsd:decimal
Parse into a java primitive double. Its 15 decimal places should be enough, we expect values no longer than 10 digits total, 5 fraction digits.
Store into DB MySql 5.5, type double
Load via Hibernate into a JPA entity, i.e. still primitive double
Sum bunch of these values
Print the sum into another XML file
Now, I assume the optimal solution would be converting everything to a decimal format. Unsurprisingly, there is a pressure to go with the cheapest solution. It turns out that converting doubles to BigDecimal just before adding a couple of numbers works in case B in following example:
import java.math.BigDecimal;
public class Arithmetic {
public static void main(String[] args) {
double a = 0.3;
double b = -0.2;
// A
System.out.println(a + b);//0.09999999999999998
// B
System.out.println(BigDecimal.valueOf(a).add(BigDecimal.valueOf(b)));//0.1
// C
System.out.println(new BigDecimal(a).add(new BigDecimal(b)));//0.099999999999999977795539507496869191527366638183593750
}
}
More about this:
Why do we need to convert the double into a string, before we can convert it into a BigDecimal?
Unpredictability of the BigDecimal(double) constructor
I am worried that such a workaround would be a ticking bomb.
First, I am not so sure that this arithmetic is bullet proof for all cases.
Second, there is still some risk that someone in the future might implement some changes and change B to C, because this pitfall is far from obvious and even a unit test may fail to reveal the bug.
I would be willing to live with the second point but the question is: Would this workaround provide correct results? Could there be a case where somehow
Double.valueOf("12345.12345").toString().equals("12345.12345")
is false? Given that Double.toString, according to javadoc, prints just the digits needed to uniquely represent underlying double value, so when parsed again, it gives the same double value? Isn't that sufficient for this use case where I only need to add the numbers and print the sum with this magical Double.toString(Double d) method? To be clear, I do prefer what I consider the clean solution, using BigDecimal everywhere, but I am kind of short of arguments to sell it, by which I mean ideally an example where conversion to BigDecimal before addition fails to do the job described above.
If you can't avoid parsing into primitive double or store as double, you should convert to BigDecimal as early as possible.
double can't exactly represent decimal fractions. The value in double x = 7.3; will never be exactly 7.3, but something very very close to it, with a difference visible from the 16th digit or so on to the right (giving 50 decimal places or so). Don't be mislead by the fact that printing might give exactly "7.3", as printing already does some kind of rounding and doesn't show the number exactly.
If you do lots of computations with double numbers, the tiny differences will eventually sum up until they exceed your tolerance. So using doubles in computations where decimal fractions are needed, is indeed a ticking bomb.
[...] we expect values no longer than 10 digits total, 5 fraction digits.
I read that assertion to mean that all numbers you deal with, are to be exact multiples of 0.00001, without any further digits. You can convert doubles to such BigDecimals with
new BigDecimal.valueOf(Math.round(doubleVal * 100000), 5)
This will give you an exact representation of a number with 5 decimal fraction digits, the 5-fraction-digits one that's closest to the input doubleVal. This way you correct for the tiny differences between the doubleVal and the decimal number that you originally meant.
If you'd simply use BigDecimal.valueOf(double val), you'd go through the string representation of the double you're using, which can't guarantee that it's what you want. It depends on a rounding process inside the Double class which tries to represent the double-approximation of 7.3 (being maybe 7.30000000000000123456789123456789125) with the most plausible number of decimal digits. It happens to result in "7.3" (and, kudos to the developers, quite often matches the "expected" string) and not "7.300000000000001" or "7.3000000000000012" which both seem equally plausible to me.
That's why I recommend not to rely on that rounding, but to do the rounding yourself by decimal shifting 5 places, then rounding to the nearest long, and constructing a BigDecimal scaled back by 5 decimal places. This guarantees that you get an exact value with (at most) 5 fractional decimal places.
Then do your computations with the BigDecimals (using the appropriate MathContext for rounding, if necessary).
When you finally have to store the number as a double, use BigDecimal.doubleValue(). The resulting double will be close enough to the decimal that the above-mentioned conversion will surely give you the same BigDecimal that you had before (unless you have really huge numbers like 10 digits before the decimal point - the you're lost with double anyway).
P.S. Be sure to use BigDecimal only if decimal fractions are relevant to you - there were times when the British Shilling currency consisted of twelve Pence. Representing fractional Pounds as BigDecimal would give a disaster much worse than using doubles.
It depends on the Database you are using. If you are using SQL Server you can use data type as numeric(12, 8) where 12 represent numeric value and 8 represents precision. similarly, for my SQL DECIMAL(5,2) you can use.
You won't lose any precision value if you use the above-mentioned datatype.
Java Hibernate Class :
You can define
private double latitude;
Database:
Suppose I have money value represented as 10.000,00 in congo currency and I want it to be converted to a decimal amount like 10000.00. The money representation can be any from deferent countries and the outcome should be a decimal amount.
How do I achieve this in java?
It is better to keep money in integer format in minimal possible coin value - f.e. in US dollars - if you have 1$ and 25 cents - just save it as int 125 cents - it helps to avoid very common problems with java float/doubles precision errors.
You can use replace for example :
money.replace(",", "#").replace(".", "").replace("#", ".")
This will gives you:
10000.00
If you want to get the decimal value then you can use :
String money = "10.000,00";
NumberFormat format = NumberFormat.getInstance(Locale.FRANCE);
Number number = format.parse(money.replace(".", ""));
System.out.println(number);
double d = number.doubleValue();
System.out.println(d);
This will gives you:
10000.0
The method NumberFormat.getCurrencyInstance(Locale)
is made exactly for this purpose.
It takes care for all country-specific formatting (decimal point or comma, currency name, currency before or after number, thousands grouping, ...).
You use it like this, for example for Congo (Democratic Republic):
Locale locale = new Locale("", "CD");
NumberFormat currencyFormat= NumberFormat.getCurrencyInstance(locale);
String s = currencyFormat.format(10000.0);
Result is "CDF 10,000.00"
Note: For the following, I assume it will not necessarily be known beforehand which currency/locale to expect.
I did that just recently like this: Split the string by all possible markers (.,' should be enough). Then join the parts except for the last one. Before you add that, you add whatever decimal-sign you need. So, "10.000,00" => [[10],[000],[00]] => 10000.00. This can then be converted to BigDecimal.
(Don't use float or double for monetary values.)
The reason for doing it that way: You can handle most formats like
1.234,56
1,234.56
1'234.56
1234.567
And you automatically can handle numbers of fraction digits != 2.
You'd only have to be careful with whitespaces and currencies with no fractions (split array will be of size = 1). Also be careful with currencies with 3 fraction digits. It can be cumbersome to tell amounts apart using this approach that are not using a decimal at all and those that do ("1.234" =>"1234.000" and "1.234,567" => "1234.567"). You might need a little additional validation & correction for those cases.
I don't know if that's interesting for you, but Java 8 has a Currency class, that can give you number of fraction digits.
I also suggest writing a unit test, so you can be sure all expected input formats will end up in your desired format. And of course I suggest extensive documentation of what formats are accepted / allowed.
I'm having trouble with (what I suspect is) a rounding error.
I have a string, 0.686357E-01, which I'm trying to convert to a double. I've been able to split it up using the Pattern.split() function, and I'm capturing the base and the exponent values just fine. However, once I try to multiply them appropriately, I get this as a result: 0.06863570000000001.
Here's my relevant code:
pattern = Pattern.compile("E\\+?");
String[] number = pattern.split(string);
double base = Double.parseDouble(number[0]);
int exponent = Integer.parseInt(number[1]);
number= base*Math.pow(10, exponent);
So, how do I avoid the rounding error? (There are ways that I can work around it, but if it's possible to do, then I'd like to know how to fix the issue)
Thanks.
You don't need to split it, Double.parseDouble can handle those kinds of numbers just fine.
double number = Double.parseDouble("0.686357E-01");
See? It works!
0.0686357 is not exactly representable as a double-precision value.
Two solutions:
Use e.g. BigDecimal.
Limit the displayed precision to a certain number of significant figures when converting back to human-readable.
Floating point numbers do not have perfect precision. If that is an issue, use BigDecimal:
String string = "0.686357E-01";
BigDecimal number = new BigDecimal(string);
System.out.println(number);
Double will print always like that, but the value will remain correct. You'll need to format the output to get the correct value. See DecimalFormat class.
float a= (float) 1846.4;
NumberFormat df = DecimalFormat.getInstance();
df.setMinimumFractionDigits(2);
df.setMaximumFractionDigits(2);
float b = Float.parseFloat( df.format(a));
What would be the best way to set decimal places for a float (I just want to set 2 decimal places of a float value which can be 2013.43452 or 2392.2) ? Now I am getting exception like Exception in thread "main" java.lang.NumberFormatException: For input string: "1,846.40".
I think the direct problem causing the NFE is the thousand separators in the string, not the number of decimal places. You have both a ',' and a '.' in the string, and parseFloat doesn't like that.
Note, however, that you can't fix the number of decimal places in a float value - it is entirely a formatting issue. Specifically, trailing 0's have no significance in floating point values, thus will not be shown by default. You need to format the value according to your wishes at the point of output (when converting it into a displayable String e.g. by using DecimalFormat as you did above).
To get your formatting + parsing to work, just use df again:
float b = df.parse( df.format(a));
However, I still don't see any sense in that. You can't define the number of decimal places in a floating point number. 1846.40 is the same as 1846.4 or 1846.400, the trailing zeros are totally irrelevant.
Those digits come into play when you have output, e.g. writing the number to a string. But there are other facilities to do that, e.g. a string format pattern.
If you need to restrict the number of fraction digits internally use BigDecimal where you can set the scale and thus define the number of fraction digits it represents.
Sounds like you simply want to round to two decimal places.
Number format and mucking around with strings isn't where you want to go.
Try this:
float b = Math.round(a * 100)/100.0f;
What would be the best way to set decimal places for a float
There isn't one. Floats don't have decimal places. They have binary places.
If you want decimal places you have to use a decimal radix, either a BigDecimal or the result of DecimalFormat.format().