I don't know why this happens:
float flo = 196.7f;
BigDecimal bd = new BigDecimal(flo);
System.out.println(bd); //print 196.6999969482421875
BigDecimal bd2 = new BigDecimal(Float.toString(flo));
System.out.println(bd2); //print 196.7
Both your questions are clearly stated 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.
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.
Related
I have following two BigDecimal objects.
BigDecimal one = new BigDecimal(3.0);
BigDecimal two = new BigDecimal(3.00);
System.out.println(one.scale());//0
System.out.println(two.scale());//0
System.out.println(one.equals(two));//true
I've read JavaDocs, but anywhere can't understand what is difference between equals and compareTo method. JavaDoc says that these objects isn't equal by equals method and result must be false, but result is true. I'm confused.
You need to use the String constructor to get the correct scales, because the BigDecimal(double) will get the small scale possible
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.
More precision about the documentations :
BigDecimal.equals(Object)
Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
BigDecimal.compareTo(BigDecimal)
Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) 0), where is one of the six comparison operators.
You will find that the equals use the scale for the comparison, giving some "strange" result.
BigDecimal bd1 = new BigDecimal("2"); //scale 0
BigDecimal bd2 = new BigDecimal("2.00"); //scale 2
bd1.equals(bd2); //false
bd1.compareTo(bd2); //0 => which means equivalent
It's better to use compareTo for BigDecimal. This method will return a number greater than zero if a > b, 0 if a == b, and less than zero if a < b
Change new BigDecimal(3.0); to new BigDecimal("3.0"); and you will see the difference.
You used the constructor new BigDecimal(double val) with a double-literal which value is just 3 it doesn't matter if you write 3.0 or 3.0000.
Also be careful with this constructor since it represents the exact decimal representation of the double's binary floating-point value, therefor new BigDecimal(0.1) creates a BigDecimal with the value 0.1000000000000000055511151231257827021181583404541015625 (See JavaDoc)
Better (always) use the constructor new BigDecimal(String val).
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
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
I know the following behavior is an old problem, but still I don't understand.
System.out.println(0.1 + 0.1 + 0.1);
Or even though I use BigDecimal
System.out.println(new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue());
Why this result is: 0.30000000000000004 instead of: 0.3?
How can I solve this?
What you actually want is
new BigDecimal("0.1")
.add(new BigDecimal("0.1"))
.add(new BigDecimal("0.1"));
The new BigDecimal(double) constructor gets all the imprecision of the double, so by the time you've said 0.1, you've already introduced the rounding error. Using the String constructor avoids the rounding error associated with going via the double.
First never, never use the double constructor of BigDecimal. It may be the right thing in a few situations but mostly it isn't
If you can control your input use the BigDecimal String constructor as was already proposed. That way you get exactly what you want. If you already have a double (can happen after all), don't use the double constructor but instead the static valueOf method. That has the nice advantage that we get the cannonical representation of the double which mitigates the problem at least.. and the result is usually much more intuitive.
This is not a problem of Java, but rather a problem of computers generally. The core problem lies in the conversion from decimal format (human format) to binary format (computer format). Some numbers in decimal format are not representable in binary format without infinite repeating decimals.
For example, 0.3 decimal is 0.01001100... binary But a computer has a limited "slots" (bits) to save a number, so it cannot save all the whole infinite representation. It saves only
0.01001100110011001100 (for example). But that number in decimal is no longer 0.3, but 0.30000000000000004 instead.
Try this:
BigDecimal sum = new BigDecimal(0.1).add(new BigDecimal(0.1)).add(new BigDecimal(0.1));
EDIT: Actually, looking over the Javadoc, this will have the same problem as the original. The constructor BigDecimal(double) will make a BigDecimal corresponding to the exact floating-point representation of 0.1, which is not exactly equal to 0.1.
This, however, gives the exact result, since integers CAN always be expressed exactly in floating-point representation:
BigDecimal one = new BigDecimal(1);
BigDecimal oneTenth = one.divide(new BigDecimal(10));
BigDecimal sum = oneTenth.add(oneTenth).add(oneTenth);
The problem you have is that 0.1 is represented with a slightly higher number e.g.
System.out.println(new BigDecimal(0.1));
prints
0.1000000000000000055511151231257827021181583404541015625
The Double.toString() takes into account this representation error so you don't see it.
Similarly 0.3 is represented by a value slightly lower than it really is.
0.299999999999999988897769753748434595763683319091796875
If you multiply the represented value of 0.1 by 3 you don't get the represented value for 0.3, you instead get something a little higher
0.3000000000000000166533453693773481063544750213623046875
This is not just a representation error but also a rounding error caused by the operations. This is more than the Double.toString() will correct and so you see the rounding error.
The moral of the story, if you use float or double also round the solution appropriately.
double d = 0.1 + 0.1 + 0.1;
System.out.println(d);
double d2 = (long)(d * 1e6 + 0.5) / 1e6; // round to 6 decimal places.
System.out.println(d2);
prints
0.30000000000000004
0.3
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