Java 中的分數

Rashmi Patidar 2023年10月12日
Java 中的分數

在數學上,分數是值的部分或部分。當一個物體以一定的比例被均等地分解時,所形成的值稱為分數。分數分為有理數和無理數。

在 Java 程式語言中,有一種特權可以對分數執行各種運算,例如數學過程。可以對小數進行加、減、乘、除運算。

下面是演示使用者定義類中的有理數操作的程式碼塊。

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

在上面的程式中,執行從 main 方法開始。首先,使用類中定義的靜態 valueOf() 函式初始化一個有理數。它接受一個字串值並對輸入字串執行操作。

它返回使用者定義型別的 RationalNumber。該函式首先檢查具有正確驗證和模式的輸入字串。輸入字串被分成指數、小數和整個部分。這些單獨的破碎值組合形成一個有理數。

建立有理數的另一種方法是使用 instanceOf() 靜態工廠函式。該方法在內部呼叫 canonical 函式,該函式呼叫類的私有建構函式。建構函式分離並設定特定例項的分子和分母的值。規範形式,它只是指有理數的標準表示形式。

在給定的情況下,p/q 是標準或規範形式,其中 q!=0 應始終為真。該方法編寫了滿足有理數基本條件的邏輯。它分別檢查分子和分母的符號並執行運算。它檢查分母值;如果它是零,操作會丟擲一個異常。它還檢查分子和分母值中的最大公約數或 gcdgcd 函式存在於返回 BigInteger 值的 BigInteger 類中。

繼續前進,呼叫 convert 方法,傳遞建立的第一個有理數例項。此方法還在傳遞的有理數例項上呼叫七個不同的函式。

下面,你將更好地理解這些方法:

  • 在列印流中列印有理數。通常,每當列印例項時,都會呼叫預設的 toString 方法。該方法列印類的名稱,後跟記憶體位置的十六進位制表示。但在這裡,該函式被覆蓋以提供另一個表示 p/q 形式的數字的實現。
  • negate 函式將通過呼叫 BigInteger 類的 negate 方法在內部否定有理數。它將在有理數之前新增一個減號。
  • invert 函式在內部呼叫 canonical 方法,但只有一個區別。它在內部將第三個 checkGcd 引數作為 false 傳遞。當輸入值為布林真時,方法邏輯通過將分數除以其最大公約數來簡化有理數。但在這種情況下,需要的是不簡化的反轉。
  • intValue 函式首先檢查例項是否為 Integer 值。Integers 是可以是正數、負數或零但不能是分數的數字。因此,它在內部對分子和分母呼叫 divide 方法。該方法在 BigInteger 類中給出並返回一個 BigInteger 值。當分母為零值時,它也會丟擲 ArithmeticException。使用 intValue 函式將返回值轉換為 int 值。
  • longValue 函式在內部呼叫除法函式 snd 將 BigInteger 輸出解析為 long 資料型別。
  • floatValue 函式提供有理數的浮點值。doubleValue() 函式也是如此,它以 Double 資料型別對輸出進行型別轉換。

整個過程呼叫一個初始有理數例項,即 r1 例項。類似的序列再次重複併為有理例項 r2 列印出來。

Java 中分數的數學運算

現在加、減、除、乘和冪算術運算需要兩個運算元進行計算。因此,讓我們在下面詳細討論這些方法:

  1. add 函式首先檢查分子值是正數、負數還是零。如果非零,則檢查兩個有理數的分母是否相同。如果發現相同,則將分子值相加並返回形成的有理數。否則,如果沒有找到類似的,它定義了有理數加法的邏輯。
  2. subtract 方法在否定傳遞的第二個有理數例項後在內部呼叫 add 方法。
  3. multiply 方法內部有相當複雜的邏輯。它檢查任一有理數的分子是否為零,並將輸出作為零值返回。如果不為零,則使用任一集合的分母檢查分子。即 r1 分子與 r2 分母進行檢查,反之亦然。當沒有條件匹配時,r1 的分子與 r2 例項分子相乘,並且兩者的分母相乘。最後,它呼叫 BigInteger 類的 multiply 函式來執行乘法。
  4. divide 函式將反轉傳遞的例項並使用第一個例項在內部呼叫 multiply 函式。
  5. 最後是 pow 函式,它計算 r2 例項的二次冪。pow 的實現在 BigInteger 類中定義,該類採用指數進行評估。當指數為負值時,該方法會引發 ArithmeticException 異常。

下面是上面給出的複雜程式碼的輸出:

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
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

相關文章 - Java Math