Fractions in Java

Rashmi Patidar Oct 12, 2023
Fractions in Java

Mathematically, fractions are the parts or sections of values. When an object is broken equally in definite proportions, the value formed is called a fraction. Fractions are classified into Rational and Irrational Numbers.

In the Java programming language, there is a privilege to perform various operations over fractions like mathematical procedures. One can add, subtract, multiply, and divide over the fractional numbers.

Below is the code block that demonstrates the Rational number operations in the user-defined class.

import java.math.BigInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RationalNumber {
  public final static RationalNumber ZERO = new RationalNumber(BigInteger.ZERO, BigInteger.ONE);

  private final BigInteger numerator, denominator;

  private RationalNumber(BigInteger numerator, BigInteger denominator) {
    this.numerator = numerator;
    this.denominator = denominator;
  }

  private static RationalNumber canonical(
      BigInteger numerator, BigInteger denominator, boolean checkGcd) {
    if (denominator.signum() == 0) {
      throw new IllegalArgumentException("denominator is zero");
    }
    if (numerator.signum() == 0) {
      return ZERO;
    }
    if (denominator.signum() < 0) {
      numerator = numerator.negate();
      denominator = denominator.negate();
    }
    if (checkGcd) {
      BigInteger gcd = numerator.gcd(denominator);
      if (!gcd.equals(BigInteger.ONE)) {
        numerator = numerator.divide(gcd);
        denominator = denominator.divide(gcd);
      }
    }
    return new RationalNumber(numerator, denominator);
  }

  public static RationalNumber getInstance(long numerator, long denominator) {
    return canonical(new BigInteger("" + numerator), new BigInteger("" + denominator), true);
  }

  public static RationalNumber valueOf(String s) {
    Pattern p = Pattern.compile("(-?\\d+)(?:.(\\d+)?)?0*(?:e(-?\\d+))?");
    Matcher m = p.matcher(s);
    if (!m.matches()) {
      throw new IllegalArgumentException("Unknown format '" + s + "'");
    }
    String whole = m.group(1);
    String decimal = m.group(2);
    String exponent = m.group(3);
    String n = whole;
    if (decimal != null) {
      n += decimal;
    }
    BigInteger numerator = new BigInteger(n);
    int exp = exponent == null ? 0 : Integer.valueOf(exponent);
    int decimalPlaces = decimal == null ? 0 : decimal.length();
    exp -= decimalPlaces;
    BigInteger denominator;
    if (exp < 0) {
      denominator = BigInteger.TEN.pow(-exp);
    } else {
      numerator = numerator.multiply(BigInteger.TEN.pow(exp));
      denominator = BigInteger.ONE;
    }

    return canonical(numerator, denominator, true);
  }

  public static void main(String[] args) {
    RationalNumber r1 = RationalNumber.valueOf("3.14e4");
    RationalNumber r2 = RationalNumber.getInstance(111, 7);
    convert("r1", r1);
    convert("r2", r2);
    convert("r1 + r2", r1.add(r2));
    convert("r1 - r2", r1.subtract(r2));
    convert("r1 * r2", r1.multiply(r2));
    convert("r1 / r2", r1.divide(r2));
    convert("r2 ^ 2", r2.pow(2));
  }

  public static void convert(String name, RationalNumber r) {
    System.out.printf("%s = %s%n", name, r);
    System.out.printf("%s.negate() = %s%n", name, r.negate());
    System.out.printf("%s.invert() = %s%n", name, r.invert());
    System.out.printf("%s.intValue() = %,d%n", name, r.intValue());
    System.out.printf("%s.longValue() = %,d%n", name, r.longValue());
    System.out.printf("%s.floatValue() = %,f%n", name, r.floatValue());
    System.out.printf("%s.doubleValue() = %,f%n", name, r.doubleValue());
    System.out.println();
  }

  public RationalNumber add(RationalNumber o) {
    if (o.numerator.signum() == 0) {
      return this;
    } else if (numerator.signum() == 0) {
      return o;
    } else if (denominator.equals(o.denominator)) {
      return new RationalNumber(numerator.add(o.numerator), denominator);
    } else {
      return canonical(numerator.multiply(o.denominator).add(o.numerator.multiply(denominator)),
          denominator.multiply(o.denominator), true);
    }
  }

  public RationalNumber multiply(RationalNumber o) {
    if (numerator.signum() == 0 || o.numerator.signum() == 0) {
      return ZERO;
    } else if (numerator.equals(o.denominator)) {
      return canonical(o.numerator, denominator, true);
    } else if (o.numerator.equals(denominator)) {
      return canonical(numerator, o.denominator, true);
    } else if (numerator.negate().equals(o.denominator)) {
      return canonical(o.numerator.negate(), denominator, true);
    } else if (o.numerator.negate().equals(denominator)) {
      return canonical(numerator.negate(), o.denominator, true);
    } else {
      return canonical(numerator.multiply(o.numerator), denominator.multiply(o.denominator), true);
    }
  }

  public boolean isInteger() {
    return numerator.signum() == 0 || denominator.equals(BigInteger.ONE);
  }

  public RationalNumber negate() {
    return new RationalNumber(numerator.negate(), denominator);
  }

  public RationalNumber invert() {
    return canonical(denominator, numerator, false);
  }

  public RationalNumber pow(int exp) {
    return canonical(numerator.pow(exp), denominator.pow(exp), true);
  }

  public RationalNumber subtract(RationalNumber o) {
    return add(o.negate());
  }

  public RationalNumber divide(RationalNumber o) {
    return multiply(o.invert());
  }

  public int intValue() {
    return isInteger() ? numerator.intValue() : numerator.divide(denominator).intValue();
  }

  public long longValue() {
    return isInteger() ? numerator.longValue() : numerator.divide(denominator).longValue();
  }

  public float floatValue() {
    return (float) doubleValue();
  }

  public double doubleValue() {
    return isInteger() ? numerator.doubleValue()
                       : numerator.doubleValue() / denominator.doubleValue();
  }

  @Override
  public String toString() {
    return isInteger() ? String.format("%,d", numerator)
                       : String.format("%,d / %,d", numerator, denominator);
  }
}

