General way to convert String into Big Decimal [duplicate] - java

This question already has answers here:
Safe String to BigDecimal conversion
(9 answers)
Closed 6 years ago.
In my Java code I have four number in text format (String):
Es.
String s1 = "1234.56";
String s2 = "1235,56";
String s3 = "1,234.56";
String s4 = "1.234,56";
I want to convert these Strings into BigDecimal:
BigDecimal b1 = new BigDecimal(s1);
...
...
Obviously it does not work, because BigDecimal does not accept all formats (strings with commas).
There is a general way that allows me to convert all strings in BigDecimal?

Decimal part is allways locale dependent, so you can use the NumberFormat class and use the parsed result as string to get a BigDecimal from it
Example:
final NumberFormat format = NumberFormat.getInstance(Locale.FRANCE);
final Number number = format.parse("3,1415");
final BigDecimal bd = new BigDecimal(number.toString());
System.out.println(bd);

What about DecimalFormat ? Doc here
You just have to define a pattern for decimal separator before parsing the string...
DecimalFormat decimalFormat = new DecimalFormat("#,###.##");
decimalFormat.setParseBigDecimal(true);
BigDecimal bigDecimal = (BigDecimal) decimalFormat.parse("1,234.56");
System.out.println(bigDecimal);
Should do the trick

To cover all cases I would suggest following impl:
private static BigDecimal convertToBigDecimal(String s) {
StringBuilder sb = new StringBuilder();
if (s.indexOf('.') != -1 && s.indexOf(',') != -1) {
s = s.replace(',', '.');
String[] ss = s.split("\\.");
for (int i = 0; i < ss.length; i++) {
if (i > 0 && i == ss.length - 1) {
sb.append('.');
}
sb.append(ss[i]);
}
} else if (s.indexOf('.') != -1) {
String[] ss = s.split("\\.");
if (ss.length > 2) {
sb.append(s.replace(".", ""));
} else {
// assume it is decimal delimiter
sb.append(ss[0]).append('.').append(ss[1]);
}
} else if (s.indexOf(',') != -1) {
String[] ss = s.split(",");
if (ss.length > 2) {
sb.append(s.replace(",", ""));
} else {
// assume it is decimal delimiter
sb.append(ss[0]).append('.').append(ss[1]);
}
}
BigDecimal bd = new BigDecimal(sb.toString());
return bd;
}
The ideas is first check if we have both than we definitely have decimal part and we replace all , to . and then skip all . except last one. Otherwise if we have several same delimiters it is number without decimal part. In other case we need to know pattern to distinguish with decimal or not. For sample numbers we assume it is with decimal.

Related

How to select a specific digit in Java?

Suppose you code a simple divide program in Java and run it. Now suppose it gives you this answer 451.12531 . But you want to select only a single or two digit from this answer may be before the point or after it. In this case we assume that we need to select. Second number which is 5. And you want to print this only. How do you do that?
This can be done by converting your Double to a String using:
String s = String.valueOf(double);
You can then use the Character.getNumericValue() method to get the desired number/position:
int x = Character.getNumericValue(s.charAt(1));
Full example:
Double d = 451.12531;
String s = String.valueOf(d);
int x = Character.getNumericValue(s.charAt(1));
where x is your desired number, in the above example it will be 5
Try this :
private void selectSingleDigit(double number) {
String noInStringFormat = String.valueOf(number);
int digit = getDigitAtSpecificDigit(2,noInStringFormat);
System.out.println(digit);
}
private int getDigitAtSpecificDigit(int index,String str){
if (str != null && !str.isEmpty()) {
return Integer.parseInt(String.valueOf(str.charAt(index)));
} else return -1;
}
Try using this, here d1 will have digits before decimal point and d2 will have digits after decimal point.
String d1 = text.substring( 0,text.indexOf('.'))
String d2 = text.substring( text.indexOf('.'), text.length());

Validate a String to Double with max digits

