36

I'm looking for a Java library which represents fractions (rational numbers). For example, if I want to store the fraction 1/3 then it will not be saved as 0.33333 which will lose its accuracy.

Here is some of the functionality I expect finding in such a library:

  • getNumerator()
  • getDenominator()
  • add(Rational r1, Rational r2), subtract(Rational r1, Rational r2), multiply(Rational r1, Rational r2), divide(Rational r1, Rational r2)
  • isProper()
  • getCommonDenominator(Collection<Rational> rationals)
  • getSimplified()

I can implement such a library by myself, though I was wondering whether something similar already exists.

EDIT: It would also be nice if the library implements (in addition to the above) some number theory algorithms, such as getEgyptianFractionsSum() etc.

snakile
  • 52,936
  • 62
  • 169
  • 241

6 Answers6

18

Does Apache Commons Math suit you?

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
Petar Minchev
  • 46,889
  • 11
  • 103
  • 119
14

The JScience library includes the class org.jscience.mathematics.number.Rational. In addition to the usual factories, accessors and operations, one can construct other useful entities, including Polynomial<Rational>, Vector<Rational> and Matrix<Rational>.

As an example, a function to obtain the lowest common denominator of a collection of fractions might look like this:

private static LargeInteger lcd(Collection<Rational> fractions) {
    Rational sum = Rational.ZERO;
    for (Rational rational : fractions) {
        sum = sum.plus(rational);
    }
    return sum.getDivisor();
}

The following statement prints 6:

System.out.println(lcd(Arrays.asList(
    Rational.valueOf(1, 2), Rational.valueOf(1, 3))));
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 1
    Strictly speaking, your example would in general not return the lowest common denominator, but just the denominator of the sum. E.g. for `System.out.println(lcd(Arrays.asList(Rational.valueOf(1, 2), Rational.valueOf(1, 2))));` it returns 1, although 2 would be the lowest common denominator (1/2 + 1/2 = 2/2 = 1/1). – Alex1357 Jan 15 '21 at 09:28
2

Edit from Future: Please use Apache Commons or any other supported library. This was just a sample demonstration.

I implemented a small class that can be used for that purposes, maybe it can be useful for you as well, use with caution.

import java.util.ArrayList;

public class RationalNumber {

    /**
     *
     * @author Suat KARAKUSOGLU
     * @email  suatkarakusoglu@gmail.com
     * This class has 2 kind of constructors
     * 1. is RationalNumber a=new RationalNumber("3.3");
     *       RationalNumber a=new RationalNumber("-3.3");
     * With this constructor one can enter the decimal number and also specify whether negative or not
     *
     * 2. is RationalNumber a=new RationalNumber(3,5);
     * With this constructor the first value is nominator and second one is denominator.
     *
     * The advantage side of this class is, it prevents the fractional errors while dividing
     * RationalNumber keeps all denominator and nominator values as it is and when the real value is
     * needed, the calculation occurs at that time.
     *
     * Supports multiply,divide,add,subtract operations on RationalNumber classes.
     *
     */


    /*
     * Simple Usage:
     *
     * RationalNumber a=new RationalNumber("3.3");
     * RationalNumber b=new RationalNumber("4.5");
     * System.out.println("a ="+a.getStringValue());
     * System.out.println("b ="+b.getStringValue());
     * System.out.println("a-b ="+a.subtract(b).getStringValue());
     * System.out.println("a ="+a.getStringValue());
     * System.out.println("b ="+b.getStringValue());
     * RationalNumber k=a.divide(b);
     * System.out.println("a/b="+k.getStringValue());
     * System.out.println("a/b="+k.getDoubleValue());
     *
     * System out results:
     *
     * a =33/10
     * b =9/2
     * a-b =-6/5
     * a =33/10
     * b =9/2
     * a/b=11/15
     * a/b=0.7333333333333333
     *
     */

    public ArrayList<Long> nominators = new ArrayList<Long>();
    public ArrayList<Long> denominators = new ArrayList<Long>();

