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