Fracciones en Java

Rashmi Patidar 12 octubre 2023
Fracciones en Java

Matemáticamente, las fracciones son las partes o secciones de valores. Cuando un objeto se rompe igualmente en proporciones definidas, el valor formado se llama fracción. Las fracciones se clasifican en números racionales e irracionales.

En el lenguaje de programación Java, existe el privilegio de realizar varias operaciones sobre fracciones como procedimientos matemáticos. Uno puede sumar, restar, multiplicar y dividir los números fraccionarios.

A continuación se muestra el bloque de código que demuestra las operaciones numéricas racionales en la clase definida por el usuario.

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);
  }
}

En el programa anterior, la ejecución comienza desde el método main. En primer lugar, un número racional se inicializa usando la función estática valueOf() definida en la clase. Toma un valor de cadena y realiza manipulaciones sobre la cadena de entrada.

Devuelve un RationalNumber del tipo definido por el usuario. La función primero busca una cadena de entrada con validaciones y patrones adecuados. La cadena de entrada se separa en exponente, decimal y la parte completa. Estos valores separados individuales se combinan para formar un número racional.

Otra forma de crear un número racional es usando una función de fábrica estática instanceOf(). El método llama internamente a la función canonical, que llama a un constructor privado de la clase. El constructor separa y establece los valores del numerador y denominador para una instancia específica. Por forma canónica, simplemente significa la forma de representación estándar del número racional.

En el caso dado, p/q es la forma estándar o canónica donde q!=0 debería ser siempre verdadero. El método tiene la lógica escrita para satisfacer las condiciones básicas de un número racional. Comprueba el signo del numerador y el denominador por separado y realiza operaciones. Comprueba los valores del denominador; si es cero, la operación arroja una excepción. También comprueba el máximo común divisor o gcd en los valores del numerador y denominador. La función gcd está presente en la clase BigInteger que devuelve un valor BigInteger.

Continuando, se llama al método convert, pasando la primera instancia de número racional que se crea. Este método también llama a siete funciones diferentes sobre la instancia del número racional que se pasa.

A continuación, comprenderá mejor cada uno de estos métodos:

  • Imprima el número racional en el flujo de impresión. Generalmente, cada vez que se imprime una instancia, se llama al método predeterminado toString. El método imprime el nombre de la clase seguido de la representación hexadecimal de la ubicación de la memoria. Pero aquí, la función se anula para dar otra implementación que representa el número en la forma p/q.
  • La función negate negará internamente el número racional llamando al método negate de la clase BigInteger. Agregará un signo menos antes del número racional.
  • La función de inversión llama internamente al método canonical con una única diferencia. Internamente pasa el tercer parámetro checkGcd como falso. Cuando el valor de entrada es booleano verdadero, la lógica del método simplifica el número racional dividiendo la fracción por su máximo común divisor. Pero en este caso, la necesidad es invertir sin simplificar.
  • La función intValue comprueba primero si la instancia es un valor Integer. Los Integers son números que pueden ser positivos, negativos o cero, pero no pueden ser fracciones. Entonces, internamente llama al método divide sobre numerador y denominador. El método se da en la clase BigInteger y devuelve un valor BigInteger. También arroja ArithmeticException cuando el denominador encuentra un valor cero. El valor devuelto se transforma en un valor int utilizando la función intValue.
  • La función longValue llama internamente a la función dividir y analiza la salida BigInteger a un tipo de datos long.
  • La función floatValue proporciona el valor de coma flotante del número racional. Y así como la función doubleValue(), que encasilla la salida en el tipo de datos Double.

Todo el proceso se llama a una instancia de número racional inicial que es la instancia r1. Una secuencia similar se repite de nuevo y se imprime para la instancia racional r2.

Operaciones matemáticas para fracciones en Java

Ahora, las operaciones aritméticas de sumar, restar, dividir, multiplicar y potenciar necesitan dos operandos para su evaluación. Entonces, analicemos los métodos en detalle a continuación:

  1. La función add primero comprueba los valores del numerador si son positivos, negativos o cero. Si es distinto de cero, comprueba si los denominadores de ambos números racionales son iguales. Si se encuentra igual, suma el valor del numerador y devuelve el número racional formado. De lo contrario, si no se encuentra similar, define la lógica para la suma de números racionales.
  2. El método subtract llama internamente al método add después de negar la segunda instancia de número racional pasada.
  3. El método multiply tiene una lógica interna bastante compleja. Comprueba si el numerador es cero de cualquiera de los números racionales, devuelve la salida como un valor cero. Si no es cero, verifica el numerador con el denominador de cualquiera de los conjuntos. Ese es el numerador r1 que se comprueba con el denominador r2 y viceversa. Cuando ninguna condición coincide, el numerador de r1 se multiplica por el numerador de instancia r2 y los denominadores de ambos se multiplican. Finalmente, llama a la función multiply de la clase BigInteger para realizar la multiplicación.
  4. La función divide invertirá la instancia pasada y llamará internamente a la función multiply utilizando la primera instancia.
  5. La última es la función pow que evalúa la segunda potencia de la instancia r2. La implementación de pow se define en la clase BigInteger que toma el exponente para su evaluación. El método lanza la excepción ArithmeticException cuando el exponente es un valor negativo.

A continuación se muestra el resultado del código complejo que se proporciona anteriormente:

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

Artículo relacionado - Java Math