thhVictor
thhVictor

Reputation: 338

Converting float to BigDecimal then back to float

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

Answers (2)

Patricia Shanahan
Patricia Shanahan

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

Peter Lawrey
Peter Lawrey

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

Related Questions