基本完工了,除了一个抄自python的限制分母大小的方法会有bug(Σ( ° △ °|||)︴其他基本可以了
另外ToString重写了,所以直接ToString是分数形式(X/Y),要想显示小数形式要先转成double。
using System; using System.Linq; using System.Text.RegularExpressions; using System.Threading; namespace RainbowFraction { public struct RainbowFraction : IComparable, IComparable<RainbowFraction>, IEquatable<RainbowFraction> { #region 字段 private long _numerator; private long _denominator; #endregion #region 属性 /// <summary> /// 分子 /// </summary> public long Numerator { set { SetNumAndDen(out _numerator, out _denominator, value, Denominator); } get { return _numerator; } } /// <summary> /// 分母 /// </summary> public long Denominator { get { return _denominator; } set {//分母分子不能同为负 if (value != 0) { SetNumAndDen(out _numerator, out _denominator, Numerator, value); } else { throw new RainbowFractionException("分母不可为0."); } } } /// <summary> /// 值 /// </summary> public double Value { get { return Numerator / (double)Denominator; } } #endregion #region 构造 /// <summary> /// 以分子分母构造 /// </summary> /// <param name="srcNum"></param> /// <param name="srcDen"></param> public RainbowFraction(long srcNum, long srcDen) { if (srcDen == 0) { throw new RainbowFractionException("分母不可为0"); } if (srcNum * srcDen >= 0) { _numerator = Math.Abs(srcNum); _denominator = Math.Abs(srcDen); } else { _numerator = Math.Abs(srcNum) * -1; _denominator = Math.Abs(srcDen); } } /// <summary> /// 以double构造 /// </summary> /// <param name="srcDouble"></param> public RainbowFraction(double srcDouble) { _numerator = 0; _denominator = 1; RainbowFraction tmp = ToRainbowFraction(srcDouble); _numerator = tmp.Numerator; _denominator = tmp.Denominator; } /// <summary> /// 以字符串构造 /// </summary> /// <param name="srcString"></param> public RainbowFraction(string srcString) { _numerator = 0; _denominator = 1; RainbowFraction tmp = ToRainbowFraction(srcString); _numerator = tmp.Numerator; _denominator = tmp.Denominator; } #endregion #region 方法 /// <summary> /// double to fraction /// </summary> /// <param name="srcDouble"></param> /// <returns></returns> public static RainbowFraction ToRainbowFraction(double srcDouble) { RainbowFraction result = new RainbowFraction(); try { checked { string srcString = srcDouble.ToString(); double tmpNum = srcDouble; long tmpDen = 1; while (srcString.IndexOf('E') > 0) { tmpNum *= 10; tmpDen *= 10; srcString = tmpNum.ToString(); } if (srcString.Contains('.')) { int lengthAfterDot = srcString.Split('.')[1].Length; while (lengthAfterDot > 0) { tmpNum *= 10; tmpDen *= 10; lengthAfterDot--; } } result = new RainbowFraction((long)Math.Round(tmpNum), tmpDen); } } catch (OverflowException) { throw new RainbowFractionException("转换时发生溢出"); } catch (Exception) { throw new RainbowFractionException("转换失败"); } return result; } /// <summary> /// string to double /// </summary> /// <param name="srcString"></param> /// <returns></returns> public static RainbowFraction ToRainbowFraction(string srcString) { RainbowFraction result = new RainbowFraction(); try { double srcDouble = 0; Regex fracRegex = new Regex(@"^-?\d+\/-?\d+$"); if (double.TryParse(srcString, out srcDouble)) {//形如1.23 result = ToRainbowFraction(srcDouble); } else if (fracRegex.IsMatch(srcString)) {//形如1/23 result = new RainbowFraction(Convert.ToInt64(srcString.Split('/')[0]), Convert.ToInt64(srcString.Split('/')[1])); } else { throw new RainbowFractionException("输入字符串有误"); } } catch { throw new RainbowFractionException("转换失败"); } return result; } /// <summary> /// 转化为字符串 /// </summary> /// <returns></returns> public override string ToString() { return Denominator == 0 ? "NaN" : string.Format("{0}/{1}", Numerator, Denominator); } /// <summary> /// 限制分母最大值 /// 抄自python,个别情况下会出异常 /// </summary> /// <param name="srcMaxDen"></param> /// <returns></returns> public RainbowFraction LimitDenominator(long srcMaxDen) { if (srcMaxDen < 1) { throw new RainbowFractionException("分母需要大于0."); } if (srcMaxDen > long.MaxValue) { throw new RainbowFractionException("分母过大."); } if (Denominator < srcMaxDen) { return this; } try { long p0 = 0; long q0 = 1; long p1 = 1; long q1 = 0; long n = Numerator; long d = Denominator; while (true) { long a = n / d; long q2 = q0 + a * q1; if (q2 > srcMaxDen) { break; } long p0Old = p0; p0 = p1; q0 = q1; p1 = p0Old + a * p1; q1 = q2; long nOld = n; n = d; d = nOld - a * d; } long k = (srcMaxDen - q0) / q1; RainbowFraction result1 = new RainbowFraction(p0 + k * p1, q0 + k * q1); RainbowFraction result2 = new RainbowFraction(p1, q1); if (Math.Abs(result2 - this) <= Math.Abs(result1 - this)) { return result2; } else { return result1; } } catch (DivideByZeroException) { throw new RainbowFractionException("转化错误,请尝试别的数字."); } } /// <summary> /// 设置分子分母及符号 /// </summary> /// <param name="srcNum"></param> /// <param name="srcDen"></param> /// <param name="destNum"></param> /// <param name="destDen"></param> private static void SetNumAndDen(out long srcNum, out long srcDen, long destNum, long destDen) { if (destNum * destDen >= 0) { srcNum = Math.Abs(destNum); srcDen = Math.Abs(destDen); } else { srcNum = -Math.Abs(destNum); srcDen = Math.Abs(destDen); } } /// <summary> /// 约分后分数 /// </summary> /// <returns></returns> public RainbowFraction ReduceNumAndDen() { long gcd = GreatestCommonDivisor(Numerator, Denominator); return new RainbowFraction(Numerator/gcd, Denominator/gcd); } /// <summary> /// 辗转相除法求最大公约数 /// </summary> /// <param name="srcBig"></param> /// <param name="srcSmall"></param> /// <returns></returns> private static long GreatestCommonDivisor(long srcBig, long srcSmall) { srcBig = Math.Abs(srcBig); srcSmall = Math.Abs(srcSmall); if (srcSmall > srcBig) { srcSmall = srcSmall ^ srcBig; srcBig = srcSmall ^ srcBig; srcSmall = srcSmall ^ srcBig; } if (srcBig%srcSmall == 0) { return srcSmall; } else { return GreatestCommonDivisor(srcSmall, srcBig % srcSmall); } } #endregion #region 接口实现 /// <summary> /// 分数比较 /// </summary> /// <param name="obj"></param> /// <returns></returns> public int CompareTo(object obj) { int result; double tmpValue; if (obj == null) { throw new RainbowFractionException("比较失败"); } if (obj is string) { if (this > ToRainbowFraction(obj as string)) { result = 1; } else if (this < ToRainbowFraction(obj as string)) { result = -1; } else { result = 0; } } else if (double.TryParse(obj as string, out tmpValue)) { result = Value.CompareTo(tmpValue); } else { throw new RainbowFractionException("比较失败"); } return result; } /// <summary> /// 分数比较 /// </summary> /// <param name="other"></param> /// <returns></returns> public int CompareTo(RainbowFraction other) { int result = 0; if (this > other) { result = 1; } else if (this < other) { result = -1; } return result; } /// <summary> /// 分数判等 /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(RainbowFraction other) { return Value == other.Value; } #endregion #region 隐式转化 /// <summary> /// double to fraction /// </summary> /// <param name="srcDouble"></param> /// <returns></returns> public static implicit operator RainbowFraction(double srcDouble) { RainbowFraction result = new RainbowFraction(srcDouble); return result; } /// <summary> /// fraction to double /// </summary> /// <param name="srcFrac"></param> /// <returns></returns> public static implicit operator double(RainbowFraction srcFrac) { return srcFrac.Value; } #endregion #region 运算符重载 //一元运算 public static RainbowFraction operator -(RainbowFraction srcFrac) { return new RainbowFraction(srcFrac.Numerator * -1, srcFrac.Denominator); } //二元逻辑运算 public static bool operator >(RainbowFraction left, RainbowFraction right) { return left.Value > right.Value; } public static bool operator >=(RainbowFraction left, RainbowFraction right) { return left.Value >= right.Value; } public static bool operator <(RainbowFraction left, RainbowFraction right) { return left.Value < right.Value; } public static bool operator <=(RainbowFraction left, RainbowFraction right) { return left.Value < right.Value; } public static bool operator ==(RainbowFraction left, RainbowFraction right) { return left.Value == right.Value; } public static bool operator !=(RainbowFraction left, RainbowFraction right) { return left.Value != right.Value; } //二元算术运算 /// <summary> /// RainbowFraction Add /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> public static RainbowFraction operator +(RainbowFraction left, RainbowFraction right) { return AddFraction(left, right); } /// <summary> /// RainbowFraction Minus /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> public static RainbowFraction operator -(RainbowFraction left, RainbowFraction right) { return SubFraction(left, right); } /// <summary> /// RainbowFraction Mult /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> public static RainbowFraction operator *(RainbowFraction left, RainbowFraction right) { return MultFraction(left, right); } /// <summary> /// RainbowFraction Div /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> public static RainbowFraction operator /(RainbowFraction left, RainbowFraction right) { return DivFraction(left, right); } /// <summary> /// Add function /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static RainbowFraction AddFraction(RainbowFraction left, RainbowFraction right) { RainbowFraction result = new RainbowFraction(); result.Denominator = left.Denominator * right.Denominator; result.Numerator = left.Numerator * right.Denominator + right.Numerator * left.Denominator; return result; } /// <summary> /// Sub function /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static RainbowFraction SubFraction(RainbowFraction left, RainbowFraction right) { RainbowFraction result = new RainbowFraction(); result.Denominator = left.Denominator * right.Denominator; result.Numerator = left.Numerator * right.Denominator - right.Numerator * left.Denominator; return result; } /// <summary> /// Mult fraction /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static RainbowFraction MultFraction(RainbowFraction left, RainbowFraction right) { RainbowFraction result = new RainbowFraction(); result.Denominator = left.Denominator * right.Denominator; result.Numerator = left.Numerator * right.Numerator; return result; } /// <summary> /// div fraction /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static RainbowFraction DivFraction(RainbowFraction left, RainbowFraction right) { RainbowFraction result = new RainbowFraction(); result.Denominator = left.Denominator * right.Numerator; result.Numerator = left.Numerator * right.Denominator; return result; } #endregion } /// <summary> /// 分数异常 /// </summary> public class RainbowFractionException : Exception { public RainbowFractionException(string srcMsg) : base(srcMsg) { } } }