Parsing a floating point number with mantissa in Java - java

Using the standard Java Double class to parse a floating point number with mantissa seems to omit the + sign from the exponent.
For example:
Double.parseDouble("1258124354E-28") returns 1.258124354E-19, but
Double.parseDouble("1243544322E+24") returns 1.243544322E33 (not E+33)
Is there any way to get the + sign using Double, without string post-processing?
The BigDecimal class does a better job, e.g.
new BigDecimal("1243544322E+24").toString() does return 1.243544322E+33
but it is generally slower than Double, which shows in intensive processing.

You need to differentiate between the value stored in a double and its string representation.
A double doesn't store a textual representation at all. 1.243544322E33 is 1.243544322E+33. They're the same value.
Now if you want to work on how you format a double value back to a String, that's a different matter - but the parsing you're doing is fine. You're not losing any information.

Jon is correct as above, its representing it correctly in memory. If you want it to display with the + on the mantissa then you are talking about string formatting the double.
Long winded official formatter reference
import java.util.Formatter;
double myDouble = Double.parseDouble("1243544322E+24");
Formatter formatter = new Formatter();
formatter.format("%+E", myDouble); //This should return with what you want, though with an extra + infront of the number
I haven't ran this code so I'm not 100% sure it will do the job. The + in the format specifier forces it to place a + or - sign for any number... I believe that extends to the mantissa.

Related

Is there a way to get right results from BigDecimal.floatValue() function? [duplicate]

