Parse double without mantissa and exponent separator - java

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

Related

Numeric values from a String

I am creating a physics calculator and currently working on average velocity. For displacement I would like the user to input a string i.e. "5km north". Then using a method assign only the numeric values in the string to be doubles using Double.parseDouble or something of the like.
Below is my method to pull out numeric values
private double getNumericalValue(String userInput) {
double numericalOutput = 0.0;
for (int i = 0; i < userInput.length(); i++) {
if (userInput.charAt(i) >= 65 && userInput.charAt(i) <= 122) {
numericalOutput = Double.parseDouble(userInput.substring(0, i));
}
}
return numericalOutput;
}
and lastly the getAverageVelocity method
private void getAverageVelocity() {
// formula average velocity = displacement s / change in time t
double avgVelocity = 0.0;
System.out.println("Enter the displacement: [ex. 5 km north]");
String displacement = getStringInput();
System.out.println("Enter the change in time: [ex. 1 hour]");
String changeInTime = getStringInput();
// parse the string and change numerical input into double
double numericalS = getNumericalValue(displacement);
double numericalT = getNumericalValue(changeInTime);
avgVelocity = numericalS / numericalT;
System.out.println("The Average Velocity is: " + avgVelocity);
}
As you can see I figured I could simply compare chars to their ascii value. However, it is including the k or a space in my double output which is throwing this error.
Exception in thread "main" java.lang.NumberFormatException: For input string: "5 k"
at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2054)
at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.base/java.lang.Double.parseDouble(Double.java:556)
at Main.getNumericalValue(Main.java:122)
at Main.getAverageVelocity(Main.java:393)
at Main.kineticsIn1DCalculator(Main.java:375)
at Main.performAction(Main.java:141)
at Main.runner(Main.java:29)
at Main.main(Main.java:22)
Any help and a thorough explanation would be great. Thanks.
What you are actually testing in the if block is whether the character at i is a letter A-Z, a-z and other symbols(i.e. [ ^ ], ...etc) to check if it's a number (i.e. 0-9) the corresponding decimal range is 48-57 check this link
as for the exception it's because of substring(0, 0) will result in an empty string, and when trying to parse it, Double.parseDouble() will throw an exception.
here is a solution you can use:
private double getNumericalValue(String userInput) {
double numericalOutput = 0.0;
for (int i = 0; i < userInput.length(); i++) {
if (userInput.charAt(i) >= 48 && userInput.charAt(i) <= 57) {
numericalOutput = Double.parseDouble(userInput.substring(0, i + 1));
}
}
return numericalOutput;
}
Note: substring(i, j) i is inclusive, j exclusive
Update :
just noticed, in your case the exception happens because you are trying to parse the letter "k" since it passes the condition, resulting in Double.parseDouble() throwing an exception.
You have the error because even if the first number is found the for loop continues. You simply have to add break instruction, then the for loop ends at the first number found.
This is the final code of the method getNumericalValue:
private double getNumericalValue(String userInput) {
double numericalOutput = 0.0;
for (int i = 0; i < userInput.length(); i++) {
if (userInput.charAt(i) >= 65 && userInput.charAt(i) <= 122) {
numericalOutput = Double.parseDouble(userInput.substring(0, i));
break;
}
}
return numericalOutput;
}
You simply should input the line and parse its parts.
Best to introduce a class to hold a value with unit, where "km north" counts as a unit.
Since java 14 there exists the record class.
record ValueWithUnit(double value, String unit) {
public static ValueWithUnit from(String s) {
String v = s.replaceFirst("^(.*?)([ A-z].*)$", "$1");
String u = s.replaceFirst("^(.*?)([ A-z].*)$", "$2");
if (u == v) {
u = "";
}
double x = Double.parseDouble(v); // Replaces getNumericalValue(v);
return new Value(x, u);
}
#Override
public String toString() {
String.format("%f %s", value, unit);
}
}
You could use it directly as:
ValueWithUnit vu = new ValueWithUnit(30.0, "km");
System.out.println(vu);
The code then becomes:
private void getAverageVelocity() {
// formula average velocity = displacement s / change in time t
double avgVelocity = 0.0;
System.out.println("Enter the displacement: [ex. 5 km north]");
ValueWithUnit displacement = ValueWithUnit.from(getStringInput());
System.out.println("Enter the change in time: [ex. 1 hour]");
ValueWithUnit changeInTime = ValueWithUnit.from(getStringInput());
// parse the string and change numerical input into double
double numericalS = displacement.value;
double numericalT = changeInTime.value;
avgVelocity = numericalS / numericalT;
System.out.printf("The Average Velocity is: %f %s%n", avgVelocity,
displacement.unit + "/" + changeInTime.unit);
}