In the program above, the execution starts from the main method. Firstly, a rational number gets initialized using the static valueOf() function defined in the class. It takes a string value and performs manipulations over the input String.

It returns a RationalNumber of the user-defined type. The function first checks for an input String with proper validations and patterns. The input String gets separated into exponent, decimal, and the whole part. These individual broken values combine to form a rational number.

Another way of creating a rational number is using an instanceOf() static factory function. The method internally calls the canonical function, which calls a private constructor of the class. The constructor separates and sets the values of the numerator and denominator for a specific instance. By canonical form, it simply means the standard representation form of the rational number.

In the given case, p/q is the standard or canonical form where q!=0 should always be true. The method has the logic written to satisfy the basic conditions of a rational number. It checks for the sign of numerator and denominator separately and performs operations. It checks for denominator values; if it’s zero, the operation throws an exception. It also checks for the greatest common divisor or gcd in the numerator and denominator values. The gcd function is present in the BigInteger class that returns a BigInteger value.

Moving ahead, the convert method gets called, passing the first rational number instance that gets created. This method also calls upon seven different functions over the rational number instance that gets passed.

Below, you’ll understand each of these methods better:

  • Print the rational number in the print stream. Generally, whenever an instance gets printed, the default toString method gets called. The method prints the name of the class followed by the hexadecimal representation of the memory location. But here, the function is overridden to give another implementation that represents the number in the p/q form.
  • The negate function will internally negate the rational number by calling the negate method of the BigInteger class. It will add a minus sign before the rational number.
  • The invert function internally calls the canonical method with a single difference. It internally passes the third checkGcd parameter as false. When the input value is Boolean true, the method logic simplifies the rational number by dividing the fraction by its greatest common divisor. But in this case, the need is to invert without simplification.
  • The intValue function first checks if the instance is an Integer value. Integers are numbers that can be positive, negative, or zero but cannot be fractions. So, internally it calls the divide method over numerator and denominator. The method is given in the BigInteger class and returns a BigInteger value. It also throws ArithmeticException when the denominator finds a zero value. The returned value gets transformed into an int value using intValue function.
  • The longValue function internally calls the divide function snd parse the BigInteger output to a long datatype.
  • The floatValue function provides the floating-point value of the rational number. And so as the doubleValue() function, which typecasts the output in the Double datatype.

