Frazioni in Java

Rashmi Patidar 12 ottobre 2023
Frazioni in Java

Matematicamente, le frazioni sono parti o sezioni di valori. Quando un oggetto viene spezzato equamente in proporzioni definite, il valore formato è chiamato frazione. Le frazioni sono classificate in numeri razionali e numeri irrazionali.

Nel linguaggio di programmazione Java, esiste il privilegio di eseguire varie operazioni su frazioni come procedure matematiche. Si può aggiungere, sottrarre, moltiplicare e dividere sui numeri frazionari.

Di seguito è riportato il blocco di codice che dimostra le operazioni sui numeri razionali nella classe definita dall’utente.

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

Nel programma sopra, l’esecuzione inizia dal metodo main. Innanzitutto, un numero razionale viene inizializzato utilizzando la funzione statica valueOf() definita nella classe. Prende un valore stringa ed esegue manipolazioni sulla stringa di input.

Restituisce un RationalNumber del tipo definito dall’utente. La funzione prima verifica la presenza di una stringa di input con convalide e modelli appropriati. La stringa di input viene separata in esponente, decimale e parte intera. Questi singoli valori rotti si combinano per formare un numero razionale.

Un altro modo per creare un numero razionale è utilizzare una funzione di fabbrica statica instanceOf(). Il metodo chiama internamente la funzione canonical, che chiama un costruttore privato della classe. Il costruttore separa e imposta i valori del numeratore e del denominatore per un’istanza specifica. Per forma canonica si intende semplicemente la forma di rappresentazione standard del numero razionale.

Nel caso dato, p/q è la forma standard o canonica dove q!=0 dovrebbe essere sempre vero. Il metodo ha la logica scritta per soddisfare le condizioni di base di un numero razionale. Controlla separatamente il segno del numeratore e del denominatore ed esegue le operazioni. Controlla i valori del denominatore; se è zero, l’operazione genera un eccezione. Controlla anche il massimo comun divisore o gcd nei valori del numeratore e del denominatore. La funzione gcd è presente nella classe BigInteger che restituisce un valore BigInteger.

Andando avanti, viene chiamato il metodo convert, passando la prima istanza di numero razionale che viene creata. Questo metodo richiede anche sette diverse funzioni sull’istanza del numero razionale che viene passata.

Di seguito, capirai meglio ciascuno di questi metodi:

  • Stampa il numero razionale nel flusso di stampa. In genere, ogni volta che viene stampata un’istanza, viene chiamato il metodo predefinito toString. Il metodo stampa il nome della classe seguito dalla rappresentazione esadecimale della locazione di memoria. Ma qui, la funzione viene sovrascritta per fornire un’altra implementazione che rappresenta il numero nella forma p/q.
  • La funzione nega negherà internamente il numero razionale chiamando il metodo negate della classe BigInteger. Aggiungerà un segno meno prima del numero razionale.
  • La funzione invert richiama internamente il metodo canonical con una sola differenza. Passa internamente il terzo parametro checkGcd come falso. Quando il valore di input è Boolean true, la logica del metodo semplifica il numero razionale dividendo la frazione per il suo massimo comun divisore. Ma in questo caso si tratta di invertire senza semplificazioni.
  • La funzione intValue verifica innanzitutto se l’istanza è un valore Integer. Gli interi sono numeri che possono essere positivi, negativi o zero ma non possono essere frazioni. Quindi, internamente chiama il metodo divide su numeratore e denominatore. Il metodo è fornito nella classe BigInteger e restituisce un valore BigInteger. Genera anche ArithmeticException quando il denominatore trova un valore zero. Il valore restituito viene trasformato in un valore int utilizzando la funzione intValue.
  • La funzione longValue chiama internamente la funzione divide e analizza l’output BigInteger in un tipo di dati long.
  • La funzione floatValue fornisce il valore in virgola mobile del numero razionale. E così come la funzione doubleValue(), che converte l’output nel tipo di dati Double.

L’intero processo viene chiamato su un’istanza di numero razionale iniziale che è l’istanza r1. Una sequenza simile viene nuovamente ripetuta e viene stampata per l’istanza razionale r2.

Operazioni matematiche per le frazioni in Java

Ora le operazioni di addizione, sottrazione, divisione, moltiplicazione e potenza richiedono due operandi per la valutazione. Quindi discutiamo i metodi in dettaglio di seguito:

  1. La funzione add verifica prima i valori del numeratore se sono positivi, negativi o zero. Se diverso da zero, controlla se i denominatori di entrambi i numeri razionali sono gli stessi. Se trovato lo stesso, aggiunge il valore del numeratore e restituisce il numero razionale formato. Altrimenti, se non trovato simile, definisce la logica per l’aggiunta di numeri razionali.
  2. Il metodo subtract chiama internamente il metodo add dopo aver negato la seconda istanza di numero razionale passata.
  3. Il metodo multiply ha internamente una logica piuttosto complessa. Controlla se il numeratore è zero di uno dei numeri razionali, restituisce l’output come valore zero. Se non è zero, controlla il numeratore con il denominatore di entrambi gli insiemi. Cioè il numeratore r1 viene verificato con il denominatore r2 e viceversa. Quando nessuna condizione corrisponde, il numeratore di r1 viene moltiplicato per il numeratore dell’istanza r2 e i denominatori di entrambi vengono moltiplicati. Infine, chiama la funzione multiply della classe BigInteger per eseguire la moltiplicazione.
  4. La funzione divide invertirà l’istanza passata e chiamerà internamente la funzione multiply utilizzando la prima istanza.
  5. Ultima è la funzione pow che valuta la seconda potenza dell’istanza r2. L’implementazione di pow viene definita nella classe BigInteger che prende l’esponente per la valutazione. Il metodo genera l’eccezione ArithmeticException quando l’esponente è un valore negativo.

Di seguito è riportato l’output del codice complesso sopra indicato:

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

Articolo correlato - Java Math