1

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]
JavaGeek
  • 335
  • 1
  • 2
  • 11
  • 2
    You have some minor problems with your code, but the main issue is that this task is mathematically impossible unless you have some pre-known limits for possible values of numerator and denominator. For any fraction there is infinite number of other fractions which have the same BigDecimal representation up to any given precision. – Alex Sveshnikov Dec 16 '21 at 20:57
  • Does this answer your question? [How to convert floats to human-readable fractions?](https://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions) – JohanC Dec 16 '21 at 21:07
  • See also [Generate Continued Fractions for a float](https://stackoverflow.com/questions/4637967/algorithm-challenge-generate-continued-fractions-for-a-float) – JohanC Dec 16 '21 at 21:09
  • BigDecimal cannot precisely represent 1/3, which takes an infinite number of decimal digits, so you can't get that fraction back out again. – Louis Wasserman Dec 16 '21 at 22:12

1 Answers1

3

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.

MvG
  • 57,380
  • 22
  • 148
  • 276