Reputation: 338
I am trying to convert a float to BigDecimal then do some maths. After that i would require the final results in Float.
From what I known, the only safe way to get a float to become a BigDecimal number is to use the string constructor.
I have two approaches but both didnt work. Both gives me an additional 0.000001 value
Approach #1:
Float num1 = 13.846154f;
BigDecimal calculation = new BigDecimal(Float.toString(num1));
BigDecimal num2 = new BigDecimal("2.0").add(calculation);
Float finalResults = num2.floatValue();
Approach #2:
Float num1 = 13.846154f;
BigDecimal calculation = new BigDecimal(Float.toString(num1));
BigDecimal num2 = new BigDecimal("2.0").add(calculation);
Float finalResults = Float.parseFloat(num2.toString());
Is there a way to safely do the math and not lose precision when i convert the number back to float?
Edit #1 I did some testing and found something weird.
Float original = 13.846154f;
BigDecimal originalNum = new BigDecimal(Float.valueOf(original));
BigDecimal numTwo = new BigDecimal("14.0");
BigDecimal numThree = new BigDecimal("20.0");
BigDecimal numFour = new BigDecimal("30.0");
numTwo = numTwo.add(originalNum);
numThree = numThree.add(originalNum);
numFour = numFour.add(originalNum);
It is still adding that extra 0.000001..
Upvotes: 4
Views: 9143
Reputation: 26185
The new BigDecimal(double)
constructor is exact, as is the conversion from float
to double
, so if you start with a float
value num1
, new BigDecimal(num1)
will give the exact BigDecimal
equivalent.
It may not be quite what you expect. For example, the closest float
to 13.846154 is 13.84615421295166015625, so that is the initial value of num1
and of calculation
.
The BigDecimal
addition of 2 is exact, giving 15.84615421295166015625.
The next problem is turning it into a float
for further calculation. As it happens, it is exactly representable as a float
, so there is no further loss of precision. In general, converting a BigDecimal
to float
will involve rounding to the nearest float
.
It turns out that all the loss of precision in this calculation happened on the initialization of num1
. If you do it this way, without all the conversions to and from String, you will get as close as you can, given initial use of float
.
If float
is not precise enough for your calculations, but you need a float
to pass to another method, consider using either BigDecimal
or double
for the whole calculation except for the final step. BigDecimal
's floatValue()
does return the closest float
to the exact value of the BigDecimal
.
There is one case in which it is advantageous to use String
. If you know you want a BigDecimal to be an exact decimal fraction, constructing the BigDecimal
from the String representation of the fraction gets it exactly. Writing something like new BigDecimal(1.0)
gets a BigDecimal
whose value is 0.1000000000000000055511151231257827021181583404541015625, the value of the closest double to 0.1. If you have already converted to float
or double
, converting via String
will either make no difference or cause loss of precision. It will not help.
Upvotes: 2
Reputation: 533500
If you want to avoid losing any precision, you have to use BigDeciml all the way, using a little bit of BigDecimal is likely to be pointless.
I suggest using double
, it has one billion times the accuracy (Really it is ~10^9 *)
If you are going to use float
don't use Float
as this just adds overhead for no benefit.
If you do 13.846154
this is already more precision than float
supports.
System.out.println(13.846155f);
System.out.println(23.846155f);
System.out.println(33.846155f);
prints
13.846155
23.846155
33.846153 <- a representation error with this many digits.
Instead I suggest you try
double finalResult = 13.846154 + 2.0;
btw
System.out.println(13.846154+ 2.0);
prints
15.846154
Upvotes: 2