Why is BigDecimal's precision not accurate enough? - java

Code:
BigDecimal test = new BigDecimal(3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199);
System.out.println(test.toPlainString());
Here is the output:
3.141592653589793115997963468544185161590576171875
I debugged it and test has a value of 3.141592653589793115997963468544185161590576171875 in memory too. This is quite intriguing because only the first part is right: 3.141592653589793.

Your 3.1415... is a double literal, it gets truncated to double precision by the compiler before the BigDecimal even sees it. Put quotes around it and pass it as a string.

new BigDecimal(double)
constructor will get the imprecision of double.
Try using the String based constructor instead:
new BigDecimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199")

Try it with quotes around the value in the constructor:
BigDecimal test = new BigDecimal("3.14159265358979323846264 .... ");
As it is at the moment you are really adding a lower precision Java double value, not the full precision number

You are building a BigDecimal object by using the constructor which takes a double. Thus, Java will convert your number to double before building the BigDecimal object.
Javadoc says:
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625.
Try using another constructor which takes a String for example.

BigDecimal is accurate enough. However, the number you provided is being converted to the not accurate int or long. Just because you made a long number, does not mean it was taken with that precision:
BigDecimal test1 = new BigDecimal(3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199);
System.out.println(test1.toPlainString());
The output is:
3.141592653589793115997963468544185161590576171875
Try this instead, enclose the input in quotes so that the full precision is captured and it does not get converted by the compiler to a int or long:
BigDecimal test2 = new BigDecimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199");
System.out.println(test2.toPlainString());
The, the output of test2 will be the full precision:
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199

Related

Java initialize doubles from floats [duplicate]

I have a primitive float and I need as a primitive double. Simply casting the float to double gives me weird extra precision. For example:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
However, if instead of casting, I output the float as a string, and parse the string as a double, I get what I want:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
Is there a better way than to go to String and back?
It's not that you're actually getting extra precision - it's that the float didn't accurately represent the number you were aiming for originally. The double is representing the original float accurately; toString is showing the "extra" data which was already present.
For example (and these numbers aren't right, I'm just making things up) suppose you had:
float f = 0.1F;
double d = f;
Then the value of f might be exactly 0.100000234523. d will have exactly the same value, but when you convert it to a string it will "trust" that it's accurate to a higher precision, so won't round off as early, and you'll see the "extra digits" which were already there, but hidden from you.
When you convert to a string and back, you're ending up with a double value which is closer to the string value than the original float was - but that's only good if you really believe that the string value is what you really wanted.
Are you sure that float/double are the appropriate types to use here instead of BigDecimal? If you're trying to use numbers which have precise decimal values (e.g. money), then BigDecimal is a more appropriate type IMO.
I find converting to the binary representation easier to grasp this problem.
float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));
You can see the float is expanded to the double by adding 0s to the end, but that the double representation of 0.27 is 'more accurate', hence the problem.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
This is due the contract of Float.toString(float), which says in part:
How many digits must be printed for
the fractional part […]? 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 float. That
is, suppose that x is the exact
mathematical value represented by the
decimal representation produced by
this method for a finite nonzero
argument f. Then f must be the float
value nearest to x; or, if two float
values are equally close to x, then f
must be one of them and the least
significant bit of the significand of
f must be 0.
I've encountered this issue today and could not use refactor to BigDecimal, because the project is really huge. However I found solution using
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
And this works.
Note that calling result.doubleValue() returns 5623.22998046875
But calling doubleResult.doubleValue() returns correctly 5623.23
But I am not entirely sure if its a correct solution.
I found the following solution:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
If you use float and double instead of Float and Double use the following:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
Use a BigDecimal instead of float/double. There are a lot of numbers which can't be represented as binary floating point (for example, 0.1). So you either must always round the result to a known precision or use BigDecimal.
See http://en.wikipedia.org/wiki/Floating_point for more information.
Floats, by nature, are imprecise and always have neat rounding "issues". If precision is important then you might consider refactoring your application to use Decimal or BigDecimal.
Yes, floats are computationally faster than decimals because of the on processor support. However, do you want fast or accurate?
A simple solution that works well, is to parse the double from the string representation of the float:
double val = Double.valueOf(String.valueOf(yourFloat));
Not super efficient, but it works!
For information this comes under Item 48 - Avoid float and double when exact values are required, of Effective Java 2nd edition by Joshua Bloch. This book is jam packed with good stuff and definitely worth a look.
Does this work?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;
There is a way to convert Float value into Double without adding the extra precision
Float aFloat= new Float(0.11);
String s = aFloat.toString();
Double aDouble = Double.parseDouble(s);
This Approach will not add an extra precisions to your Float value while converting. The only Problem with this approach is memory usage of the JVM by creating an extra tamp String object.
When calling an toString() (aDouble.toString()) on Double will never add an extra precisions. The precisions will be added while type conversion.

Why does multiplying a float and double give less accurate result than multiplying two floats [duplicate]

