Frações em Java

Rashmi Patidar 12 outubro 2023
Frações em Java

Matematicamente, as frações são as partes ou seções de valores. Quando um objeto é dividido igualmente em proporções definidas, o valor formado é chamado de fração. As frações são classificadas em números racionais e irracionais.

Na linguagem de programação Java, existe o privilégio de realizar várias operações sobre frações, como procedimentos matemáticos. Pode-se adicionar, subtrair, multiplicar e dividir pelos números fracionários.

Abaixo está o bloco de código que demonstra as operações de número Rational na classe definida pelo usuário.

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

No programa acima, a execução começa a partir do método main. Em primeiro lugar, um número racional é inicializado usando a função estática valueOf() definida na classe. Ele pega um valor de string e executa manipulações sobre a String de entrada.

Ele retorna um RationalNumber do tipo definido pelo usuário. A função primeiro verifica se há uma string de entrada com validações e padrões adequados. A string de entrada é separada em expoente, decimal e a parte inteira. Esses valores individuais quebrados se combinam para formar um número racional.

Outra maneira de criar um número racional é usar uma função de fábrica estática instanceOf(). O método chama internamente a função canonical, que chama um construtor privado da classe. O construtor separa e define os valores do numerador e denominador para uma instância específica. Por forma canônica, significa simplesmente a forma de representação padrão do número racional.

No caso dado, p/q é a forma padrão ou canônica onde q!=0 deve ser sempre verdadeiro. O método possui a lógica escrita para satisfazer as condições básicas de um número racional. Ele verifica o sinal do numerador e do denominador separadamente e executa as operações. Ele verifica os valores do denominador; se for zero, a operação lança uma exceção. Ele também verifica o máximo divisor comum ou mdc nos valores do numerador e do denominador. A função gcd está presente na classe BigInteger que retorna um valor BigInteger.

Seguindo em frente, o método convert é chamado, passando a primeira instância de número racional que é criada. Este método também chama sete funções diferentes sobre a instância de número racional que é passada.

Abaixo, você entenderá melhor cada um desses métodos:

  • Imprima o número racional no fluxo de impressão. Geralmente, sempre que uma instância é impressa, o método padrão toString é chamado. O método imprime o nome da classe seguido pela representação hexadecimal da localização da memória. Mas aqui, a função é substituída para fornecer outra implementação que representa o número na forma p/q.
  • A função negate negará internamente o número racional chamando o método negate da classe BigInteger. Ele adicionará um sinal de menos antes do número racional.
  • A função invert chama internamente o método canonical com uma única diferença. Ele passa internamente o terceiro parâmetro checkGcd como falso. Quando o valor de entrada é booleano verdadeiro, a lógica do método simplifica o número racional dividindo a fração por seu maior divisor comum. Mas, neste caso, a necessidade é inverter sem simplificação.
  • A função intValue primeiro verifica se a instância é um valor Inteiro. Inteiros são números que podem ser positivos, negativos ou zero, mas não podem ser frações. Então, internamente, ele chama o método divide sobre numerador e denominador. O método é fornecido na classe BigInteger e retorna um valor BigInteger. Ele também lança ArithmeticException quando o denominador encontra um valor zero. O valor retornado é transformado em um valor int usando a função intValue.
  • A função longValue chama internamente a função divide e analisa a saída BigInteger para um tipo de dados long.
  • A função floatValue fornece o valor de ponto flutuante do número racional. E assim como a função doubleValue(), que tipifica a saída no tipo de dados Double.

Todo o processo é chamado por uma instância de número racional inicial que é a instância r1. Uma sequência semelhante é repetida novamente e é impressa para a instância racional r2.

Operações matemáticas para frações em Java

Agora, as operações aritméticas de adição, subtração, divisão, multiplicação e potência precisam de dois operandos para avaliação. Então, vamos discutir os métodos em detalhes abaixo:

  1. A função add primeiro verifica os valores do numerador se eles são positivos, negativos ou zero. Se diferente de zero, verifica se os denominadores de ambos os números racionais são iguais. Se for encontrado o mesmo, adiciona o valor do numerador e retorna o número racional formado. Caso contrário, se não for semelhante, ele define a lógica para a adição de um número racional.
  2. O método subtract chama internamente o método add depois de negar a segunda instância de número racional passada.
  3. O método multiply possui uma lógica interna bastante complexa. Ele verifica se o numerador é zero de qualquer um dos números racionais e retorna a saída como um valor zero. Se não for zero, verifica o numerador com o denominador de qualquer um dos conjuntos. Ou seja, o numerador r1 é verificado com o denominador r2 e vice-versa. Quando nenhuma condição corresponde, o numerador de r1 é multiplicado pelo numerador de instância r2 e os denominadores de ambos são multiplicados. Finalmente, ele chama a função multiply da classe BigInteger para realizar a multiplicação.
  4. A função divide irá inverter a instância passada e chamar internamente a função multiply usando a primeira instância.
  5. Por último está a função pow que avalia a segunda potência da instância r2. A implementação de pow é definida na classe BigInteger que leva o expoente para avaliação. O método lança a exceção ArithmeticException quando o expoente é um valor negativo.

Abaixo está a saída do código complexo fornecido acima:

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

Artigo relacionado - Java Math