For example when I parse a string "12345678901234567890" to double using Double.parseDouble() it returns the value "12345678901234567000" since it can hold up to 17 digits.
I want to validate this scenario and the user should be allowed to pass only 17 digits. How do I do this?
Example :
1.2345678901234567890 is invalid because it has more than 17 digits total
1.2345E+10 is valid
Tried something like this which can count the digits using split function
String input="12345678901234567E100";
String inputWithoutSign;
int lengthFullNumber;
int lengthFraction;
double v = Double.parseDouble(input);
if(input.startsWith("+") || input.startsWith("-")){
inputWithoutSign = input.split("[-+]",2)[1];
}
else inputWithoutSign = input;
String num = inputWithoutSign.split("[eE]", 2)[0];
if(num.indexOf('.') == -1){
lengthFullNumber = num.length();
lengthFraction = 0;
}else{
String[] splitNum = num.split("\\.", 2);
lengthFullNumber = splitNum[0].length();
lengthFraction = splitNum[1].length();
}
System.out.println("length:"+(lengthFullNumber+lengthFraction));
Presuming I understand your goal of limiting the number of digits, this may help solve the problem.
Test cases
String[] vals = {
"12345678901234567890", "123456789091919191919",
"182828282.18282828", "182828282.182828282", "191929e10",
"192929.22929e10"
};
Try and parse them
for (String v : vals) {
// remove possible decimal point and signs
String test = v.replaceAll("[.+-]", "");
// remove any exponents at end of string
test = test.replace("\\D+.*", "");
if (test.length() > 17) {
System.out.println(v + " has too many digits");
continue;
}
double d = Double.parseDouble(v);
System.out.println(v + " parses to " + d);
}

Parse double without mantissa and exponent separator

I have to read double from data files with the following format
1.234+5 // = 1.234e+5
1.234-5 // = 1.234e-5
I can't change the format and I have to parse millions/billions double from data files(= method should be efficient) .
Can I provide a decimal format or is there a convenient method (available in java jdk) able to parse these doubles ?
Double.parseDouble("1.234+5"); // throws NumberFormatException
Scanner scanner = new Scanner("1.234+5");
Scanner.nextDouble(); // throws InputMismatchException
EDIT 1
Precision for what i call data files :
Data files are ENDF formatted file which is a really strict format (300 pages manual) and here is an extract of one of these files
9.223500+4 2.330248+2 0 0 0 63515 8457
2.22102+16 1.57788+13 0 0 6 03515 8457
4.170051+4 1.312526+3 1.641191+5 1.625818+3 4.413323+6 1.648523+53515 8457
I can parse integers with a simple
Integer.parseInt()
But i can't with double.
You can use a regex to insert an e which is then parseable in the normal way:
private static final Pattern INSERT_EXPONENT = Pattern.compile("(.*)([+-].*)");
System.out.println(INSERT_EXPONENT.matcher("1.234+5").replaceFirst("$1e$2"));
System.out.println(INSERT_EXPONENT.matcher("1.234-5").replaceFirst("$1e$2"));
This is just quick&dirty and doesn't guard against invalid input.
Here is my attempt. I don't know if this is what you are looking for, but this will convert the number into a double format. Hope this will help.
String temp = "1.234+5";
StringBuilder str = new StringBuilder(temp);
for(int index = temp.length() - 1; index > 0; index--)
{
//this will look for a + or - symbol in your string
if(temp.charAt(index) == '+' || temp.charAt(index) == '-') {
str.insert(index, 'e'); //this will insert e before the symbol
break;
}
}
temp = str.toString();
System.out.println(temp);
double a= Double.parseDouble(temp); //convert the string back to a double
System.out.println(a); //this is just to test the output
double b = 1.234e+5;
System.out.println(b);
You will probably need a regex.
private void test() {
String [] test = {"1.234+5", "-1.234-2"};
for (String s : test) {
System.out.println(s + " -> " + parse(s));
}
}
private static final Pattern FindMissingE = Pattern.compile("([+-]{0,1}[0-9.]+)([+-]{0,1}[0-9]+)");
private double parse(String s) {
Matcher m = FindMissingE.matcher(s);
if(m.find()) {
return Double.parseDouble(m.replaceFirst("$1e$2"));
}
return 0.0;
}