    public RationalNumber(String rationalNumberStringValue) {
        this(parseRationalNumberStringValue(rationalNumberStringValue)[0],
                parseRationalNumberStringValue(rationalNumberStringValue)[1]);

    }

    private static Long[] parseRationalNumberStringValue(
            String rationalNumberStringValue) {

        boolean positive = true;
        if (rationalNumberStringValue.charAt(0) == '-') {
            positive = false;
            rationalNumberStringValue = rationalNumberStringValue.substring(1);
        }

        // 0. index is keeping nominator
        // 1. index is keeping denominator
        Long[] nominatorDenominator = new Long[2];
        nominatorDenominator[0] = 1l;
        nominatorDenominator[1] = 1l;

        String[] splittedNumberArr = rationalNumberStringValue.split("\\.");
        String denominatorStr = splittedNumberArr[1];

        for (int i = 0; i < denominatorStr.length(); i++) {
            nominatorDenominator[1] *= 10;
        }

        rationalNumberStringValue = removeCharAt(rationalNumberStringValue,
                rationalNumberStringValue.indexOf('.'));
        nominatorDenominator[0] = Long.valueOf(rationalNumberStringValue);
        if (!positive) {
            nominatorDenominator[0] *= -1;
        }
        return nominatorDenominator;

    }

    public static String removeCharAt(String s, int pos) {
        return s.substring(0, pos) + s.substring(pos + 1);
    }

    public RationalNumber(Integer nominator, Integer denominator) {

        this((long) nominator, (long) denominator);

    }

    public RationalNumber(Long nominator, Long denominator) {

        nominators.add(nominator);
        denominators.add(denominator);
        simplify();

    }

    public RationalNumber(ArrayList<Long> nominatorList,
            ArrayList<Long> denominatorList) {

        nominators.addAll(nominatorList);
        denominators.addAll(denominatorList);
        simplify();

    }

    public String getStringValue() {
        return getMultipliedValue(this.nominators) + "/"
                + getMultipliedValue(this.denominators);
    }

    public double getDoubleValue() {
        return (double) getMultipliedValue(this.nominators)
                / (double) getMultipliedValue(this.denominators);
    }

    public RationalNumber multiply(RationalNumber rationalNumberToMultiply) {

        RationalNumber mulResult = new RationalNumber(
                rationalNumberToMultiply.nominators,
                rationalNumberToMultiply.denominators);
        mulResult.nominators.addAll(this.nominators);
        mulResult.denominators.addAll(this.denominators);

        return RationalNumber.simplifyRationalNumber(mulResult);
    }

    public RationalNumber divide(RationalNumber rationalNumberToDivide) {

        RationalNumber divideResult = new RationalNumber(
                rationalNumberToDivide.nominators,
                rationalNumberToDivide.denominators);

        // division means multiplication with reverse values
        ArrayList<Long> tempLongList = divideResult.nominators;
        divideResult.nominators = divideResult.denominators;
        divideResult.denominators = tempLongList;

        return this.multiply(divideResult);

    }

    public RationalNumber add(RationalNumber rationalNumberToAdd) {

        rationalNumberToAdd = RationalNumber
                .simplifyRationalNumber(rationalNumberToAdd);

        return new RationalNumber(
                (getMultipliedValue(this.nominators) * getMultipliedValue(rationalNumberToAdd.denominators))
                        + (getMultipliedValue(this.denominators) * getMultipliedValue(rationalNumberToAdd.nominators)),
                (getMultipliedValue(this.denominators) * getMultipliedValue(rationalNumberToAdd.denominators)));

    }

    public RationalNumber subtract(RationalNumber rationalNumberToSubtract) {

        rationalNumberToSubtract = RationalNumber
                .simplifyRationalNumber(rationalNumberToSubtract);

        RationalNumber subtractTempRational = new RationalNumber(
                rationalNumberToSubtract.nominators,
                rationalNumberToSubtract.denominators);

        // Multiply one of its nominators negative value
        subtractTempRational.nominators.set(0,
                (subtractTempRational.nominators.get(0) * -1));

        // add with its negative value
        return this.add(subtractTempRational);

    }