Converting double to String in order to read a txt file [duplicate]

How can I convert a String such as "12.34" to a double in Java?
You can use Double.parseDouble() to convert a String to a double:
String text = "12.34"; // example String
double value = Double.parseDouble(text);
For your case it looks like you want:
double total = Double.parseDouble(jlbTotal.getText());
double price = Double.parseDouble(jlbPrice.getText());
If you have problems in parsing string to decimal values, you need to replace "," in the number to "."
String number = "123,321";
double value = Double.parseDouble( number.replace(",",".") );
To convert a string back into a double, try the following
String s = "10.1";
Double d = Double.parseDouble(s);
The parseDouble method will achieve the desired effect, and so will the Double.valueOf() method.
double d = Double.parseDouble(aString);
This should convert the string aString into the double d.
Use new BigDecimal(string). This will guarantee proper calculation later.
As a rule of thumb - always use BigDecimal for sensitive calculations like money.
Example:
String doubleAsString = "23.23";
BigDecimal price = new BigDecimal(doubleAsString);
BigDecimal total = price.plus(anotherPrice);
You only need to parse String values using Double
String someValue= "52.23";
Double doubleVal = Double.parseDouble(someValue);
System.out.println(doubleVal);
Citing the quote from Robertiano above again - because this is by far the most versatile and localization adaptive version. It deserves a full post!
Another option:
DecimalFormat df = new DecimalFormat();
DecimalFormatSymbols sfs = new DecimalFormatSymbols();
sfs.setDecimalSeparator(',');
df.setDecimalFormatSymbols(sfs);
double d = df.parse(number).doubleValue();
String double_string = "100.215";
Double double = Double.parseDouble(double_string);
There is another way too.
Double temp = Double.valueOf(str);
number = temp.doubleValue();
Double is a class and "temp" is a variable.
"number" is the final number you are looking for.
This is what I would do
public static double convertToDouble(String temp){
String a = temp;
//replace all commas if present with no comma
String s = a.replaceAll(",","").trim();
// if there are any empty spaces also take it out.
String f = s.replaceAll(" ", "");
//now convert the string to double
double result = Double.parseDouble(f);
return result; // return the result
}
For example you input the String "4 55,63. 0 " the
output will the double number 45563.0
Using Double.parseDouble() without surrounding try/catch block can cause potential NumberFormatException had the input double string not conforming to a valid format.
Guava offers a utility method for this which returns null in case your String can't be parsed.
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/primitives/Doubles.html#tryParse(java.lang.String)
Double valueDouble = Doubles.tryParse(aPotentiallyCorruptedDoubleString);
In runtime, a malformed String input yields null assigned to valueDouble
Used this to convert any String number to double when u need int just convert the data type from num and num2 to int ;
took all the cases for any string double with Eng:"Bader Qandeel"
public static double str2doubel(String str) {
double num = 0;
double num2 = 0;
int idForDot = str.indexOf('.');
boolean isNeg = false;
String st;
int start = 0;
int end = str.length();
if (idForDot != -1) {
st = str.substring(0, idForDot);
for (int i = str.length() - 1; i >= idForDot + 1; i--) {
num2 = (num2 + str.charAt(i) - '0') / 10;
}
} else {
st = str;
}
if (st.charAt(0) == '-') {
isNeg = true;
start++;
} else if (st.charAt(0) == '+') {
start++;
}
for (int i = start; i < st.length(); i++) {
if (st.charAt(i) == ',') {
continue;
}
num *= 10;
num += st.charAt(i) - '0';
}
num = num + num2;
if (isNeg) {
num = -1 * num;
}
return num;
}
String s = "12.34";
double num = Double.valueOf(s);
Try this,
BigDecimal bdVal = new BigDecimal(str);
If you want Double only then try
Double d = Double.valueOf(str);
System.out.println(String.format("%.3f", new BigDecimal(d)));

