2015年10月11日日曜日

分数を扱うFractionクラスの実装[C#]

 昨日作った累乗和の公式を求めるプログラムでは,分数を扱えなかったのでdouble型で実装した。今日は分数のクラスを定義したのでソースコードを貼っておく。

 基本的なオペレーターは定義したけれども足りないとか間違っているとかあるかもしれないので指摘or温かい目でご覧ください。

 だけどソースコード長い。GitHubとか始めたほうがいいかもしれんな……。
using System;

class Fraction
{
    /// <summary>
    /// 分子
    /// </summary>
    public int Upper
    {
        get { return _Upper; }
        set { this._Upper = value; CheckFraction(); }
    }
    /// <summary>
    /// 分母
    /// </summary>
    public int Lower
    {
        get { return _Lower; }
        set { this._Lower = value; CheckFraction(); }
    }
    /// <summary>
    /// 分子の値を格納する変数
    /// </summary>
    private int _Upper;
    /// <summary>
    /// 分母の値を格納する変数
    /// </summary>
    private int _Lower;

    public Fraction(int Upper, int Lower)
    {
        Assignment(Upper, Lower);
    }
    public Fraction(int n)
    {
        Assignment(n, 1);
    }
    public Fraction(Fraction f)
    {
        Assignment(f.Upper, f.Lower);
    }

    /// <summary>
    /// double型に変換するメソッド
    /// </summary>
    /// <returns>double型に変換した値</returns>
    public double ToDouble()
    {
        return 1.0 * Upper / Lower;
    }

    /// <summary>
    /// float型に変換するメソッド
    /// </summary>
    /// <returns>float型に変換した値</returns>
    public float ToFloat()
    {
        return 1.0f * Upper / Lower;
    }

    /// <summary>
    /// 約分や分母が0などのミスが無いか確認するメソッド
    /// </summary>
    private void CheckFraction()
    {
        int u = _Upper;
        int l = _Lower;
        int temp;
        bool sign;


        if (l == 0)
        {
            // 分母が0なのでゼロ除算の例外をぶん投げる
            throw new DivideByZeroException();
        }
        else if (u == 0)
        {
            // 分母が0ならばこうする
            _Upper = 0;
            _Lower = 1;
        }
        else
        {
            // 分数の値が負ならsign = true となる
            sign = l * u < 0;
            // とりあえずl,uを正にする(余りの計算をするため)
            l = Math.Abs(l);
            u = Math.Abs(u);

            // 常にu <= lとなるようにする
            if (u > l)
            {
                temp = u;
                u = l;
                l = temp;
            }

            // ユークリッドの互除法
            while (l % u != 0)
            {
                temp = l % u;
                l = u;
                u = temp;
            }

            // 約分
            _Upper = Math.Abs(_Upper)/u;
            _Lower = Math.Abs(_Lower)/u;

            // 正負を直す
            if (sign)
                _Upper = -_Upper;
        }
    }

    /// <summary>
    /// クラス内で代入を行うメソッド(ただしCheckFractonから呼んではならない)
    /// </summary>
    /// <param name="Upper">分母</param>
    /// <param name="Lower">分子</param>
    private void Assignment(int Upper, int Lower)
    {
        this._Upper = Upper;
        this._Lower = Lower;
        CheckFraction();
    }

    public static Fraction operator +(Fraction left, Fraction right)
    {
        int l = left.Lower * right.Lower;
        int u = left.Upper * right.Lower + right.Upper * left.Lower;

        return new Fraction(u, l);
    }

    public static Fraction operator -(Fraction left, Fraction right)
    {
        right.Upper = -right.Upper;

        return left + right;
    }

    public static Fraction operator *(Fraction left, Fraction right)
    {
        int l = left.Lower * right.Lower;
        int u = left.Upper * right.Upper;

        return new Fraction(u, l);
    }

    public static Fraction operator /(Fraction left, Fraction right)
    {
        // 逆数にする(0除算ならここで例外が発生する)
        right = new Fraction(right.Lower, right.Upper);
        return right * left;
    }
    public static Fraction operator /(Fraction f, int n)
    {
        return new Fraction(f.Upper, f.Lower * n);
    }

    public static Fraction operator *(Fraction f, int n)
    {
        return new Fraction(f.Upper * n, f.Lower);
    }
    
    public static implicit operator Fraction(int n)
    {
        return new Fraction(n);
    }

    public static bool operator <(Fraction left, Fraction right)
    {
        return left.ToDouble() < right.ToDouble();
    }

    public static bool operator >(Fraction left, Fraction right)
    {
        return left.ToDouble() > right.ToDouble();
    }

    public static bool operator <=(Fraction left, Fraction right)
    {
        return left.ToDouble() <= right.ToDouble();
    }

    public static bool operator >=(Fraction left, Fraction right)
    {
        return left.ToDouble() >= right.ToDouble();
    }

    public static implicit operator double (Fraction f)
    {
        return f.ToDouble();
    }

    public static implicit operator float (Fraction f)
    {
        return f.ToFloat();
    }

    public static bool operator ==(Fraction left, Fraction right)
    {
        if (left.Upper == null || right.Upper == null)
            return false;
        return left.Upper == right.Upper && left.Lower == right.Lower;
    }

    public static bool operator !=(Fraction left, Fraction right)
    {
        return !(left == right);
    }

    public override bool Equals(object obj)
    {
        if (obj == null || this.GetType() != obj.GetType())
            return false;

        return (this == (Fraction)obj);
    }

    public override int GetHashCode()
    {
        return this.Upper * this.Lower;
    }

    public override string ToString()
    {
        return String.Format("{0}/{1}", this.Upper, this.Lower);
    }
}

 サンプルコードはまた今度。

追記:
このプログラムを使って累乗和の公式を導出しました→累乗和の公式をプログラムで導出してみる(分数編)

0 件のコメント:

コメントを投稿