BigDecimal behavior in Java - 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

Related

Why does nextUp method in Math class skips some values?

I was just messing around with this method to see what it does. I created a variable with value 3.14 just because it came to my mind at that instance.
double n = 3.14;
System.out.println(Math.nextUp(n));
The preceding displayed 3.1400000000000006.
Tried with 3.1400000000000001, displayed the same.
Tried with 333.33, displayed 333.33000000000004.
With many other values, it displays the appropriate value for example 73.6 results with 73.60000000000001.
What happens to the values in between 3.1400000000000000 and 3.1400000000000006? Why does it skips some values? I know about the hardware related problems but sometimes it works right. Also even though it is known that precise operations cannot be done, why is such method included in the library? It looks pretty useless due to the fact that it doesn't work always right.
One useful trick in Java is to use the exactness of new BigDecimal(double) and of BigDecimal's toString to show the exact value of a double:
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
System.out.println(new BigDecimal(3.14));
System.out.println(new BigDecimal(3.1400000000000001));
System.out.println(new BigDecimal(3.1400000000000006));
}
}
Output:
3.140000000000000124344978758017532527446746826171875
3.140000000000000124344978758017532527446746826171875
3.1400000000000005684341886080801486968994140625
There are a finite number of doubles, so only a specific subset of the real numbers are the exact value of a double. When you create a double literal, the decimal number you type is represented by the nearest of those values. When you output a double, by default, it is shown as the shortest decimal fraction that would round to it on input. You need to do something like the BigDecimal technique I used in the program to see the exact value.
In this case, both 3.14 and 3.1400000000000001 are closer to 3.140000000000000124344978758017532527446746826171875 than to any other double. The next exactly representable number above that is 3.1400000000000005684341886080801486968994140625
Floating point numbers are stored in binary: the decimal representation is just for human consumption.
Using Rick Regan's decimal to floating point converter 3.14 converts to:
11.001000111101011100001010001111010111000010100011111
and 3.1400000000000006 converts to
11.0010001111010111000010100011110101110000101001
which is indeed the next binary number to 53 significant bits.
Like #jgreve mentions this has to do due to the use of float & double primitives types in java, which leads to the so called rounding error. The primitive type int on the other hand is a fixed-point number meaning that it is able to "fit" within 32-bits. Doubles are not fixed-point, meaning that the result of double calculations must often be rounded in order to fit back into its finite representation, which leads sometimes (as presented in your case) to inconsistent values.
See the following two links for more info.
https://stackoverflow.com/a/322875/6012392
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
A work around could be the following two, which gives a "direction" to the first double.
double n = 1.4;
double x = 1.5;
System.out.println(Math.nextAfter(n, x));
or
double n = 1.4;
double next = n + Math.ulp(n);
System.out.println(next);
But to handle floating point values it is recommended to use the BigDecimal class

Why BigDecimal constructor instance with differents values?

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.

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 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).

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