Pattern for Integers in Java (###.###.###)

I need to format Integers to a different pattern.
Example:
Input: 13040321
Output: 13.040.321
Example2:
Input: 2323
Output: 2.323
I tried use it:
String pattern = "###.###.###";
DecimalFormat decimalFormat = new DecimalFormat(pattern); //ERROR HERE
decimalFormat.setGroupingSize(3);
String formattedValue = decimalFormat.format(value);
return formattedValue;
I cannot have outputs using "," or decimals.
By looking at the DecimalFormat docs, I can say, that . is used for decimal separator. If you would like to group numbers, you should use , instead. Also you should specify locale, so the following code should work fine:
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setGroupingSize(3);
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setGroupingSeparator('.');
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
System.out.println(decimalFormat.format(value));
The DecimalFormat's pattern requires you use the comma as the grouping character. So your pattern has to be #,###. Since you want to output periods for grouping, you would need to set your own DecimalFormatSymbols setDecimalFormatSymbols
I assume this formatting is consistent for a particular locale. But if it isn't, your can construct your own custom instance...
An extremely dirty (but working) solution:
public static String pattern(int number) {
String textValue = String.valueOf(number);
StringBuilder sb = new StringBuilder(textValue);
String reversedTextValue = sb.reverse().toString();
StringBuilder result = new StringBuilder();
for(int i = 0; i < reversedTextValue.length(); i++) {
result.append(i % 3 == 0 ? "." + reversedTextValue.charAt(i) : reversedTextValue.charAt(i));
}
return result.reverse().toString().substring(0, result.length() - 1);
}
String getPattern(Integer i, String pattern) {
//assumes a pattern that uses '#' to represent where a digit goes
int y = 0;
StringBuilder sb = new StringBuilder();
String iStr = Integer.toString(i);
for (int x = 0; x < iStr.length(); i++) {
while (y < pattern.length()) {
if ('#' == pattern.charAt(y)) {
sb.append(iStr.charAt(x));
y++;
break;
} else {
sb.append(pattern.charAt(y));
y++;
}
}
}
return sb.toString();
}

Grouping the numbers with comma in android

Eg:- double ab=1234567.00;
The expected output should be,
ab=12,34,567;
But the following format gives the default three digit grouping.
DecimalFormat df_separator = new DecimalFormat("###,###,##0.00");
Also tried with,
DecimalFormat df_separator = new DecimalFormat("###,##,##0.00");
still in vain.......
Here you are sir,
NumberFormat numberFormat = NumberFormat.getInstance();
String formattedNr = numberFormat.format(12345678L);
This will give you: 12,345,678.00
Edit:
public String formatDouble(double number)
{
String result = "";
String numberStr = String.valueOf(number);
char[] charArray = numberStr.toCharArray();
Character[] charObjectArray = ArrayUtils.toObject(charArray);
for (int i=charObjectArray.length()-1; i>=0 i++)
{
if (charObjectArray[i] == ".")
{
result = "." + result;
continue;
}
result = charObjectArray[i] + result;
if (i % 2 == 0) result = "," + result;
}
return result;
}
This is pseudo code as I don't have a JVM atm but it should (almost) do the job.
Edit: Finally
Add the following jar to your project: http://icu-project.org/apiref/icu4j/com/ibm/icu/text/NumberFormat.html
Format format = com.ibm.icu.text.NumberFormat.getCurrencyInstance(new Locale("en", "in"));
System.out.println(format.format(new BigDecimal("100000000")));
double ab=1234567.00;
String str = new DecimalFormat("#,##,##,###.00").format(ab);
Log.d("TAG", str);
try this.

Categories

Resources