new BigDecimal(double) vs new BigDecimal(String) [duplicate] - java

This question already has answers here:
BigDecimal from Double incorrect value?
(4 answers)
Convert double to BigDecimal and set BigDecimal Precision
(8 answers)
Closed 7 years ago.
When BigDecimal is used with an input of double and BigDecimal with an input of String different results seem to appear.
BigDecimal a = new BigDecimal(0.333333333);
BigDecimal b = new BigDecimal(0.666666666);
BigDecimal c = new BigDecimal("0.333333333");
BigDecimal d = new BigDecimal("0.666666666");
BigDecimal x = a.multiply(b);
BigDecimal y = c.multiply(d);
System.out.println(x);
System.out.println(y);
x outputs as
0.222222221777777790569747304508155316795087227497352441864147715340493949298661391367204487323760986328125
while y is
0.222222221777777778
Am I wrong in saying that this is because of double imprecision? But since this is a BigDecimal, shouldn't it be the same?

Am I wrong in saying that this is because of double imprecision?
You are absolutely right, this is exactly because of double's imprecision.
But since this is a BigDecimal, shouldn't it be the same?
No, it shouldn't. The error is introduced the moment you create new BigDecimal(0.333333333), because 0.333333333 constant already has an error embedded in it. At that point there is nothing you can do to fix this representation error: the proverbial horse is out of the barn by then, so it's too late to close the doors.
When you pass a String, on the other hand, the decimal representation matches the string exactly, so you get a different result.

Yes, this is floating point error. The problem is that the literals 0.333333333 and 0.666666666 are represented as doubles before being passed as an argument to BigDecimal --- notably, BigDecimal's constructor takes a double as an argument.
This is supported by the standard, which says that floating point literals default to double unless otherwise specified.

Java docs has its answer. According to Java docs of BigDecimal(double val)
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.

When you define a double variable in any way, in most cases it won't be the value you have defined, but the closest possible binary representation. You are passing a double to the constructor, so already providing that small imprecision.

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.