    private long getMultipliedValue(ArrayList<Long> longList) {
        Long mulResult = 1l;
        for (Long tempLong : longList) {
            mulResult *= tempLong;
        }
        return mulResult;
    }

    // simplifies original rationalnumber
    public void simplify() {
        long tempGcd = 1;
        long iValue = 1;
        long jValue = 1;
        for (int i = 0; i < this.nominators.size(); i++) {
            iValue = this.nominators.get(i);
            for (int j = 0; j < this.denominators.size(); j++) {
                jValue = this.denominators.get(j);
                tempGcd = gcd(iValue, jValue);
                this.nominators.set(i, iValue / tempGcd);
                this.denominators.set(j, jValue / tempGcd);
            }
        }
    }

    public static RationalNumber simplifyRationalNumber(
            RationalNumber rationalNumberToSimplify) {
        long tempGcd = 1;
        long iValue = 1;
        long jValue = 1;
        for (int i = 0; i < rationalNumberToSimplify.nominators.size(); i++) {
            for (int j = 0; j < rationalNumberToSimplify.denominators.size(); j++) {
                iValue = rationalNumberToSimplify.nominators.get(i);
                jValue = rationalNumberToSimplify.denominators.get(j);
                tempGcd = gcd(iValue, jValue);
                rationalNumberToSimplify.nominators.set(i, iValue / tempGcd);
                rationalNumberToSimplify.denominators.set(j, jValue / tempGcd);
            }
        }
        return rationalNumberToSimplify;
    }

    // Euclidean algorithm to find greatest common divisor
    public static long gcd(long a, long b) {

        a = Math.abs(a);
        b = Math.abs(b);

        if (a < b) {
            long temp = a;
            a = b;
            b = temp;
        }

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

    public RationalNumber add(int integerToAdd) {

        RationalNumber tempRationalNumber=new RationalNumber(integerToAdd,1);
        return this.add(tempRationalNumber);
    }
    public RationalNumber subtract(int integerToSubtract) {

        RationalNumber tempRationalNumber=new RationalNumber(integerToSubtract,1);
        return this.subtract(tempRationalNumber);
    }
    public RationalNumber multiply(int integerToMultiply) {

        RationalNumber tempRationalNumber=new RationalNumber(integerToMultiply,1);
        return this.multiply(tempRationalNumber);
    }
    public RationalNumber divide(int integerToDivide) {

        RationalNumber tempRationalNumber=new RationalNumber(integerToDivide,1);
        return this.divide(tempRationalNumber);
    }



}
Suat KARAKUSOGLU
  • 622
  • 6
  • 14
  • 2
    How does this several hundred lines long class qualify as "small"? ò.O – Emily L. Nov 18 '16 at 10:31
  • I wouldn't expect such a library to be necessarily small, but in order to qualify as a library, it should be published somewhere "officially" instead of just in a Stack Overflow answer. – Roland Illig Jun 19 '20 at 19:29
  • You are absolutely right Roland!, I didn't claim that this is a library of some kind of, also i warned about the usage :), it was just a tryout solution that i have written to show how this can be accomplished many years ago. I have edited the answer for people to be more careful about the usage. – Suat KARAKUSOGLU Jun 20 '20 at 20:58
1

I'm not sure how commonly used it is, but the apfloat packages (Java and C++) contain a class for rational arithmetic.

hardmath
  • 8,753
  • 2
  • 37
  • 65
1

The Apfloat library has lots of great features, performance, accuracy and so on. Its definitely a better BigDecimal which to be fair works but is quite simple and offers little functionality.

http://www.apfloat.org/apfloat_java/

Contents:

Classpath setting First example Constructing Apfloats Double and float constructor caveats Apfloats are immutable Precision Output Advanced mathematical functions Integers Complex numbers Rational numbers Using some other radix than 10 Equality and comparison Formatting

mP.
  • 18,002
  • 10
  • 71
  • 105
0

You may want to try RationalJ, which is a Java library for rational numbers.