I have a list of fractions that have to be converted to a bigDecimal first and then converted to a fraction again. I have made two methods: first converts from fraction to bigDecimal and the second does the opposite. However, I have noticed that during the conversion I am losing on some precision, hence I was wondering if there is a way to restore a fraction to its initial state with 100% confidence, assuming that I can store any amount of decimals.
Here is my code, and on the prints, you can notice that I am losing precision on very small fractions.
package Docs; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.math3.fraction.BigFraction; public class FractionalApproximation{ private static BigDecimal fractionToPercentage(String fraction) { final String[] split = fraction.split("/"); final float numerator = Float.valueOf(split[0]); final float denominator = Float.valueOf(split[1]); return new BigDecimal((numerator / denominator) * 100).setScale(3, BigDecimal.ROUND_HALF_DOWN); } private static String percentageToFraction(double share){ double percentage = share / 100; BigFraction bigFraction = new BigFraction(percentage, 0.002D, 1000); return bigFraction.getNumerator() + "/" + bigFraction.getDenominator(); } public static void main(String[] args) { List<String> initialFractions = new ArrayList<String>(Arrays.asList("1/3","1/112","1/6","1/1","1/56","1/224", "1/448", "4/448")); System.out.println(initialFractions); List<BigDecimal> percentageResultList = new ArrayList<BigDecimal>(); for(String fraction : initialFractions){ percentageResultList.add(fractionToPercentage(fraction)); } System.out.println(percentageResultList); List<String> fractionResultList = new ArrayList<String>(); for(BigDecimal value : percentageResultList){ fractionResultList.add(percentageToFraction(value.doubleValue())); } System.out.println(fractionResultList); } } Initial fractions :[1/3, 1/112, 1/6, 1/1, 1/56, 1/224, 1/448, 4/448] Fractions converted to BigDecimal: [33.333, 0.893, 16.667, 100.000, 1.786, 0.446, 0.223, 0.893] BigDecimal converted to fraction: [1/3, 1/111, 1/6, 1/1, 1/55, 1/224, 1/448, 1/111]
Advertisement
Answer
I would recommend you look into continued fractions, an in particular read https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations. That section describes how computing continued fractions can be used to represent a given real number with increasing accuracy.
You will still have two problems to deal with.
One is that you will need to make a choice how long to continue, i.e. which of these values you feel are actually contributing detail that should be represented, and which of them are merely noise from the computation. If your big decimal is free of numerical noise, i.e. the direct and correctly rounded result of a single division of integers, then you can just compute the decimal representation of each convergent with the same number of digits, and see which of them exactly matches your input.
The other problem is that computing continued fractions requires a number of operations that would entail their own rounding errors when performed using big decimals. In particular you will need to compute a number of inversions, i.e. mapping x to 1/x. You might be better off if in the very first step you convert your big decimal to some form of big rational (i.e. a quotient of two big integers) and then perform all the steps there. Both the initial conversion and all the subsequent transformations should be exact in big rational representation.