1. Introduction

A fraction is another way of representing a number, consisting of a numerator and a denominator. For example, the fraction 3/5 can be thought of as “3 out of 5,” representing the same value as the decimal number 0.6. In this tutorial, we’ll explore different approaches to convert decimal numbers to fractions in Java.

2. Using Multiplying With a Power of 10

One simple way to convert a decimal to a fraction is to multiply the decimal by a power of 10 and then use the resulting numerator and denominator as the fraction.

Here is the code snippet for this approach:

String convertDecimalToFractionUsingMultiplyingWithPowerOf10(double decimal) {
    String decimalStr = String.valueOf(decimal);
    int decimalPlaces = decimalStr.length() - decimalStr.indexOf('.') - 1;
    
    long denominator = (long) Math.pow(10, decimalPlaces);
    long numerator = (long) (decimal * denominator);

    return numerator + "/" + denominator;
}

First, we calculate the number of decimal places by finding the position of the decimal point in the string and subtracting it from the length of the string. Then we compute the denominator by raising 10 to the power of the number of decimal places.

Next, we multiply the original decimal number with the denominator to determine the numerator. The method returns a string representation of the fraction by concatenating the numerator and denominator with a slash (/) separator.

Let’s validate our solution using assertEquals():

assertEquals("5/10", convertDecimalToFractionUsingMultiplyingWithPowerOf10(0.5));
assertEquals("1/10", convertDecimalToFractionUsingMultiplyingWithPowerOf10(0.1));
assertEquals("6/10", convertDecimalToFractionUsingMultiplyingWithPowerOf10(0.6));
assertEquals("85/100", convertDecimalToFractionUsingMultiplyingWithPowerOf10(0.85));
assertEquals("125/100", convertDecimalToFractionUsingMultiplyingWithPowerOf10(1.25));
assertEquals("1333333333/1000000000", convertDecimalToFractionUsingMultiplyingWithPowerOf10(1.333333333));

This approach is simple and easy to implement, but it has some limitations. The simple conversion might not produce the most reduced form of the fraction. For instance, converting 0.5 using this method would result in 5/10, which can be simplified to 1/2.

3. Using the Greatest Common Divisor (GCD)

A more robust way to convert a decimal to a fraction is to use the Greatest Common Divisor (GCD) to simplify the fraction. The GCD is the largest number that divides both the numerator and denominator without leaving a remainder.

First, we create a gcd() method to calculate the greatest common divisor of two integers a and b using the Euclidean algorithm:

long gcd(long a, long b) {
    if (b == 0) {
        return a;
    } else {
        return gcd(b, a % b);
    }
}

Inside the method, we check:

  • If b is 0, the GCD is a.
  • Otherwise, the GCD is calculated recursively by finding the GCD of b and the remainder of a divided by b.

Next, we create a method to apply the gcd() method to simplify the fraction:

String convertDecimalToFractionUsingGCD(double decimal) {
    String decimalStr = String.valueOf(decimal);
    int decimalPlaces = decimalStr.length() - decimalStr.indexOf('.') - 1;
    long denominator = (long) Math.pow(10, decimalPlaces);
    long numerator = (long) (decimal * denominator);

    long gcd = gcd(numerator, denominator);
    numerator /= gcd;
    denominator /= gcd;

    return numerator + "/" + denominator;
}

Similar to the previous approach, we calculate the number of decimal places to determine the denominator. This is followed by multiplying the original decimal number with the denominator to determine the numerator.

Then we apply the gcd() method to calculate the GCD of the numerator and denominator. Afterward, we simplify the fraction by dividing both the numerator and denominator by the gcd. This ensures that the fraction is in its simplest form.

Finally, the method returns the simplified fraction as a string by concatenating the numerator and denominator with a slash (/) separator:

assertEquals("1/2", convertDecimalToFractionUsingGCD(0.5));
assertEquals("1/10", convertDecimalToFractionUsingGCD(0.1));
assertEquals("3/5", convertDecimalToFractionUsingGCD(0.6));
assertEquals("17/20", convertDecimalToFractionUsingGCD(0.85));
assertEquals("5/4", convertDecimalToFractionUsingGCD(1.25));
assertEquals("1333333333/1000000000", convertDecimalToFractionUsingGCD(1.333333333));

While this approach is efficient for finding GCD, it can become computationally expensive for very large numbers. This is because each recursive call involves calculating the modulo operation (%), which can be slow for large inputs.

4. Using Apache Commons Math

Lastly, we can also use a library like Apache Commons Math to convert a decimal to a fraction. In this case, we’re leveraging the Fraction class from Apache Commons Math to convert a decimal to a fraction:

String convertDecimalToFractionUsingApacheCommonsMath(double decimal) {
    Fraction fraction = new Fraction(decimal);
    return fraction.toString();
}