I have a primitive float and I need as a primitive double. Simply casting the float to double gives me weird extra precision. For example:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
However, if instead of casting, I output the float as a string, and parse the string as a double, I get what I want:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
Is there a better way than to go to String and back?
It's not that you're actually getting extra precision - it's that the float didn't accurately represent the number you were aiming for originally. The double is representing the original float accurately; toString is showing the "extra" data which was already present.
For example (and these numbers aren't right, I'm just making things up) suppose you had:
float f = 0.1F;
double d = f;
Then the value of f might be exactly 0.100000234523. d will have exactly the same value, but when you convert it to a string it will "trust" that it's accurate to a higher precision, so won't round off as early, and you'll see the "extra digits" which were already there, but hidden from you.
When you convert to a string and back, you're ending up with a double value which is closer to the string value than the original float was - but that's only good if you really believe that the string value is what you really wanted.
Are you sure that float/double are the appropriate types to use here instead of BigDecimal? If you're trying to use numbers which have precise decimal values (e.g. money), then BigDecimal is a more appropriate type IMO.
I find converting to the binary representation easier to grasp this problem.
float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));
You can see the float is expanded to the double by adding 0s to the end, but that the double representation of 0.27 is 'more accurate', hence the problem.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
This is due the contract of Float.toString(float), which says in part:
How many digits must be printed for
the fractional part […]? 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 float. That
is, suppose that x is the exact
mathematical value represented by the
decimal representation produced by
this method for a finite nonzero
argument f. Then f must be the float
value nearest to x; or, if two float
values are equally close to x, then f
must be one of them and the least
significant bit of the significand of
f must be 0.
I've encountered this issue today and could not use refactor to BigDecimal, because the project is really huge. However I found solution using
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
And this works.
Note that calling result.doubleValue() returns 5623.22998046875
But calling doubleResult.doubleValue() returns correctly 5623.23
But I am not entirely sure if its a correct solution.
I found the following solution:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
If you use float and double instead of Float and Double use the following:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
Use a BigDecimal instead of float/double. There are a lot of numbers which can't be represented as binary floating point (for example, 0.1). So you either must always round the result to a known precision or use BigDecimal.
See http://en.wikipedia.org/wiki/Floating_point for more information.
Floats, by nature, are imprecise and always have neat rounding "issues". If precision is important then you might consider refactoring your application to use Decimal or BigDecimal.
Yes, floats are computationally faster than decimals because of the on processor support. However, do you want fast or accurate?
A simple solution that works well, is to parse the double from the string representation of the float:
double val = Double.valueOf(String.valueOf(yourFloat));
Not super efficient, but it works!
For information this comes under Item 48 - Avoid float and double when exact values are required, of Effective Java 2nd edition by Joshua Bloch. This book is jam packed with good stuff and definitely worth a look.
Does this work?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;
There is a way to convert Float value into Double without adding the extra precision
Float aFloat= new Float(0.11);
String s = aFloat.toString();
Double aDouble = Double.parseDouble(s);
This Approach will not add an extra precisions to your Float value while converting. The only Problem with this approach is memory usage of the JVM by creating an extra tamp String object.
When calling an toString() (aDouble.toString()) on Double will never add an extra precisions. The precisions will be added while type conversion.

BigDecimal behavior in Java

public class Test {
public static void main(String[] args){
System.out.println(new BigDecimal(58.34));
}
}
If I run above given program in Java, it is giving me output like:
58.340000000000003410605131648480892181396484375
Why is it?
You pass a double when you construct the BigDecimal so the precision is already lost. Easiest fix is probably something like
System.out.println(new BigDecimal("58.34"));
Output is
58.34
This will cause due to double value
System.out.println(new BigDecimal(58.34));
^^^
To avoid this you can use String value
System.out.println(new BigDecimal("58.34"));
I think the BigDecimal.java best explains this feature.
API say's
Translates a double into a BigDecimal which is the exact decimal
representation of the double's binary floating-point value. The scale
of the returned BigDecimal is the smallest value such that (10scale ×
val) is an integer.
Notes:
The results of this constructor can be somewhat unpredictable. One
might assume that writing new BigDecimal(0.1) in Java creates a
BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with
a scale of 1), but it is actually equal to
0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that
matter, as a binary fraction of any finite length). Thus, the value
that is being passed in to the constructor is not exactly equal to
0.1, appearances notwithstanding.
The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which
is exactly equal to 0.1, as one would expect. Therefore, it is
generally recommended that the String constructor be used in
preference to this one.
When a double must be used as a source for a BigDecimal, note that
this constructor provides an exact conversion

Converting from float to double, why the extra decimal points? [duplicate]

