Why am I losing Bigdecimal precision? - java

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("[.,-]", "");

Related

How to print formatted double value to string in java?

I want to create a String from a double value with 10 character for example
Double d = 150.23;
The output string like this 0000015023+
I have used this code but it is not working:
String imponibile = String.format("%10d%n", myDoubleValue);
You want to print 150.23 without the period. Formatting is not supposed to achieve that. You have to:
transform the double number to a int number with the desired rounding and print the int:
int i = (int) Math.round(100.0 * d);
String.format("%010d", i)
Where "010" means print at least 10 digits and pad with zero if there are less. The padding char going before the number of digits.
print the double and remove the period from the string afterwards:
String.format("%011.2f", d).replace(".", "")
Note how you now have to specify 11 including the period. And you have to specify the number of digits after the period
I don't think there is a way to print the sign after a number with String.format. You can easily require to print it at the start which is the normal way to print numbers:
String s = String.format("%+010d", i);
And if you must you can use substring and concatenation to put it at the end:
String imponibile = s.substring(1) + s.charAt(0);
Try f instead of d:
String imponibile = String.format("%010.0f", myDoubleValue*100);
Floating Point - may be applied to Java floating-point types: float,
Float, double, Double, and BigDecimal
Class Formatter

String to BigDecimal, which one is good approch

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);

BigDecimal() omitting leading zero

How I can store the BigDecimal() same as input?
I need to store real numbers using BigDecimal() which may contain following:
02.34
0.12
.12
0
000.000
I used the following approach :
Scanner sc= new Scanner(System.in);
int n=sc.nextInt();
BigDecimal[] amount = new BigDecimal[n];
for(int i=0;i<n;i++) {
amount[i]=sc.nextBigDecimal();
}
But while printing, it prints formatted. Like this:
2.34
0.12
0.12
0.000
0
I want it should be same as inputted. Therefore please let me know that how could I manage to store inputs intact.
the following code should solve your problem, Keep your string as is and convert it into BigDecimal only when you are comparing
Arrays.sort(s, Collections.reverseOrder(new Comparator<String>() {
#Override
public int compare(String a1, String a2) {
BigDecimal a = new BigDecimal(a1);
BigDecimal b = new BigDecimal(a2);
return a.compareTo(b);
}
}));
Since input is originally a string, the only way to store it intact is to store it as a String.
If you later need to do some math on the numbers, convert them then. You could also possibly store them as both String and BigDecimal in an object.
If you want to store input intact, store it as a String. This is exactly the built-in type dedicated to storing character sequences exactly as entered.
BigDecimal, on the other hand, has a numeric representation with no mechanism for storing its decimal input. In fact, you can initialize BigDecimal without providing a decimal representation at all - e.g. through a sequence of arithmetical operations, in which case the initial representation would not be applicable at all.
If you need to do this in many places, make a class for it. Store the original string, along wit BigDecimal that it represents. This way you would be able to do the conversion only once, and keep the original representation along with it:
class OrigBigDecimal {
private String orig;
private BigDecimal val;
public OrigBigDecimal(String s) {
orig = s;
val = new BigDecimal(s);
}
public String toString() { return s; }
public BigDecimal getVal() { return val; }
}
There are a number of ways to approach this:
1- You can create a small elegant object that has 2 variables, a BigDecimal and a string, and its constructor takes the input as a string, store it in a string and parse it and store it in the BigDecimal.
2- You can configure the BigDecimal output format per input:
Format of BigDecimal number
Format a BigDecimal as String with max 2 decimal digits, removing 0 on decimal part

Converting strings to integers and adding them in Java

I have 4 strings:
str1 = 10110011;(length of all string is:32)
str2 = 00110000;
str3 = 01011000;
str4 = 11110000;
In my project I have to add these string and the result should be:
result[1] = str1[1]+str2[1]+str3[1]+str4[1];
result should be obtained as addition of integer numbers.
For the example above, result = 22341011
I know integer to string conversion in Java is very easy but I found string to integer conversion a little harder.
To parse Integers -2^31 < n < 2^31-1 use:
Integer value = Integer.valueOf("10110011");
For numbers that are larger, use the BigInteger class:
BigInteger value1 = new BigInteger("101100111011001110110011101100111011001110110011");
BigInteger value2 = // etc
BigInteger result = value1.add(value2).add(value3); //etc.
The simplest way to do this is with Integer.parseInt(str1). Returns an int containing the value represented by the string.
valueOf() returns an Integer object, rather than an int primitive.
Because your numbers are so big they will not fit in an int. Use the BigInteger class.
I am not known about your project and what actually your problem is. But I came to guess from your partial information that, you have multiple set of strings in bit representation as you explained.
str1 = "1000110.....11";
str1 = "1110110.....01"; etc
adding those decimal values,gives an ambiguous result as an integer can be the sum of multiple integer values. Just see an example below where there are total 5 possibilities[with positive decimal values] to yield 6.
1+5 = 6;
2+4 = 6;
3+3 = 6;
4+2 = 6;
5+1 = 6;
If you proceed in that way you just do an error,nothing else in your case.
One better solution can be,
compute the decimal values of individual strings. Instead of adding(+) them, just concat(join) them to form a single string.
I am suggesting this approach because, This gives always a unique value and later you may need to know individual strings decimal values.
String strVal1 = String.format(computeDecimal(str1));
String strVal2 = String.format(computeDecimal(str2));
String strVal3 = String.format(computeDecimal(str3));
.
.
.
String strValn = String.format(computeDecimal(strn));
String myVal = String.concate(strVal1,strVal1,strVal1,....strValn);
Now you can treat your string as your wish.
//This will give you a non conflicting result.
Better to implement above approach than BigIntegers.
Hope this helps you greatly.

