String to BigDecimal, which one is good approch - java

I am trying to convert string to BigDecimal. Please tell which one is below is good approch
BigDecimal selAmount = BigDecimal.ZERO;
String amount = "1234";
selAmount = BigDecimal.valueOf(Double.parseDouble(amount));
or
selAmount = new BigDecimal(amount);

Don't use the first approach. If the string represents a value which can't be exactly represented by a double, you'll get accuracy issues.
You can't use the second approach either, since there is no overload of BigDecimal.valueOf which accepts a String.
So, option 3:
BigDecimal selAmount = new BigDecimal(amount);

You can pass string directly to the constructor of BigDecimal.

The second approach is better to proceed with, simply pass the string to the constructor of BigDecimal. The first approach may have precision issues.
From JavaDocs has
public BigDecimal(String val)
Translates the string representation of a BigDecimal into a
BigDecimal. The string representation consists of an optional sign,
'+' ( '\u002B') or '-' ('\u002D'), followed by a sequence of zero or
more decimal digits ("the integer"), optionally followed by a
fraction, optionally followed by an exponent.

BigDecimal.valueOf(double)); calls return new BigDecimal(Double.toString(val)); implicitly. So, your second case would be more efficient, and as Thilo says, more correct.
Code :
*/
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}

Passing string as a constructor is better way. You will lose precision in the first case.
e.g
String s = "123";
BigDecimal bigDecimal = new BigDecimal(s);

Related

Strange behavior by RoundingMode.DOWN [duplicate]

I have a BigDecimal defined like this:
private static final BigDecimal sd = new BigDecimal(0.7d);
if i print it, i get the value:
0.6999999999999999555910790149937383830547332763671875
which causes some wrong calculations. Does anyone know a way to get the exact value of 0.7 as BigDecimal? Change it to 0.71 would view the right result, but it shouldn't be like that
Use a String literal:
private static final BigDecimal sd = new BigDecimal("0.7");
If you use a double, actually public BigDecimal(double val) is called. The reason you do not get 0.7 is that it cannot be exactly represented by a double. See the linked JavaDoc for more information.
Perhaps if you bothered to read the documentation, i.e. the javadoc of the constructor you're using, you'd already know the answer.
When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.
When you then look at the javadoc of BigDecimal.valueOf(double), you'll find:
Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).
So there is your answer: Use BigDecimal.valueOf(0.7d), not new BigDecimal(0.7d).
You should use the declared value in String literal such as new BigDecimal("0.7");
Here are three ways:
private static final BigDecimal sd = new BigDecimal("0.7");
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL32);
private static final BigDecimal sd = new BigDecimal(0.7d, MathContext.DECIMAL64)
Constructing a BigDecimal from a double is surprisingly complicated. First, it can only be done via the detour of a string. (You can't get the constructor with double and a MathContext right. I've tried a lot. At the latest in cases in which the number of places before the decimal point would need to change due to rounding, it becomes difficult. Hence the warning in the Javadoc that you shouldn’t use it.)
However, even there, it is not enough with a simple String.format(), since String.format() is sensitive to the default Locale and outputs different decimal separators depending on system/VM settings, while the BigDecimal constructor always requires a dot as a decimal separator. So you have to construct your own Formatter with Locale.US. If you have this up and running, you will get a warning of an unclosed resource.
I found this to work:
static BigDecimal toBigDecimal(double value, int decimalPlaces) {
String format = "%." + decimalPlaces + "f";
try (Formatter formatter = new Formatter(Locale.US)) {
String formatted = formatter.format(format, value).toString();
return new BigDecimal(formatted);
}
}

Why am I losing Bigdecimal precision?