How to get the value without round up/down in Java? [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 3 years ago.
double d0 = Double.parseDouble("53.82233040000000557");
double d1 = Double.valueOf("53.82233040000000557");
output
d0 = 53.822330400000006
d1 = 53.822330400000006
Precision Numbers in Java
Class java.math.BigDecimal is better for handling numbers where precision matters (like monetary amounts), see BigDecimal VS double.
It could be used for geo-coordinates (latitude/longitude). Although practitionars argue that double is precise enough for lat./long. - since you don't want to locate something at nano-meter scale.
Example Code
If you need high precision and scale for your number, use BigDecimal like this:
BigDecimal decimalValue = new BigDecimal("53.82233040000000557");
System.out.println("as BigDecimal: " + decimalValue.toPlainString());
// prints exactly: 53.82233040000000557
Run this code online (IDE one): Double VS BigDecimal for high precision
Read more
Read more in a tutorial on Java: BigDecimal and BigInteger
If you need precision, you have to use a BigDecimal.
The answer is that you cannot. The values you are getting are the most accurate approximation to your values that can possibly be stored in a double. There is no possible way to get a more accurate, less rounded value stored in a double.
If you require that the answers are not rounded at all, therefore, you should not be using double. The data type you should be using instead is BigDecimal.

Why does new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1? [duplicate]

This question already has answers here:
Why are floating point numbers inaccurate?
(5 answers)
BigDecimal compareTo not working as expected
(1 answer)
Closed 7 years ago.
Why does new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1?
If I expect those two to be equal, is there an alternative way to compare them?
Due to the imprecise nature of floating point arithmetic, they're not exactly equal
System.out.println(new BigDecimal(0.015));
displays
0.01499999999999999944488848768742172978818416595458984375
To expand on the answer from #Reimeus, the various constructors for BigDecimal accept different types of input. The floating point constructors, take a floating point as input, and due to the limitations of the way that floats/doubles are stored, these can only store accurately values that are a power of 2.
So, for example, 2⁻², or 0.25, can be represented exactly. 0.875 is (2⁻¹ + 2⁻² + 2⁻³), so it can also be represented accurately. So long as the number can be represented by a sum of powers, where the upper and lower power differ by no more than 53, then the number can be represented exactly. The vast majority of numbers don't fit this pattern!
In particular, 0.15 is not a power of two, nor is it the sum of a power of two, and so the representation is not accurate.
The string constructor on the other hand does store it accurately, by using a different format internally to store the number. Hence, when you compare the two, they compare as being different.
A double cannot exactly represent the value 0.015. The closest value it can represent in its 64 binary bits is 0.01499999999999999944488848768742172978818416595458984375. The constructor new BigDecimal(double) is designed to preserve the precise value of the double argument, which can never be exactly 0.015. Hence the result of your comparison.
However, if you display that double value, for example by:
System.out.println(0.01499999999999999944488848768742172978818416595458984375);
it outputs 0.015 – which hints at a workaround. Converting a double to a String chooses the shortest decimal representation needed to distinguish it from other possible double values.
Thus, if you create a BigDecimal from the double's String representation, it will have a value more as you expect. This comparison is true:
new BigDecimal(Double.toString(0.015)).equals(new BigDecimal("0.015"))
In fact, the method BigDecimal.valueOf(double) exists for exactly this purpose, so you can shorten the above to:
BigDecimal.valueOf(0.015).equals(new BigDecimal("0.015"))
You should use the new BigDecimal(double) constructor only if your purpose is to preserve the precise binary value of the argument. Otherwise, call BigDecimal.valueOf(double), whose documentation says:
This is generally the preferred way to convert a double (or float) into a BigDecimal.
Or, use a String if you can and avoid the subtleties of double entirely.
What actually happens here is this:
0.015 is a primitive double. Which means that as soon as you write it, it is already no longer 0.015, but rather 0.0149.... The compiler stores it as a binary representation in the bytecode.
BigDecimal is constructed to store exactly whatever is given to it. In this case, 0.0149...
BigDecimal is also able to parse Strings into exact representations. In this case "0.015" is parsed into exactly 0.015. Even though double cannot represent that number, BigDecimal can
Finally, when you compare them, you can see that they are not equal. Which makes sense.
Whenever using BigDecimal, be cautious of the previously used type. String, int, long will remain exact. float and double have the usual precision caveat.

Why is BigDecimal's precision not accurate enough?

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

BigDecimal - to use new or valueOf

I came across two ways of getting BigDecimal object out of a double d.
new BigDecimal(d)
BigDecimal.valueOf(d)
Which would be a better approach? Would valueOf create a new object?
In general (not just BigDecimal), what is recommended - new or valueOf?
Those are two separate questions: "What should I use for BigDecimal?" and "What do I do in general?"
For BigDecimal: this is a bit tricky, because they don't do the same thing. BigDecimal.valueOf(double) will use the canonical String representation of the double value passed in to instantiate the BigDecimal object. In other words: The value of the BigDecimal object will be what you see when you do System.out.println(d).
If you use new BigDecimal(d) however, then the BigDecimal will try to represent the double value as accurately as possible. This will usually result in a lot more digits being stored than you want. Strictly speaking, it's more correct than valueOf(), but it's a lot less intuitive.
There's a nice explanation of this in the JavaDoc:
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.
In general, if the result is the same (i.e. not in the case of BigDecimal, but in most other cases), then valueOf() should be preferred: it can do caching of common values (as seen on Integer.valueOf()) and it can even change the caching behaviour without the caller having to be changed. new will always instantiate a new value, even if not necessary (best example: new Boolean(true) vs. Boolean.valueOf(true)).
If you are using your BigDecimal objects to store currency values, then I strongly recommend that you do NOT involve any double values anywhere in their calculations.
As stated in another answer, there are known accuracy issues with double values and these will come back to haunt you big time.
Once you get past that, the answer to your question is simple. Always use the constructor method with the String value as the argument to the constructor, as there is no valueOf method for String.
If you want proof, try the following:
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal("0.01");
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);
You'll get the following output:
bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01
See also this related question
Basically valueOf(double val) just does this:
return new BigDecimal(Double.toString(val));
Therefore -> yep, a new object will be created :).
In general I think it depends upon your coding style. I would not mixure valueOf and "new", if both are the same outcome.
Foreword
Why are we having these discussions about floating-point type, numbers & arithmetic? Simple. We count in base 10, but the machine count in base 2.
BigDecimal - Need for an exact representation (not approximation)
If you are using BigDecimal, this means that you want an exact representation of 0.1 and other negative powers of ten (usually you would be dealing with money or arithmetic involving decimals).
Double means trouble (where BigDecimal is concerned)
Then, if you are finding yourself having to manipulate double(or float) values using BigDecimal, then you are in double trouble, because it is impossible to represent 0.1 as a double in base 2. The machine "stores" doubles(IEEE-754 standard for floating-point arithmetic) as base 2. Here is a good write-up of what's really happening if you are interested.). Duncan's answer illustrates what i am trying to say, of what to do and not do.
Any programming language that you think can store 0.1 accurately is actually not. It is just an approximation.
System.out.println(0.1d);
//Prints 0.1 or so you think ;-)
//If you are not convinced, try this:
double x = 1.1; double y = 1.0;
if (x-y == 0.1) {// print true } else {// print false}
//or perhaps this:
double amount1 = 2.15;
double amount2 = 1.10;
System.out.println("Difference: " + (amount1 - amount2));
Examples
double smallD = 0.0001;
double smallDNoScientificNotation = 0.001; //>= 10E-3
double normalD = 10.345678;
double bigDNoScientificNotation = 1234567.123456789; //<=10E7
double bigD = 56_789_123_456_789.123456789;
//double
System.out.println(smallD); //1.0E-4, computerized scientific notation, this is how Double toString works
System.out.println(smallDNoScientificNotation); //0.001, OK
System.out.println(normalD); //10.345678, OK
System.out.println(bigDNoScientificNotation); //1234567.123456789, OK
System.out.println(bigD); //5.6789123456789125E13, computerized scientific notation, this is how Double toString works
//new BigDecimal(double): not OK, don't use! Attempting to representing the base-2 representation as accurately as possible
System.out.println(new BigDecimal(smallD)); //0.000100000000000000004792173602385929598312941379845142364501953125
System.out.println(new BigDecimal(smallDNoScientificNotation)); //0.001000000000000000020816681711721685132943093776702880859375
System.out.println(new BigDecimal(normalD)); //10.34567799999999948568074614740908145904541015625
System.out.println(new BigDecimal(bigDNoScientificNotation)); //1234567.12345678894780576229095458984375
System.out.println(new BigDecimal(bigD)); //56789123456789.125
//BigDecimal.valueOf (Dont use if the range is >= 10E-3, >= 10E7), under the hood it's using Double.toString
System.out.println(BigDecimal.valueOf(smallD)); //0.00010 - notice the extra 0, stemming from 1.0E-4
System.out.println(BigDecimal.valueOf(smallDNoScientificNotation)); //0.001
System.out.println(BigDecimal.valueOf(normalD)); //10.345678
System.out.println(BigDecimal.valueOf(bigDNoScientificNotation)); //1234567.123456789
System.out.println(BigDecimal.valueOf(bigD)); //56789123456789.125 //loss of accuracy
Computerized scientific notation - more here.
BONUS 1 - Pitfalls
Here
BONUS 2 - Effective Java 3rd edition (Joshua Bloch)
Item 60: Avoid float or double if exact answers are required
The float and double types are particularly ill-suited for monetary calculations because it is impossible to represent 0.1 (or any other negative power of ten) as a float or double exactly.
:
There are, however, two disadvantages to using BigDecimal: it's a lot less convenient than using a primitive arithmetic type, and it's a lot slower. The latter disadvantage is irrelevant if you're solving a single short problem, but the former may annoy you.
:
An alternative to using BigDecimal is to use int or long, depending on the amounts involved, and to keep track of the decimal point yourself. In this example, the obvious approach is to do all computation in cents.
Extra reading for the mathematically inclined ;-)
What Every Computer Scientist Should Know About Floating-Point Arithmetic

Categories

Resources