How can I format a String number to have commas and round?

What is the best way to format the following number that is given to me as a String?
String number = "1000500000.574" //assume my value will always be a String
I want this to be a String with the value: 1,000,500,000.57
How can I format it as such?
You might want to look at the DecimalFormat class; it supports different locales (eg: in some countries that would get formatted as 1.000.500.000,57 instead).
You also need to convert that string into a number, this can be done with:
double amount = Double.parseDouble(number);
Code sample:
String number = "1000500000.574";
double amount = Double.parseDouble(number);
DecimalFormat formatter = new DecimalFormat("#,###.00");
System.out.println(formatter.format(amount));
This can also be accomplished using String.format(), which may be easier and/or more flexible if you are formatting multiple numbers in one string.
String number = "1000500000.574";
Double numParsed = Double.parseDouble(number);
System.out.println(String.format("The input number is: %,.2f", numParsed));
// Or
String numString = String.format("%,.2f", numParsed);
For the format string "%,.2f" - "," means separate digit groups with commas, and ".2" means round to two places after the decimal.
For reference on other formatting options, see https://docs.oracle.com/javase/tutorial/java/data/numberformat.html
Given this is the number one Google result for format number commas java, here's an answer that works for people who are working with whole numbers and don't care about decimals.
String.format("%,d", 2000000)
outputs:
2,000,000
Once you've converted your String to a number, you can use
// format the number for the default locale
NumberFormat.getInstance().format(num)
or
// format the number for a particular locale
NumberFormat.getInstance(locale).format(num)
I've created my own formatting utility. Which is extremely fast at processing the formatting along with giving you many features :)
It supports:
Comma Formatting E.g. 1234567 becomes 1,234,567.
Prefixing with "Thousand(K),Million(M),Billion(B),Trillion(T)".
Precision of 0 through 15.
Precision re-sizing (Means if you want 6 digit precision, but only have 3 available digits it forces it to 3).
Prefix lowering (Means if the prefix you choose is too large it lowers it to a more suitable prefix).
The code can be found here. You call it like this:
public static void main(String[])
{
int settings = ValueFormat.COMMAS | ValueFormat.PRECISION(2) | ValueFormat.MILLIONS;
String formatted = ValueFormat.format(1234567, settings);
}
I should also point out this doesn't handle decimal support, but is very useful for integer values. The above example would show "1.23M" as the output. I could probably add decimal support maybe, but didn't see too much use for it since then I might as well merge this into a BigInteger type of class that handles compressed char[] arrays for math computations.
you can also use the below solution
public static String getRoundOffValue(double value){
DecimalFormat df = new DecimalFormat("##,##,##,##,##,##,##0.00");
return df.format(value);
}
public void convert(int s)
{
System.out.println(NumberFormat.getNumberInstance(Locale.US).format(s));
}
public static void main(String args[])
{
LocalEx n=new LocalEx();
n.convert(10000);
}
You can do the entire conversion in one line, using the following code:
String number = "1000500000.574";
String convertedString = new DecimalFormat("#,###.##").format(Double.parseDouble(number));
The last two # signs in the DecimalFormat constructor can also be 0s. Either way works.
Here is the simplest way to get there:
String number = "10987655.876";
double result = Double.parseDouble(number);
System.out.println(String.format("%,.2f",result));
output:
10,987,655.88
The first answer works very well, but for ZERO / 0 it will format as .00
Hence the format #,##0.00 is working well for me.
Always test different numbers such as 0 / 100 / 2334.30 and negative numbers before deploying to production system.
According to chartGPT
Using DecimalFormat:
DecimalFormat df = new DecimalFormat("#,###.00");
String formattedNumber = df.format(yourNumber);
Using NumberFormat:
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setGroupingUsed(true);
String formattedNumber = nf.format(yourNumber);
Using String.format():
String formattedNumber = String.format("%,.2f", yourNumber);
Note: In all the above examples, "yourNumber" is the double value that you want to format with a comma. The ".2f" in the format string indicates that the decimal places should be rounded to 2 decimal places. You can adjust this value as needed.

Categories