I am converting numbers like 5.326.236,56 (money), from a txt and first removing dots and commas, but im losing the decimals, and I already defined the columns as:
#Column(name = "total", precision = 16, scale = 2)
private BigDecimal total;
but I am losing the last 2 digits that correspond to Decimal part
Here is my code:
private BigDecimal parseBigLong(String stringNumber) {
String cvalue = "";
for (int n = 0; n < stringNumber.length(); n++) {
char c = stringNumber.charAt(n);
if (!(".").equals(String.valueOf(c))) {
if (!(",").equals(String.valueOf(c))) {
if (!("-").equals(String.valueOf(c))) {
cvalue = cvalue + c;
}
}
}
}
BigDecimal bigDecimal = ( BigDecimal.valueOf(Long.parseLong(cvalue) / 100));
return bigDecimal;
}
Basically, you are doing an integer division on the long before constructing the BigDecimal.
Naturally, the integer division is producing another long ... which cannot represent those two digits after the decimal point.
You can avoid this by doing the division using BigDecimal:
BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(cvalue))
.divide(new BigDecimal(100));
Or if you don't need to enforce the constraint that cvalue is a valid integer (long) representation:
BigDecimal bigDecimal = (new BigDecimal(cvalue))
.divide(new BigDecimal(100));
There may be a better way. The DecimalFormat class understands all sorts of (localized) number formats. If you create a suitable format and then call setParseBigDecimal(true) the format's parse method will produce a BigDecimal ... directly ... without any string bashing to get rid of comma and period characters. (And you won't need to assume that the input number has exactly two digits after the decimal.)
First, your conversion logic is strange:
You are ripping off all -, , and . from your String, and assume it to be 2 decimals when constructing the BigDecimal.
Which means, if you are given a string 1234.5678, you are going to build 123456.78 as the result.
Depending on what's your intention, here are the answers:
If you want to convert to BigDecimal based on the value in input string
Which means, if you want String "1,234.5678" to become 1234.5678 in BigDecimal, you could make use of DecimalFormat, as described in this question: https://stackoverflow.com/a/18231943/395202
If the strange logic is what you intended to do
Which means, if you want String "1,234.5678" to become 123456.78 in BigDecimal, the specific problem in your code is you are doing a long division, and use the result to construct BigDecimal.
In Java (and many other language), division of integer with integer is going to give you integer as result, so 123456 / 100 is going to give you 1234.
What you want to achieve could be done by
BigDecimal result = BigDecimal.valueOf(longValue).divide(BigDecimal.valueOf(100));
Going back to your code, there are a lot of other problems:
Your string concatenation logic is highly inefficient. You could use StringBuilder (or other way I am suggesting soon)
You do not need to convert a char to a String to do comparison. So you
if (!(".").equals(String.valueOf(c))) {
should be written
if (c != '.') {
You could simply use regex to cleanse your input string:
String cvalue = stringNumber.replaceAll("[.,-]", "");

Bigdecimal Not giving exact output in Java

Im adding three big decimals here, but it should give me accurate answer. I'm having two strings here and then converting to big decimal. Please dont ask why Im using strings. There is some business where I will get these values as string then I need to convert. Please find the code
BigDecimal a= new BigDecimal(100.05); --> This value I receive from web service. Its a decimal value from the service.
String b= "100.05";
String c= "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
Output it gives
400.1299999999999971578290569595992565155029296875
Where as it should be 400.13
The problem is your use of new BigDecimal(100.05). The value of a is then 100.0499999999999971578290569595992565155029296875.
If you had specified that value as a string instead, all would be well:
BigDecimal a = new BigDecimal("100.05");
String b = "100.05";
String c = "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
// Output: 400.13
If you only have the input as a double, you can use BigDecimal.valueOf(double) instead of calling the constructor:
BigDecimal a = BigDecimal.valueOf(100.05); // a is now exactly 100.05
Compare the BigDecimal(double) documentation:
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. (...)
With that of BigDecimal.valueOf(Double):
Translates a double into a BigDecimal, using the double's canonical string representation provided by the Double.toString(double) method.
Note: This is generally the preferred way to convert a double (or float) into a BigDecimal, as the value returned is equal to that resulting from constructing a BigDecimal from the result of using Double.toString(double).
new BigDecimal(100.05)
This gives 100.0499999999999971578290569595992565155029296875, because 100.05 cannot be represented exactly as a double.
You have to use string here as well:
new BigDecimal("100.05")
As you get this value from a web-service, you probably convert it from a String to a float/double. If this is the case, just skip that conversion step.
If your web-service stub maps the return value to float/double, you can consider mapping it to a String directly and then feed it to BigDecimal constructor, like this:
double v = 100.05; // Value from web service
BigDecimal a= new BigDecimal(String.valueOf(v));
String b= "100.05";
String c= "200.03";
System.out.println(a.add(new BigDecimal(b).add(new BigDecimal(c))));
Live Example
That works because the string will only contain as many digits as are needed to differentiate the almost-100.05 value from the next value on either side that can be represented, and so we get the string "100.05", which then BigDecimal can process correctly.
You can format the answer to Decimal places using String.format and specifiying how many digits.
System.out.println(String.format("%.2f", a.add(new BigDecimal(b).add(new BigDecimal(c)))));

BigDecimal to string

I have a BigDecimal object and i want to convert it to string.
The problem is that my value got fraction and i get a huge number (in length) and i only need the original number in string for example:
for
BigDecimal bd = new BigDecimal(10.0001)
System.out.println(bd.toString());
System.out.println(bd.toPlainString());
the output is:
10.000099999999999766941982670687139034271240234375
10.000099999999999766941982670687139034271240234375
and i need the out put to be exactly the number 10.0001 in string
To get exactly 10.0001 you need to use the String constructor or valueOf (which constructs a BigDecimal based on the canonical representation of the double):
BigDecimal bd = new BigDecimal("10.0001");
System.out.println(bd.toString()); // prints 10.0001
//or alternatively
BigDecimal bd = BigDecimal.valueOf(10.0001);
System.out.println(bd.toString()); // prints 10.0001
The problem with new BigDecimal(10.0001) is that the argument is a double and it happens that doubles can't represent 10.0001 exactly. So 10.0001 is "transformed" to the closest possible double, which is 10.000099999999999766941982670687139034271240234375 and that's what your BigDecimal shows.
For that reason, it rarely makes sense to use the double constructor.
You can read more about it here, Moving decimal places over in a double
Your BigDecimal doesn't contain the number 10.0001, because you initialized it with a double, and the double didn't quite contain the number you thought it did. (This is the whole point of BigDecimal.)
If you use the string-based constructor instead:
BigDecimal bd = new BigDecimal("10.0001");
...then it will actually contain the number you expect.
For better support different locales use this way:
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(0);
df.setGroupingUsed(false);
df.format(bigDecimal);
also you can customize it:
DecimalFormat df = new DecimalFormat("###,###,###");
df.format(bigDecimal);
By using below method you can convert java.math.BigDecimal to String.
BigDecimal bigDecimal = new BigDecimal("10.0001");
String bigDecimalString = String.valueOf(bigDecimal.doubleValue());
System.out.println("bigDecimal value in String: "+bigDecimalString);
Output:
bigDecimal value in String: 10.0001
// Convert BigDecimal number To String by using below method //
public static String RemoveTrailingZeros(BigDecimal tempDecimal)
{
tempDecimal = tempDecimal.stripTrailingZeros();
String tempString = tempDecimal.toPlainString();
return tempString;
}
// Recall RemoveTrailingZeros
BigDecimal output = new BigDecimal(0);
String str = RemoveTrailingZeros(output);
If you just need to set precision quantity and round the value, the right way to do this is use it's own object for this.
BigDecimal value = new BigDecimal("10.0001");
value = value.setScale(4, RoundingMode.HALF_UP);
System.out.println(value); //the return should be "10.0001"
One of the pillars of Oriented Object Programming (OOP) is "encapsulation", this pillar also says that an object should deal with it's own operations, like in this way:
The BigDecimal can not be a double.
you can use Int number.
if you want to display exactly own number, you can use the String constructor of BigDecimal .
like this:
BigDecimal bd1 = new BigDecimal("10.0001");
now, you can display bd1 as 10.0001
So simple.
GOOD LUCK.
To archive the necessary result with double constructor you need to round the BigDecimal before convert it to String e.g.
new java.math.BigDecimal(10.0001).round(new java.math.MathContext(6, java.math.RoundingMode.HALF_UP)).toString()
will print the "10.0001"

Java Double to String conversion without formatting

I have the number 654987. Its an ID in a database. I want to convert it to a string.
The regular Double.ToString(value) makes it into scientific form, 6.54987E5. Something I dont want.
Other formatting functions Ive found checks the current locale and adds appropriate thousand separators and such. Since its an ID, I cant accept any formatting at all.
How to do it?
[Edit] To clarify: Im working on a special database that treats all numeric columns as doubles. Double is the only (numeric) type I can retrieve from the database.
Use a fixed NumberFormat (specifically a DecimalFormat):
double value = getValue();
String str = new DecimalFormat("#").format(value);
alternatively simply cast to int (or long if the range of values it too big):
String str = String.valueOf((long) value);
But then again: why do you have an integer value (i.e. a "whole" number) in a double variable in the first place?
Use Long:
long id = 654987;
String str = Long.toString(id);
If it's an integer id in the database, use an Integer instead. Then it will format as an integer.
How about String.valueOf((long)value);
What about:
Long.toString(value)
or
new String(value)
Also you can use
double value = getValue();
NumberFormat f = NumberFormat.getInstance();
f.setGroupingUsed(false);
String strVal = f.format(value);
If what you are storing is an ID (i.e. something used only to identify another entity, whose actual numeric value has no significance) then you shouldn't be using Double to store it. Precision will almost certainly screw you.
If your database doesn't allow integer values then you should stored IDs as strings. If necessary make the string the string representation of the integer you want to use. With appropriate use of leading zeros you can make the alphabetic order of the string the same as the numeric order of the ints.
That should get you round the issue.
What about Long.toString((long)value) ?
double d = 56789;
String s = d+"";

Categories