To convert a decimal to a fraction, we create a Fraction object by passing the decimal value to its constructor. Once we have the Fraction object representing the decimal value, we can call its toString() method to get the string representation of the fraction.

The toString() method returns the fraction in the form “numerator / denominator”:

assertEquals("1 / 2", convertDecimalToFractionUsingApacheCommonsMath(0.5));
assertEquals("1 / 10", convertDecimalToFractionUsingApacheCommonsMath(0.1));
assertEquals("3 / 5", convertDecimalToFractionUsingApacheCommonsMath(0.6));
assertEquals("17 / 20", convertDecimalToFractionUsingApacheCommonsMath(0.85));
assertEquals("5 / 4", convertDecimalToFractionUsingApacheCommonsMath(1.25));
assertEquals("4 / 3", convertDecimalToFractionUsingApacheCommonsMath(1.333333333));

5. Handle Repeating Decimal

We may observe that the results computed from applying the first two methods and the Apache Commons Math library to the decimal value 1.333333333 differ. This is because they handle repeating decimals differently.

Repeating decimals are decimal numbers that have a sequence of digits that repeat indefinitely after the decimal point. For example, the decimal number 1.333333333 has a repeating sequence of the digit 3 after the decimal point.

To convert a repeating decimal to a fraction, we first identify the repeating sequence of digits that appears indefinitely after the decimal point:

String extractRepeatingDecimal(String fractionalPart) {
    int length = fractionalPart.length();
    for (int i = 1; i <= length / 2; i++) {
        String sub = fractionalPart.substring(0, i);
        boolean repeating = true;
        for (int j = i; j + i <= length; j += i) {
            if (!fractionalPart.substring(j, j + i).equals(sub)) {
                repeating = false;
                break;
            }
        }
        if (repeating) {
            return sub;
        }
    }
    return "";
}

Next, we enhance the convertDecimalToFractionUsingGCD() to handle the repeating decimal:

String convertDecimalToFractionUsingGCDRepeating(double decimal) {
    String decimalStr = String.valueOf(decimal);
    int indexOfDot = decimalStr.indexOf('.');
    String afterDot = decimalStr.substring(indexOfDot + 1);
    String repeatingNumber = extractRepeatingDecimal(afterDot);

    if (repeatingNumber == "") {
        return convertDecimalToFractionUsingGCD(decimal);
    } else {
        //...
    }
}

Once a repeating decimal is detected, we proceed by determining several key attributes:

int n = repeatingNumber.length();
int repeatingValue = Integer.parseInt(repeatingNumber);
int integerPart = Integer.parseInt(decimalStr.substring(0, indexOfDot));
int denominator = (int) Math.pow(10, n) - 1;
int numerator = repeatingValue + (integerPart * denominator);
  • n: The length of digits in the repeating sequence.
  • repeatingValue: The numerical value of the repeating digits.
  • integerPart: The whole number extracted from the decimal before the decimal point
  • denominator: The denominator is derived by raising 10 to the power of n and subtracting 1
  • numerator: The numerator is calculated by summing the repeatingValue with the multiplication of the integerPart and the denominator.

Next, we can apply the gcd() approach to calculate the GCD for the numerator and denominator:

int gcd = gcd(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
return numerator + "/" + denominator;

Now, let’s verify our solution to handle repeating decimals:

assertEquals("1/2", convertDecimalToFractionUsingGCDRepeating(0.5));
assertEquals("17/20", convertDecimalToFractionUsingGCDRepeating(0.85));
assertEquals("5/4", convertDecimalToFractionUsingGCDRepeating(1.25));
assertEquals("4/3", convertDecimalToFractionUsingGCDRepeating(1.333333333));
assertEquals("7/9", convertDecimalToFractionUsingGCDRepeating(0.777777));

For the test case 1.333333333, we identify that the repeating digit repeatingValue is 3. This means that the digit “3” repeats indefinitely after the decimal point. The length of the repeating sequence is 1, which indicates that the repeating pattern consists of a single-digit repetition.

Next, we determine the denominator by raising 10 to the power of n and subtracting 1, which would be 10^1 – 1 = 9. The numerator is calculated by adding the repeatingValue to the multiplication of the integerPart with the denominator, which would be 3 + (1 * 9) = 12.

Up to this step, the fraction would be 12/9. After we applied the gcd() method to simplify the fraction, we got back the result of 4/3. 

Additionally, it’s important to note that this enhancement may not work as expected for decimals that contain both repeating and non-repeating parts, such as 0.1123123123.

6. Conclusion

In this article, we’ve explored several methods for converting decimal to fractions. For most cases, using the GCD approach provides a good balance between simplicity and ensuring a simplified fraction.

As always, the source code for the examples is available over on GitHub.