LoginSignup
1
0

More than 1 year has passed since last update.

Javaで分数のクラスを書いてみた(既約分数への変換,非Integerの分母分子に対応)

Last updated at Posted at 2021-10-28

はじめに

 本記事ではJavaで自作してみたので分数のクラスを公開させていただきます.一応テストは行いましたが欠陥があったらすみません泣

 特徴としては,次の3点を目指しました.

  • 分母,もしくは分子が小数点ありでも大丈夫(本記事では分母と分子の型はBigDecimalです.).
  • 既約分数にできる.
  • 分数同士の割り算で分母が0になった場合は例外が発生する.

 「この分数って割り算したら無限小数になっちゃうから,切り上げとかしないといけないよなあ.でも切り上げとかやって誤差大きくしたくないなあ.代わりに既約分数とかに変換できたら嬉しいなあ」といったニーズのある方の助力になれたら嬉しいです.
 もし読みにくいコードがあればすみません泣

コード

こちらが分数のクラスのコードです.クラス名はFractionとしました.

Fraction.java
import java.math.BigDecimal;

public class Fraction {
    private BigDecimal numerator;   // 分子
    private BigDecimal denominator; // 分母

    public Fraction(BigDecimal numerator, BigDecimal denominator) {
        if (denominator == null) {
            throw new IllegalArgumentException("分母はnullにできません.");
        }
        if (numerator == null) {
            throw new IllegalArgumentException("分子はnullにできません.");
        }
        if (denominator.compareTo(new BigDecimal("0")) == 0) {
            throw new IllegalArgumentException("分母は0にできません.");
        }
        this.numerator = numerator;
        this.denominator = denominator;
    }

    public BigDecimal getNumerator() {
        return numerator;
    }

    public void setNumerator(BigDecimal numerator) {
        this.numerator = numerator;
    }

    public BigDecimal getDenominator() {
        return denominator;
    }

    public void setDenominator(BigDecimal denominator) {
        this.denominator = denominator;
    }

    // 約分する
    public Fraction getLowestTerm() {
        try {
            BigDecimal result = numerator.divide(denominator);
            return new Fraction(
                result,
                new BigDecimal("1")
            );
        } catch(ArithmeticException e) {
            int scaleByPowerOfTen = Math.max(
                numerator.scale(),
                denominator.scale()
            );
            BigDecimal tempNumerator = numerator.scaleByPowerOfTen(scaleByPowerOfTen);
            BigDecimal tempDenominator = denominator.scaleByPowerOfTen(scaleByPowerOfTen);
            BigDecimal gcm = new BigDecimal("0");
            while (gcm(tempNumerator, tempDenominator).compareTo(new BigDecimal("1")) != 0) {
                gcm = gcm(tempNumerator, tempDenominator);
                tempNumerator = tempNumerator.divide(gcm);
                tempDenominator = tempDenominator.divide(gcm);
            }
            return new Fraction(tempNumerator, tempDenominator);
        }
    }

    // 分数を足し算する
    // 分母が等しくない場合は a/b + c/d = (ad + bc)/bd を求めている
    public Fraction add(Fraction addingFraction){
        if (denominator.compareTo(addingFraction.getDenominator()) == 0) {
            return new Fraction(
                numerator.add(addingFraction.getNumerator()),
                denominator
            );
        } else {
            return new Fraction(
                ( numerator.multiply(addingFraction.getDenominator()) ).add( denominator.multiply(addingFraction.getNumerator()) ),
                denominator.multiply(addingFraction.getDenominator())
            );
        }
    }

    // 分数を引き算する
    // 分母が等しくない場合は a/b - c/d = (ad - bc)/bd を求めている
    public Fraction sub(Fraction subtractingFraction) {
        if (denominator.compareTo(subtractingFraction.getDenominator()) == 0) {
            return new Fraction(
                numerator.subtract(subtractingFraction.getNumerator()),
                denominator
            );
        } else {
            return new Fraction(
                ( numerator.multiply(subtractingFraction.getDenominator()) ).subtract( denominator.multiply(subtractingFraction.getNumerator()) ),
                denominator.multiply(subtractingFraction.getDenominator())
            );
        }
    }

    // 分数の掛け算する
    // a/b * c/d = ac/bd を求めている
    public Fraction mul(Fraction multiplyingFraction) {
        return new Fraction(
            numerator.multiply(multiplyingFraction.getNumerator()),
            denominator.multiply(multiplyingFraction.getDenominator())
        );
    }

    // 分数を割り算する
    // a/b / c/d = ad/bc を求めている
    public Fraction div(Fraction dividingFraction) {
        return new Fraction(
            numerator.multiply(dividingFraction.getDenominator()),
            denominator.multiply(dividingFraction.getNumerator())
        );
    }

    // BigDecimal同士の最大公約数を求める
    public static BigDecimal gcm(BigDecimal a, BigDecimal b) {
        BigDecimal temp;
        while((temp = a.remainder(b)).compareTo(new BigDecimal("0")) != 0) {
            a = b;
            b = temp;
        }
        return b;
    }
}

 最後のgcm()メソッドは,実際にこちらのFractionクラスをお使いになるとなったら別のクラスにお書きになるのがよろしいかと思います.

使用例

Demoクラスというmain()メソッドが書かれたクラスがあると想定して実際にFractionクラスを活用している様子を書いてみました.

Demo.java
import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        // 分数を3つ生成
        Fraction myFrac_1 = new Fraction(
            new BigDecimal("8.5"),
            new BigDecimal("1.5")
        );
        Fraction myFrac_2 = new Fraction(
            new BigDecimal("17"),
            new BigDecimal("3")
        );
        Fraction myFrac_3 = new Fraction(
            new BigDecimal("0"),
            new BigDecimal("1")
        );

        // myFrac_1にmyFrac_2を足す
        // add,sub,mul,divの書き方は同じです
        Fraction addedFrac = myFrac_1.add(myFrac_2);
        // 足し算結果は 51 / 4.5

        // myFrac_1をmyFrac_3で割る
        Fraction dividedFrac = myFrac_1.div(myFrac_3);
        // IllegalArguementExceptionが生じます.

        // myFrac_1を既約分数に変換してmyFrac_4に代入
        Fraction myFrac_4 = myFrac_1.getLowestTerm();
        // 変換結果は 17 / 3
    }
}

おわりに

 Javaで分数の計算を扱う機会があったので書いてみました。「Java 分数」や「Java Fraction」などと検索しても,既約分数に変換できるメソッドを用意している自作クラスや,分母や分子がInteger以外になっても大丈夫になっている自作クラスにたどり着けなかったので書いてみました.
 私の調査不足,もしくは「本記事でかかげた特徴を持つ分数のクラスを作るとまずいことがある」,「本記事でかかげた特徴を持つ分数のクラスを作っても色んな理由により意味がない」などがあったらすみません泣
 また,分母が0にできないとうのも結構不便だったりもするのかなと思うので,0割りもやりたいな,という方向けのコードも今度書いてみようと思います.
 もし需要がおありでしたらお使いください.

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0