I have a primitive float and I need as a primitive double. Simply casting the float to double gives me weird extra precision. For example:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
However, if instead of casting, I output the float as a string, and parse the string as a double, I get what I want:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
Is there a better way than to go to String and back?
It's not that you're actually getting extra precision - it's that the float didn't accurately represent the number you were aiming for originally. The double is representing the original float accurately; toString is showing the "extra" data which was already present.
For example (and these numbers aren't right, I'm just making things up) suppose you had:
float f = 0.1F;
double d = f;
Then the value of f might be exactly 0.100000234523. d will have exactly the same value, but when you convert it to a string it will "trust" that it's accurate to a higher precision, so won't round off as early, and you'll see the "extra digits" which were already there, but hidden from you.
When you convert to a string and back, you're ending up with a double value which is closer to the string value than the original float was - but that's only good if you really believe that the string value is what you really wanted.
Are you sure that float/double are the appropriate types to use here instead of BigDecimal? If you're trying to use numbers which have precise decimal values (e.g. money), then BigDecimal is a more appropriate type IMO.
I find converting to the binary representation easier to grasp this problem.
float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));
You can see the float is expanded to the double by adding 0s to the end, but that the double representation of 0.27 is 'more accurate', hence the problem.
111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
This is due the contract of Float.toString(float), which says in part:
How many digits must be printed for
the fractional part […]? 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 float. That
is, suppose that x is the exact
mathematical value represented by the
decimal representation produced by
this method for a finite nonzero
argument f. Then f must be the float
value nearest to x; or, if two float
values are equally close to x, then f
must be one of them and the least
significant bit of the significand of
f must be 0.
I've encountered this issue today and could not use refactor to BigDecimal, because the project is really huge. However I found solution using
Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()
And this works.
Note that calling result.doubleValue() returns 5623.22998046875
But calling doubleResult.doubleValue() returns correctly 5623.23
But I am not entirely sure if its a correct solution.
I found the following solution:
public static Double getFloatAsDouble(Float fValue) {
return Double.valueOf(fValue.toString());
}
If you use float and double instead of Float and Double use the following:
public static double getFloatAsDouble(float value) {
return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
Use a BigDecimal instead of float/double. There are a lot of numbers which can't be represented as binary floating point (for example, 0.1). So you either must always round the result to a known precision or use BigDecimal.
See http://en.wikipedia.org/wiki/Floating_point for more information.
Floats, by nature, are imprecise and always have neat rounding "issues". If precision is important then you might consider refactoring your application to use Decimal or BigDecimal.
Yes, floats are computationally faster than decimals because of the on processor support. However, do you want fast or accurate?
A simple solution that works well, is to parse the double from the string representation of the float:
double val = Double.valueOf(String.valueOf(yourFloat));
Not super efficient, but it works!
For information this comes under Item 48 - Avoid float and double when exact values are required, of Effective Java 2nd edition by Joshua Bloch. This book is jam packed with good stuff and definitely worth a look.
Does this work?
float flt = 145.664454;
Double dbl = 0.0;
dbl += flt;
There is a way to convert Float value into Double without adding the extra precision
Float aFloat= new Float(0.11);
String s = aFloat.toString();
Double aDouble = Double.parseDouble(s);
This Approach will not add an extra precisions to your Float value while converting. The only Problem with this approach is memory usage of the JVM by creating an extra tamp String object.
When calling an toString() (aDouble.toString()) on Double will never add an extra precisions. The precisions will be added while type conversion.

BigDecimal getting rounded..but i do not want any rounding

In my code i want to insert into db without any rounding or exponentiation ..but it is converted in my java code when i use a sop as 2.631578947368421E-7
Below is the code i use:
BigDecimal a =new BigDecimal(0.0000002631578947368421052631578947368421052632,MathContext.DECIMAL64);
System.out.println(a);
I just want it to be maintained as it is because i want to do some calculations .
Please do provide me an apt solution.
Construct the big decimal using a String, otherwise the constant you enter gets rounded before it is passed as argument to the constructor of BigDecimal.
So write something like this:
BigDecimal a =new BigDecimal("0.0000002631578947368421052631578947368421052632");
System.out.println(a.toPlainString());
And all should work.
EDIT: you should also get rid of the second argument of the constructor as
static MathContext.DECIMAL64 means: A MathContext object with a precision setting matching the IEEE 754R Decimal64 format, 16 digits, and a rounding mode of HALF_EVEN, the IEEE 754R default.
EDIT2: also use a.toPlainString() when printing to not use scientific notation.
You can use MathContext.UNLIMITED.
BigDecimal a =new BigDecimal(0.0000002631578947368421052631578947368421052632,MathContext.UNLIMITED);
System.out.println(a);
System.out.println(a.toPlainString());
When you print value you can use BigDecimal.toPlainString() to return "a string representation of this BigDecimal without an exponent field".
BIgDecimal getting rounded.
No it isn't. BigDecimal has nothing to do with it. The constant value 0.0000002631578947368421052631578947368421052632 is getting rounded. It cannot be represented exactly in floating-point.If you want an accurate BigDecimal with this value, use new BigDecimal(String).

Categories

Resources