The whole process gets called upon an initial rational number instance that is the r1 instance. A similar sequence is again repeated and gets printed for rational instance r2.

Mathematical Operations for Fractions in Java

Now add, subtract, divide, multiply, and power arithmetic operations need two operands for evaluation. So let’s discuss the methods in detail below:

  1. The add function first checks for the numerator values if they are positive, negative, or zero. If non-zero, it checks if the denominators of both the rational number are the same. If found the same, it adds the numerator value and returns the rational number formed. Else if not found similar, it defines logic for rational number addition.
  2. The subtract method internally calls the add method after negating the second rational number instance passed.
  3. The multiply method has quite a complex logic internally. It checks if the numerator is zero of either of the rational number, it returns the output as a zero value. If not zero, it checks the numerator with the denominator of either set. That is the r1 numerator gets checked with the r2 denominator and vice-versa. When no condition matches, the numerator of r1 gets multiplied to r2 instance numerator, and the denominators of both get multiplied. Finally, it calls the multiply function of the BigInteger class to perform multiplication.
  4. The divide function will invert the passed instance and internally call the multiply function using the first instance.
  5. Last is the pow function which evaluates the second power of the r2 instance. The implementation of pow gets defined in the BigInteger class that takes the exponent for evaluation. The method throws the ArithmeticException exception when the exponent is a negative value.

Below is the output of the complex code given above:

r1 = 31, 400 r1.negate() = -31, 400 r1.invert() = 1 / 31, 400 r1.intValue() = 31,
             400 r1.longValue() = 31, 400 r1.floatValue() = 31, 400.000000 r1.doubleValue() = 31,
             400.000000

    r2 = 111 / 7 r2.negate() = -111 / 7 r2.invert() = 7 / 111 r2.intValue() = 15 r2.longValue() =
        15 r2.floatValue() = 15.857142 r2.doubleValue() = 15.857143

    r1
    + r2 = 219,
             911 / 7 r1 + r2.negate() = -219, 911 / 7 r1 + r2.invert() = 7 / 219,
             911 r1 + r2.intValue() = 31, 415 r1 + r2.longValue() = 31,
             415 r1 + r2.floatValue() = 31, 415.857422 r1 + r2.doubleValue() = 31,
             415.857143

    r1
    - r2 = 219,
             689 / 7 r1 - r2.negate() = -219, 689 / 7 r1 - r2.invert() = 7 / 219,
             689 r1 - r2.intValue() = 31, 384 r1 - r2.longValue() = 31,
             384 r1 - r2.floatValue() = 31, 384.142578 r1 - r2.doubleValue() = 31,
             384.142857

    r1* r2 = 3,
             485, 400 / 7 r1* r2.negate() = -3, 485, 400 / 7 r1* r2.invert() = 7 / 3, 485,
             400 r1* r2.intValue() = 497, 914 r1* r2.longValue() = 497,
             914 r1* r2.floatValue() = 497, 914.281250 r1* r2.doubleValue() = 497,
             914.285714

    r1
    / r2 = 219,
             800 / 111 r1 / r2.negate() = -219, 800 / 111 r1 / r2.invert() = 111 / 219,
             800 r1 / r2.intValue() = 1, 980 r1 / r2.longValue() = 1, 980 r1 / r2.floatValue() = 1,
             980.180176 r1 / r2.doubleValue() = 1,
             980.180180

    r2
    ^ 2 = 12,
             321 / 49 r2 ^ 2.negate() = -12, 321 / 49 r2 ^ 2.invert() = 49 / 12,
             321 r2 ^ 2.intValue() = 251 r2 ^ 2.longValue() = 251 r2
    ^ 2.floatValue() = 251.448975 r2 ^ 2.doubleValue() = 251.448980
Rashmi Patidar avatar Rashmi Patidar avatar

Rashmi is a professional Software Developer with hands on over varied tech stack. She has been working on Java, Springboot, Microservices, Typescript, MySQL, Graphql and more. She loves to spread knowledge via her writings. She is keen taking up new things and adopt in her career.

LinkedIn

Related Article - Java Math