I am working with an application that is based entirely on doubles, and am having trouble in one utility method that parses a string into a double. I've found a fix where using BigDecimal for the conversion solves the issue, but raises another problem when I go to convert the BigDecimal back to a double: I'm losing several places of precision. For example:
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class test {
public static void main(String [] args){
String num = "299792.457999999984";
BigDecimal val = new BigDecimal(num);
System.out.println("big decimal: " + val.toString());
DecimalFormat nf = new DecimalFormat("#.0000000000");
System.out.println("double: "+val.doubleValue());
System.out.println("double formatted: "+nf.format(val.doubleValue()));
}
}
This produces the following output:
$ java test
big decimal: 299792.457999999984
double: 299792.458
double formatted: 299792.4580000000
The formatted double demonstrates that it's lost the precision after the third place (the application requires those lower places of precision).
How can I get BigDecimal to preserve those additional places of precision?
Thanks!
Update after catching up on this post. Several people mention this is exceeding the precision of the double data type. Unless I'm reading this reference incorrectly:
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3
then the double primitive has a maximum exponential value of Emax = 2K-1-1, and the standard implementation has K=11. So, the max exponent should be 511, no?
You've reached the maximum precision for a double with that number. It can't be done. The value gets rounded up in this case. The conversion from BigDecimal is unrelated and the precision problem is the same either way. See this for example:
System.out.println(Double.parseDouble("299792.4579999984"));
System.out.println(Double.parseDouble("299792.45799999984"));
System.out.println(Double.parseDouble("299792.457999999984"));
Output is:
299792.4579999984
299792.45799999987
299792.458
For these cases double has more than 3 digits of precision after the decimal point. They just happen to be zeros for your number and that's the closest representation you can fit into a double. It's closer for it to round up in this case, so your 9's seem to disappear. If you try this:
System.out.println(Double.parseDouble("299792.457999999924"));
You'll notice that it keeps your 9's because it was closer to round down:
299792.4579999999
If you require that all of the digits in your number be preserved then you'll have to change your code that operates on double. You could use BigDecimal in place of them. If you need performance then you might want to explore BCD as an option, although I'm not aware of any libraries offhand.
In response to your update: the maximum exponent for a double-precision floating-point number is actually 1023. That's not your limiting factor here though. Your number exceeds the precision of the 52 fractional bits that represent the significand, see IEEE 754-1985.
Use this floating-point conversion to see your number in binary. The exponent is 18 since 262144 (2^18) is nearest. If you take the fractional bits and go up or down one in binary, you can see there's not enough precision to represent your number:
299792.457999999900 // 0010010011000100000111010100111111011111001110110101
299792.457999999984 // here's your number that doesn't fit into a double
299792.458000000000 // 0010010011000100000111010100111111011111001110110110
299792.458000000040 // 0010010011000100000111010100111111011111001110110111
The problem is that a double can hold 15 digits, while a BigDecimal can hold an arbitrary number. When you call toDouble(), it attempts to apply a rounding mode to remove the excess digits. However, since you have a lot of 9's in the output, that means that they keep getting rounded up to 0, with a carry to the next-highest digit.
To keep as much precision as you can, you need to change the BigDecimal's rounding mode so that it truncates:
BigDecimal bd1 = new BigDecimal("12345.1234599999998");
System.out.println(bd1.doubleValue());
BigDecimal bd2 = new BigDecimal("12345.1234599999998", new MathContext(15, RoundingMode.FLOOR));
System.out.println(bd2.doubleValue());
Only that many digits are printed so that, when parsing the string back to double, it will result in the exact same value.
Some detail can be found in the javadoc for Double#toString
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.
If it's entirely based on doubles ... why are you using BigDecimal? Wouldn't Double make more sense? If it's too large of value (or too much precision) for that then ... you can't convert it; that would be the reason to use BigDecimal in the first place.
As to why it's losing precision, from the javadoc
Converts this BigDecimal to a double. This conversion is similar to the narrowing primitive conversion from double to float as defined in the Java Language Specification: if this BigDecimal has too great a magnitude represent as a double, it will be converted to Double.NEGATIVE_INFINITY or Double.POSITIVE_INFINITY as appropriate. Note that even when the return value is finite, this conversion can lose information about the precision of the BigDecimal value.
You've hit the maximum possible precision for the double. If you would still like to store the value in primitives... one possible way is to store the part before the decimal point in a long
long l = 299792;
double d = 0.457999999984;
Since you are not using up (that's a bad choice of words) the precision for storing the decimal section, you can hold more digits of precision for the fractional component. This should be easy enough to do with some rounding etc..

Converting big decimal to double without exponential format

I'm doing calculations on High decimal precision BigDecimal objects.
I'm using a third party library that requires the parameter in double. When I try to convert this to double i get the values in exponential format instead of decimals .
BigDecimal 0.000035000000000
Double 3.5E-5
BigDecimal bd;
BigDecimal(0.0000349999999995631583260546904057264328002929687500);
bd= bd.setScale(15,BigDecimal.ROUND_CEILING);
Log.i("bd","bd "+bd.toPlainString());
Log.i("bd","double"+bd.doubleValue());
I have found various ways of converting this 3.5E-5 value as correct decimal in points but in String format only . Whereas I need the value in double
Able to get without exponential in String object But not in double
DecimalFormat df = new DecimalFormat("#");
df.setMaximumFractionDigits(16);
String test=df.format(bd,doubleValue());
System.out.println("op2 "+test);
Double test1=Double.parseDouble(test);
System.out.println("op2 "+test1);
OUTPUT op2 .000035 op2 3.5E-5
I'm struggling since since days to find a solution as I have no control over 3rd party library that requires the value to be in double
Refferred Links
Format BigDecimal without scientific notation with full precission
Converting exponential value in java to a number format
Conversion from exponential form to decimal in Java
How to resolve a Java Rounding Double issue
Java BigDecimal without E
How to Avoid Scientific Notation in Double?
My question is different then these links, I do not want the double as string without exponential instead I want it in double format only.
EDIT
If the double value passed is in exponential format, the NDK based libary will further do calculations on this object, which will result inmore decimal points and larger Exponential value. So, I want to pass a simple double without E
You seem to be confusing a double, a 64 bit value where different bits have a different meaning, with its string representation.
Let's take a value of 0.015625. Internally, the double is represented as 64 bits (I don't know right now which combination of bits, but that is irrelevant anyway).
But the string representation depends on the format settings of the library you are using. Note that the representations 0.015625 and 1.5625E-2 represent the exact same double value. The double is not stored as a string, it does not contain a . or an E, just a number of bits. The combination of these bits form its real value, and it is the same, no matter how you represent it as a string. If you have a different format setting, the result can just as well be 1.56E-2. This just means that the code that converts the internal bit combination of the double to a string does some rounding.
So, to obtain a double from a BigDecimal, simply use bd.doubleValue(). There is no need to use an intermediate string representation, and it can even be detrimental to do so, because if the string representation performs some rounding, you don't get the best approximation of the value in the BigDecimal.
So again, bd.doubleValue() is the only correct way to do this. This does not return a specific format, it simply returns the double that is closest to the value in the BigDecimal.
And again, do not confuse the double type with how it is represented as a string. These are different things. A double is a double is a double, and that is not stored as a string, exponential or plain or rounded or whatever.
Actually, BigDecimal.toString() and BigDecimal.toPlainString() also return different string representations of the same BigDecimal. AFAIK, there is even a third string conversion function, toEngineeringString(), which gives you yet another string representation of the same BigDecimal. This is similar for double: different output routines with different arguments return different strings for the exact same value.
use this: amount=value to be converted to double from string
double dd = new BigDecimal(amount).doubleValue();
String val = String.format("%.4f", dd);
BigDecimal bd = BigDecimal.valueOf(Double.valueOf(val));
Double d=bd.doubleValue() ;
DecimalFormat formatter = new DecimalFormat("0.0000");
System.out.println("doubleValue= " + formatter .format(d));

