4

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

thhVictor
  • 338
  • 6
  • 25

2 Answers2

2

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
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Is there a way to cast BigDecimal back to float? I need float because im actually using a method that takes in float.. – thhVictor May 16 '14 at 19:37
  • 1
    @thhVictor In that case, just use a `float` all the way. Using a `Float` or a `BigDecimal` won't help as `float` isn't very accurate. To answer your question, you can use BigDecimal.floatValue() to extract the nearest representable values as a `float` – Peter Lawrey May 17 '14 at 07:57
2

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.

Patricia Shanahan
  • 25,849
  • 4
  • 38
  • 75
  • I have read a post somewhere saying using string will prevent all the precision loss by using BigDecimal addition method. However, what i require is to input a float number and do the calculate in BigDecimal then return me float. Is there a way you would suggest me doing so? – thhVictor May 16 '14 at 20:50
  • Note that there are calculations that cannot be done exactly in BigDecimal. For example, one third cannot be represented exactly. If your calculations are all exactly representable in finite decimal fractions, use BigDecimal, and take its floatValue when you need the closest float. – Patricia Shanahan May 16 '14 at 21:22
  • Then is there a way for me to do maths in java without losing precision? – thhVictor May 17 '14 at 06:52
  • Not in all cases. Ultimately, there is no way to map all the real numbers to any finite length representation. Can you be specific about the calculations you need to do? That would help in selecting the best approach. The choices include double, which supports greater precision than the most accurate physical measurement that has ever been done, BigDecimal that can exactly represent every short decimal fraction, and rational number packages that can exactly represent any ratio of integers. – Patricia Shanahan May 17 '14 at 08:14
  • I am getting my data from a build in method that returns the data in float and I am trying to calculate the results then return the results in float. The only way i saw was convert the float to BigDecimal via string constructor then do the calculation. After that I would have to convert the BigDecimal back to float so I can parse the data back to another method which takes in float as well. – thhVictor May 17 '14 at 10:25
  • If you are getting a float, transforming it, and then passing the result as a float, forget the idea of perfect accuracy. Short decimal fractions have no particular significance, so BigDecimal is irrelevant. The only question is whether float is precise enough for your intermediate results, or should you use double. I tend to use double on general principles. – Patricia Shanahan May 17 '14 at 13:20
  • As I already pointed out, you can get the exact value of a float as a BigDecimal by using the BigDecimal(double) constructor. Conversion to String may cause rounding. – Patricia Shanahan May 17 '14 at 13:21