Split String from the last iteration

This post is an update to this one : get specific character in a string with regex and remove unused zero
In the first place, i wanted to remove with an regular expression the unused zero in the last match.
I found that the regular expression is a bit overkill for what i need.
Here is what i would like now,
I would like to use split() method
to get from this :
String myString = "2020-LI50532-3329-00100"
this :
String data1 = "2020"
String data2 = "LI50532"
String data3 = "3329"
String data4 = "00100"
So then i can remove from the LAST data the unused Zero
to convert "00100" in "100"
And then concatenate all the data to get this
"2020-LI50532-3329-100"
Im not familiar with the split method, if anyone can enlight me about this ^^
You can use substring method to get rid of the leading zeros...
String myString = "2020-LI50532-3329-00100";
String[] data = myString.split("-");
data[3] = data[3].substring(2);
StringBuilder sb = new StringBuilder();
sb.append(data[0] + "-" + data[1] + "-" + data[2] + "-" + data[3]);
String result = sb.toString();
System.out.println(result);
Assuming that we want to remove the leading zeroes of ONLY the last block, maybe we can:
Extract the last block
Convert it to Integer and back to String to remove leading zeroes
Replace the last block with the String obtained in above step
Something like this:
public String removeLeadingZeroesFromLastBlock(String text) {
int indexOfLastDelimiter = text.lastIndexOf('-');
if (indexOfLastDelimiter >= 0) {
String lastBlock = text.substring(indexOfLastDelimiter + 1);
String lastBlockWithoutLeadingZeroes = String.valueOf(Integer.valueOf(lastBlock)); // will throw exception if last block is not an int
return text.substring(0, indexOfLastDelimiter + 1).concat(lastBlockWithoutLeadingZeroes);
}
return text;
}
Solution using regex:
public class Main {
public static void main(String[] args) {
// Test
System.out.println(parse("2020-LI50532-3329-00100"));
System.out.println(parse("2020-LI50532-3329-00001"));
System.out.println(parse("2020-LI50532-03329-00100"));
System.out.println(parse("2020-LI50532-03329-00001"));
}
static String parse(String str) {
return str.replaceAll("0+(?=[1-9]\\d*$)", "");
}
}
Output:
2020-LI50532-3329-100
2020-LI50532-3329-1
2020-LI50532-03329-100
2020-LI50532-03329-1
Explanation of the regex:
One or more zeros followed by a non-zero digit which can be optionally followed by any digit(s) until the end of the string (specified by $).
Solution without using regex:
You can do it also by using Integer.parseInt which can parse a string like 00100 into 100.
public class Main {
public static void main(String[] args) {
// Test
System.out.println(parse("2020-LI50532-3329-00100"));
System.out.println(parse("2020-LI50532-3329-00001"));
System.out.println(parse("2020-LI50532-03329-00100"));
System.out.println(parse("2020-LI50532-03329-00001"));
}
static String parse(String str) {
String[] parts = str.split("-");
try {
parts[parts.length - 1] = String.valueOf(Integer.parseInt(parts[parts.length - 1]));
} catch (NumberFormatException e) {
// Do nothing
}
return String.join("-", parts);
}
}
Output:
2020-LI50532-3329-100
2020-LI50532-3329-1
2020-LI50532-03329-100
2020-LI50532-03329-1
you can convert the last string portion to integer type like below for removing unused zeros:
String myString = "2020-LI50532-3329-00100";
String[] data = myString.split("-");
data[3] = data[3].substring(2);
StringBuilder sb = new StringBuilder();
sb.append(data[0] + "-" + data[1] + "-" + data[2] + "-" + Integer.parseInt(data[3]));
String result = sb.toString();
System.out.println(result);
You should avoid String manipulation where possible and rely on existing types in the Java language. One such type is the Integer. It looks like your code consists of 4 parts - Year (Integer) - String - Integer - Integer.
So to properly validate it I would use the following code:
Scanner scan = new Scanner("2020-LI50532-3329-00100");
scan.useDelimiter("-");
Integer firstPart = scan.nextInt();
String secondPart = scan.next();
Integer thirdPart = scan.nextInt();
Integer fourthPart = scan.nextInt();
Or alternatively something like:
String str = "00100";
int num = Integer.parseInt(str);
System.out.println(num);
If you want to reconstruct your original value, you should probably use a NumberFormat to add the missing 0s.
The main points are:
Always try to reuse existing code and tools available in your language
Always try to use available types (LocalDate, Integer, Long)
Create your own types (classes) and use the expressiveness of the Object Oriented language
public class Test {
public static void main(String[] args) {
System.out.println(trimLeadingZeroesFromLastPart("2020-LI50532-03329-00100"));
}
private static String trimLeadingZeroesFromLastPart(String input) {
String delem = "-";
String result = "";
if (input != null && !input.isEmpty()) {
String[] data = input.split(delem);
StringBuilder tempStrBldr = new StringBuilder();
for (int idx = 0; idx < data.length; idx++) {
if (idx == data.length - 1) {
tempStrBldr.append(trimLeadingZeroes(data[idx]));
} else {
tempStrBldr.append(data[idx]);
}
tempStrBldr.append(delem);
}
result = tempStrBldr.substring(0, tempStrBldr.length() - 1);
}
return result;
}
private static String trimLeadingZeroes(String input) {
int idx;
for (idx = 0; idx < input.length() - 1; idx++) {
if (input.charAt(idx) != '0') {
break;
}
}
return input.substring(idx);
}
}
Output:
2020-LI50532-3329-100

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