How to solve "operator * cannot be applied to java.lang.string"?

I have been trying to convert value to decimal such as RM108.00. I have used
the method value = String.format("%.2f", curProduct.price); and it works,
but when i want to
apply value * ShoppingCartActivity.getProductQuantity(curProduct), it says
that operator * cannot applied to java.lang.string. How do i solve this
problem? How should i make the total price into 2 decimal?
item.productPrice = (TextView) convertView
.findViewById(R.id.textViewTotal);
value = String.format("%.2f", curProduct.price);
if (mShowPrice) {
item.productPrice.setText("Total Price: " +
String.valueOf(curProduct.price *
ShoppingCartActivity.getProductQuantity(curProduct)));
} else{
item.productPrice.setText("RM" + String.valueOf(value));
}
What this means is that one of the two following methods is a string, not a number:
curProduct.price
ShoppingCartActivity.getProductQuantity(curProduct)
You can convert them to an integer (Or float, if there is a decimal in it) by using the following code:
Integer.parseInt(curProduct.price);
Float.parseFloat(curProduct.price);
It might also be worth looking at where these value are defined, and making sure to use integers/floats whenever possible, as the conversions can take some time. Integers should always be used for whole numbers (If they aren't extremely large), and floats or doubles should be used for decimal numbers. Don't use strings for numbers unless you know exactly what you are doing!
Thats because String.format() gives you a formatted String and not any other formatted datatype you expect, and you cannot operate arithmetic operators (such as +) on Strings. Why don't you try to parse all strings to datatypes by using
Double.parseDouble(value) for double
Float.parseFloat(value) for float
Integer.parseInt(value) for int
and then perform the arithmetic operations.

convert BigDecimal to Scientific Notation Java

How do i print this in scientific notation:
BigDecimal r= (a.subtract(exact, MathContext.DECIMAL128)).divide(exact, MathContext.DECIMAL128).stripTrailingZeros();
DecimalFormat format = new DecimalFormat("0.###E0");
System.out.println(new BigDecimal(format.format(r));
where:
a = 1.111111111111111160454356650006957352161407470703125
exact = 0.11
returns:
r = 0.900010000000000004
any ideas? I've also tried calling EngineeringString() on the BigDecimal but this also didn't seem to work for me
You overdid the thing. What you only need is:
System.out.println(format.format(r));
The DecimalFormat object does indeed create a string, creating a BigDecimal instance again would just parse the number from string again - and the toString() method is called on the BigDecimal instance, produing the output you described...
Some clarification
BigDecimal, and other numeric formats (and dates too!) are stored in binary formats in the memory, abstracted from how us, humans think of them. BigDecimal for example stores the decimal digits, and where the decimal point is. Floating point numbers are even more sophisticated. Date stores the seconds from The Epoch. You need to format them to be readable. Formatting means to create a String (or semantically similar) object, that represents the value of the given object in the desired format. This doesn't involve changing the original object in any way.
The default formatting, toString() provides one generic format. To get your output the way you'd like does not mean to change the value to be formatted right with toString(), but to transform the unchanged value into the right String. Nice example is Double.toString() (using sun.mic.FloatingDecimal): it does exponential notation when the number is large or small enough, but in between, it prints in plain decimal format...

losing precision converting from java BigDecimal to double

I am working with an application that is based entirely on doubles, and am having trouble in one utility method that parses a string into a double. I've found a fix where using BigDecimal for the conversion solves the issue, but raises another problem when I go to convert the BigDecimal back to a double: I'm losing several places of precision. For example:
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class test {
public static void main(String [] args){
String num = "299792.457999999984";
BigDecimal val = new BigDecimal(num);
System.out.println("big decimal: " + val.toString());
DecimalFormat nf = new DecimalFormat("#.0000000000");
System.out.println("double: "+val.doubleValue());
System.out.println("double formatted: "+nf.format(val.doubleValue()));
}
}
This produces the following output:
$ java test
big decimal: 299792.457999999984
double: 299792.458
double formatted: 299792.4580000000
The formatted double demonstrates that it's lost the precision after the third place (the application requires those lower places of precision).
How can I get BigDecimal to preserve those additional places of precision?
Thanks!
Update after catching up on this post. Several people mention this is exceeding the precision of the double data type. Unless I'm reading this reference incorrectly:
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3
then the double primitive has a maximum exponential value of Emax = 2K-1-1, and the standard implementation has K=11. So, the max exponent should be 511, no?
You've reached the maximum precision for a double with that number. It can't be done. The value gets rounded up in this case. The conversion from BigDecimal is unrelated and the precision problem is the same either way. See this for example:
System.out.println(Double.parseDouble("299792.4579999984"));
System.out.println(Double.parseDouble("299792.45799999984"));
System.out.println(Double.parseDouble("299792.457999999984"));
Output is:
299792.4579999984
299792.45799999987
299792.458
For these cases double has more than 3 digits of precision after the decimal point. They just happen to be zeros for your number and that's the closest representation you can fit into a double. It's closer for it to round up in this case, so your 9's seem to disappear. If you try this:
System.out.println(Double.parseDouble("299792.457999999924"));
You'll notice that it keeps your 9's because it was closer to round down:
299792.4579999999
If you require that all of the digits in your number be preserved then you'll have to change your code that operates on double. You could use BigDecimal in place of them. If you need performance then you might want to explore BCD as an option, although I'm not aware of any libraries offhand.
In response to your update: the maximum exponent for a double-precision floating-point number is actually 1023. That's not your limiting factor here though. Your number exceeds the precision of the 52 fractional bits that represent the significand, see IEEE 754-1985.
Use this floating-point conversion to see your number in binary. The exponent is 18 since 262144 (2^18) is nearest. If you take the fractional bits and go up or down one in binary, you can see there's not enough precision to represent your number:
299792.457999999900 // 0010010011000100000111010100111111011111001110110101
299792.457999999984 // here's your number that doesn't fit into a double
299792.458000000000 // 0010010011000100000111010100111111011111001110110110
299792.458000000040 // 0010010011000100000111010100111111011111001110110111
The problem is that a double can hold 15 digits, while a BigDecimal can hold an arbitrary number. When you call toDouble(), it attempts to apply a rounding mode to remove the excess digits. However, since you have a lot of 9's in the output, that means that they keep getting rounded up to 0, with a carry to the next-highest digit.
To keep as much precision as you can, you need to change the BigDecimal's rounding mode so that it truncates:
BigDecimal bd1 = new BigDecimal("12345.1234599999998");
System.out.println(bd1.doubleValue());
BigDecimal bd2 = new BigDecimal("12345.1234599999998", new MathContext(15, RoundingMode.FLOOR));
System.out.println(bd2.doubleValue());
Only that many digits are printed so that, when parsing the string back to double, it will result in the exact same value.
Some detail can be found in the javadoc for Double#toString
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.
If it's entirely based on doubles ... why are you using BigDecimal? Wouldn't Double make more sense? If it's too large of value (or too much precision) for that then ... you can't convert it; that would be the reason to use BigDecimal in the first place.
As to why it's losing precision, from the javadoc
Converts this BigDecimal to a double. This conversion is similar to the narrowing primitive conversion from double to float as defined in the Java Language Specification: if this BigDecimal has too great a magnitude represent as a double, it will be converted to Double.NEGATIVE_INFINITY or Double.POSITIVE_INFINITY as appropriate. Note that even when the return value is finite, this conversion can lose information about the precision of the BigDecimal value.
You've hit the maximum possible precision for the double. If you would still like to store the value in primitives... one possible way is to store the part before the decimal point in a long
long l = 299792;
double d = 0.457999999984;
Since you are not using up (that's a bad choice of words) the precision for storing the decimal section, you can hold more digits of precision for the fractional component. This should be easy enough to do with some rounding etc..

Categories