HOWTO · Java
Java 中的分數
本文介紹了 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 應始終為真。該方法編寫了滿足有理數基本條件的邏輯。它分別檢查分子和分母的符號並執行運算。它檢查分母值;如果它是零,操作會丟擲一個異常。它還檢查分子和分母值中的最大公約數或 gcd。gcd 函式存在於返回 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 中分數的數學運算
現在加、減、除、乘和冪算術運算需要兩個運算元進行計算。因此,讓我們在下面詳細討論這些方法:
add函式首先檢查分子值是正數、負數還是零。如果非零,則檢查兩個有理數的分母是否相同。如果發現相同,則將分子值相加並返回形成的有理數。否則,如果沒有找到類似的,它定義了有理數加法的邏輯。subtract方法在否定傳遞的第二個有理數例項後在內部呼叫add方法。multiply方法內部有相當複雜的邏輯。它檢查任一有理數的分子是否為零,並將輸出作為零值返回。如果不為零,則使用任一集合的分母檢查分子。即r1分子與r2分母進行檢查,反之亦然。當沒有條件匹配時,r1的分子與r2例項分子相乘,並且兩者的分母相乘。最後,它呼叫BigInteger類的multiply函式來執行乘法。divide函式將反轉傳遞的例項並使用第一個例項在內部呼叫multiply函式。- 最後是
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