Convert decimal to IEEE Binary

I am trying to convert a number from decimal value to its IEEE 752 form. For example:
+43.9542 -> 01000010001011111101000100011010
-43.9542 -> 11000010001011111101000100011010
And I have written this method:
public float GetFloat32(String numero) {
//Convert
int intBits = Integer.parseInt(numero, 2);
float myFloat = Float.intBitsToFloat(intBits);
//Exponent
getExponent = 127 + (String.valueOf(Integer.toBinaryString(Math.abs((int) myFloat))).length() - 1);
//Sign
getSign = (myFloat < 0) ? 1 : 0;
return myFloat;
}
There is a problem that I cannot solve. I will make another example to make it clear.
double a = k.GetFloat32("01000010001011111101000100011010")
a = 43.9542
But when the number is negative, such as
double a = k.GetFloat32("1000010001011111101000100011010")
I get this error:
It means that my code works perfectly with positive numbers (including the zero) but with negative numbers it crashes. Where is the problem?
Note
I thought that I could solve my problem in this way
Check if String numero has a charAt(0) equal at 1
If yes (numero.charAt(0) == 1) then remove the first char
Call GetFloat32() with the new number (without the 1)
Return the result adding the - in front of the num
This could work but I would like to know where is the problem in the method above. I'd prefer avoiding this solution if possible.
The problem is that int/Integer has an upper limit of 0x7FFF_FFFF and so the Integer.parseInt method won't go beyond this limit.
Use Long.parseLong, check the resulting value for exceeding 0x7FFF_FFFFL and handle according to the logic required for negative integer values.
Although, I don't see anything bad in your very own idea of stripping the sign bit and dealing with the rest to get the absolute value.
Edit There isn't a way to get the encoded float with a simple integer conversion from a bit string. Just consider that +1 and -1 in 32 binary digits according to two's complement representation differ in more than one bit, and 100....000 isn't -0. Signed magnitude isn't the same as two's complement. Moreover, Java's binary and hexadecimal (or any other base's) literals are meant to be positive quantities; if you need a negative value, use a sign.
Later Method Integer.parseUnsignedInt doesn't have an advantage over using Long, since you'll then have to know how use two's complement arithmetic to remove the leading (sign) bit to produce the absolute value which can then be split into exponent and mantissa. (Add or subtract Integer.MIN_VALUE.)
If there isn't IEEE 752 and IEEE 754 is the target format, the easiest form is
float f1 = Float.intBitsToFloat(Integer.parseUnsignedInt(numerio,2));
public class IEEE754ToFloatingValues {
public static double convertToInt(String mantissa_str) {
int power_count = -1;
double mantissa_int = 0.0;
for (int i = 0; i < mantissa_str.length(); i++) {
// System.out.print(Integer.parseInt(mantissa_str.charAt(i) + ""));
mantissa_int += (Integer.parseInt(mantissa_str.charAt(i) + "") * Math.pow(2, power_count));
power_count -= 1;
}
IEEE754ToFloatingValues.logger.info((Object) "IEEE754ToFloatingValues : convertToInt :: end");
return mantissa_int + 1.0;
}
public static String convertToBinary(int i) {
return Integer.toBinaryString(i + 0b10000).substring(1);
}
public static String decimalToHex(String decimal) {
int i = Integer.parseInt(decimal);
System.out.println("<<>>" + i);
String my_hexdata = Integer.toHexString(i);
System.out.println(my_hexdata);
return String.valueOf(ReturnFloatingValue(my_hexdata));
}
public static double ReturnFloatingValue(String my_hexdata) {
String myString = "";
if (my_hexdata == null) {
return -2.0;
}
if (my_hexdata.length() != 8) {
myString = String.format("%1$-" + 8 + "s", my_hexdata).replace(' ', '0');
System.out.println("My hex data after appending 0's is : " + myString);
}
String binary = "";
for (int i = 0; i < myString.length(); i++) {
int num = Integer.parseInt(myString.charAt(i) + "", 16);
binary += convertToBinary(num);
}
System.out.println("Binary length is : " + binary.length());
System.out.println("Binary number is : " + binary);
if (binary == null || binary.isEmpty()) {
return -3.0;
}
String ieee_32 = binary.substring(2);
ieee_32 = String.format("%1$-32s", binary).replace(' ', '0');
long sign_bit = Long.parseLong(new StringBuilder().append(ieee_32.charAt(0)).toString());
long exponent_bias = Long.parseLong(ieee_32.substring(1, 9), long exponent_unbias = exponent_bias - 127L;
System.out.println("Exponent unbias is : " + exponent_unbias);
String mantissa_str = ieee_32.substring(9);
double mantissa_int = convertToInt(mantissa_str);
double real_no = Math.pow(-1.0, (double) sign_bit) * mantissa_int * Math.pow(2.0, (double) exponent_unbias);
System.out.println("Real no is : " + real_no);
return real_no;
}
public static void main(String[] args) {
//decimalToHex("0");
}
}
import java.util.*;
public class IEEE754 {
public static void main(String[] args) {
float floa;
Scanner sc = new Scanner(System.in);
System.out.println("ENTER A FLOATING POINT NUMBER");
floa=sc.nextFloat();
int no,sign;
no=(int) floa;
String a= Integer.toBinaryString(no);
String sub=a.substring(1, a.length());
if(floa<0)
{
sign=1;
}
else
{
sign=0;
}
int exp=a.length()-1;
System.out.println(exp);
int be=127+exp;
String b= Integer.toBinaryString(be);
System.out.print("biased exponent->");
System.out.println(b);
int loop=23-exp;
float floatpart =floa-no;
int q[]=new int[25];
for(int i=1,j=0;i<=loop;i++,j++)
{
q[j]=(int) (floatpart*2);
floatpart=floatpart*2;
if(q[j]==1){
floatpart=floatpart-1;
}
}
System.out.print("mantissa->");
System.out.print(sub);
for(int k=0;k<q.length;k++)
System.out.print(q[k]);
System.out.println();
System.out.println("IEEE754 FORMAT IS");
System.out.print(sign+" "+b+" ");
System.out.print(sub);
for(int k=0;k<q.length;k++)
System.out.print(q[k]);
}
}

Categories

Resources