最近又对分数类做了一些修改,有些优化有些改写。
抄自python的限制分母大小的方法还是会有bug(Σ( ° △ °|||)︴。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Rainbow.Core 7 { 8 public class Fraction : IComparable, IComparable<Fraction>, IEquatable<Fraction> 9 { 10 #region fields 11 private long _numerator = 0; 12 private long _denominator = 1; 13 #endregion 14 15 #region properties 16 /// <summary> 17 /// 分子 18 /// </summary> 19 public long Numerator 20 { 21 get { return _numerator; } 22 set 23 { 24 SetNumAndDen(value, Denominator); 25 } 26 } 27 /// <summary> 28 /// 分母 29 /// </summary> 30 public long Denominator 31 { 32 get { return _denominator; } 33 set 34 {//分母分子不能同为负 35 JudgeDenominator(value); 36 SetNumAndDen(Numerator, value); 37 } 38 } 39 /// <summary> 40 /// 值 41 /// </summary> 42 public double Value 43 { 44 get { return Numerator / (double)Denominator; } 45 } 46 #endregion 47 48 #region constructors 49 50 public Fraction() 51 : this(0, 1) 52 { } 53 54 public Fraction(long srcNum, long srcDen) 55 { 56 JudgeDenominator(srcDen); 57 SetNumAndDen(srcNum, srcDen); 58 } 59 60 public Fraction(string srcNum, string srcDen) 61 { 62 long numerator, denominator; 63 var result = Int64.TryParse(srcNum, out numerator); 64 if (result == false) 65 throw new Exception("Can not convert numerator:" + srcNum + " to a number."); 66 result = Int64.TryParse(srcNum, out denominator); 67 if (result == false) 68 throw new Exception("Can not convert denominator:" + srcDen + " to a number."); 69 70 JudgeDenominator(denominator); 71 SetNumAndDen(numerator, denominator); 72 } 73 74 public Fraction(double srcDouble) 75 { 76 var tmp = ToFraction(srcDouble); 77 SetNumAndDen(tmp.Numerator, tmp.Denominator); 78 } 79 80 public Fraction(string srcString) 81 { 82 var tmp = ToFraction(srcString); 83 SetNumAndDen(tmp.Numerator, tmp.Denominator); 84 } 85 #endregion 86 87 #region methods 88 /// <summary> 89 /// 设置分子分母值 90 /// </summary> 91 /// <param name="destNum"></param> 92 /// <param name="destDen"></param> 93 private void SetNumAndDen(long destNum, long destDen) 94 { 95 var negative = IsDifferentSign(destNum, destDen); 96 _numerator = negative ? -Math.Abs(destNum) : Math.Abs(destNum); 97 _denominator = Math.Abs(destDen); 98 } 99 /// <summary> 100 /// 判断分母不为0 101 /// </summary> 102 /// <param name="srcDen"></param> 103 private void JudgeDenominator(long srcDen) 104 { 105 if (srcDen == 0) 106 throw new Exception("Denominator can not be zero."); 107 } 108 /// <summary> 109 /// 分子分母是否异号 110 /// </summary> 111 /// <param name="destNum"></param> 112 /// <param name="destDen"></param> 113 /// <returns></returns> 114 private static bool IsDifferentSign(long destNum, long destDen) 115 { 116 return ((destNum >> 63) ^ (destDen >> 63)) == 1 ? true : false; 117 } 118 119 public static Fraction ToFraction(double srcDouble) 120 { 121 var result = new Fraction(); 122 try 123 { 124 checked 125 { 126 string srcString = srcDouble.ToString(); 127 double tmpNum = srcDouble; 128 long tmpDen = 1; 129 while (srcString.IndexOf('E') > 0) 130 { 131 tmpNum *= 10; 132 tmpDen *= 10; 133 srcString = tmpNum.ToString(); 134 } 135 if (srcString.Contains('.')) 136 { 137 int lengthAfterDot = srcString.Split('.')[1].Length; 138 while (lengthAfterDot > 0) 139 { 140 tmpNum *= 10; 141 tmpDen *= 10; 142 lengthAfterDot--; 143 } 144 } 145 result = new Fraction((long)Math.Round(tmpNum), tmpDen); 146 } 147 } 148 catch (OverflowException) 149 { 150 throw new Exception("Overflow while converting " + srcDouble + " to fraction."); 151 } 152 return result; 153 } 154 155 public static Fraction ToFraction(string srcString) 156 { 157 Fraction result = new Fraction(); 158 double srcDouble = 0; 159 if (double.TryParse(srcString, out srcDouble)) 160 {//形如1.23,1E23 161 result = ToFraction(srcDouble); 162 } 163 else if (new System.Text.RegularExpressions.Regex(@"^-?d+/-?d+$").IsMatch(srcString)) 164 { 165 result = new Fraction(srcString.Split('/')[0], srcString.Split('/')[1]); 166 } 167 else 168 { 169 throw new Exception(string.Format("Failed while converting <{0}> to fraction.", srcString)); 170 } 171 return result; 172 } 173 /// <summary> 174 /// 约分 175 /// </summary> 176 /// <returns></returns> 177 public Fraction ReduceNumAndDen() 178 { 179 var gcd = GreatestCommonDivisor(Numerator, Denominator); 180 return new Fraction(Numerator / gcd, Denominator / gcd); 181 } 182 /// <summary> 183 /// 辗转相除法求最大公约数 184 /// </summary> 185 /// <param name="srcBig"></param> 186 /// <param name="srcSmall"></param> 187 /// <returns></returns> 188 private static long GreatestCommonDivisor(long srcBig, long srcSmall) 189 { 190 srcBig = Math.Abs(srcBig); 191 srcSmall = Math.Abs(srcSmall); 192 if (srcSmall > srcBig) 193 { 194 srcSmall = srcSmall ^ srcBig; 195 srcBig = srcSmall ^ srcBig; 196 srcSmall = srcSmall ^ srcBig; 197 } 198 if (srcBig % srcSmall == 0) 199 { 200 return srcSmall; 201 } 202 else 203 { 204 return GreatestCommonDivisor(srcSmall, srcBig % srcSmall); 205 } 206 } 207 /// <summary> 208 /// 限制分母最大值 209 /// 抄自python,个别情况下会出异常 210 /// </summary> 211 /// <param name="srcMaxDen"></param> 212 /// <returns></returns> 213 public Fraction LimitDenominator(long srcMaxDen) 214 { 215 if (srcMaxDen < 1) 216 throw new Exception("Denominator should bigger than 0."); 217 218 if (Denominator < srcMaxDen) 219 return this; 220 221 try 222 { 223 long p0 = 0; 224 long q0 = 1; 225 long p1 = 1; 226 long q1 = 0; 227 long n = Numerator; 228 long d = Denominator; 229 while (true) 230 { 231 long a = n / d; 232 long q2 = q0 + a * q1; 233 if (q2 > srcMaxDen) 234 { 235 break; 236 } 237 long p0Old = p0; 238 p0 = p1; 239 q0 = q1; 240 p1 = p0Old + a * p1; 241 q1 = q2; 242 long nOld = n; 243 n = d; 244 d = nOld - a * d; 245 } 246 long k = (srcMaxDen - q0) / q1; 247 var result1 = new Fraction(p0 + k * p1, q0 + k * q1); 248 var result2 = new Fraction(p1, q1); 249 250 if (Math.Abs(result2.Value - this.Value) <= Math.Abs(result1.Value - this.Value)) 251 return result2; 252 else 253 return result1; 254 } 255 catch (DivideByZeroException) 256 { 257 //跟python结果不一致,要调查 258 return this; 259 } 260 } 261 #endregion 262 263 #region ToString 264 /// <summary> 265 /// 转化为x/y形式 266 /// </summary> 267 /// <returns></returns> 268 public override string ToString() 269 { 270 return Denominator == 0 ? "NaN" : string.Format("{0}/{1}", Numerator, Denominator); 271 } 272 /// <summary> 273 /// 转化为double字符串 274 /// </summary> 275 /// <returns></returns> 276 public string ToDoubleString() 277 { 278 return Value.ToString(); 279 } 280 #endregion 281 282 #region interface 283 284 #region IComparable<Fraction> 成员 285 286 public int CompareTo(Fraction other) 287 { 288 return this.Value.CompareTo(other.Value); 289 } 290 291 #endregion 292 293 #region IEquatable<Fraction> 成员 294 295 public bool Equals(Fraction other) 296 { 297 return this == other; 298 } 299 300 #endregion 301 302 #region IComparable 成员 303 304 public int CompareTo(object obj) 305 { 306 int result; 307 double tmpValue; 308 if (obj == null) 309 throw new Exception("比较失败"); 310 311 if (obj is string) 312 { 313 var strFrac = obj as string; 314 if (this > ToFraction(strFrac)) 315 result = 1; 316 else if (this < ToFraction(strFrac)) 317 result = -1; 318 else 319 result = 0; 320 } 321 else if (double.TryParse(obj as string, out tmpValue)) 322 { 323 result = Value.CompareTo(tmpValue); 324 } 325 else 326 { 327 throw new Exception("比较失败"); 328 } 329 return result; 330 } 331 332 #endregion 333 334 #endregion 335 336 /// <summary> 337 /// fraction to double 338 /// </summary> 339 /// <param name="srcFrac"></param> 340 /// <returns></returns> 341 public static implicit operator double(Fraction srcFrac) 342 { 343 return srcFrac.Value; 344 } 345 346 #region operator 347 348 //一元运算 349 public static Fraction operator -(Fraction srcFrac) 350 { 351 return new Fraction(srcFrac.Numerator * -1, srcFrac.Denominator); 352 } 353 354 //二元逻辑运算 355 public static bool operator >(Fraction left, Fraction right) 356 { 357 return left.Value > right.Value; 358 } 359 public static bool operator >=(Fraction left, Fraction right) 360 { 361 return left.Value >= right.Value; 362 } 363 public static bool operator <(Fraction left, Fraction right) 364 { 365 return left.Value < right.Value; 366 } 367 public static bool operator <=(Fraction left, Fraction right) 368 { 369 return left.Value <= right.Value; 370 } 371 public static bool operator ==(Fraction left, Fraction right) 372 { 373 return left.Value == right.Value; 374 } 375 public static bool operator !=(Fraction left, Fraction right) 376 { 377 return left.Value != right.Value; 378 } 379 380 //二元算术运算 381 private static Fraction AddFraction(Fraction left, Fraction right) 382 { 383 var result = new Fraction(); 384 result.Denominator = left.Denominator * right.Denominator; 385 result.Numerator = left.Numerator * right.Denominator + right.Numerator * left.Denominator; 386 return result; 387 } 388 private static Fraction SubFraction(Fraction left, Fraction right) 389 { 390 var result = new Fraction(); 391 result.Denominator = left.Denominator * right.Denominator; 392 result.Numerator = left.Numerator * right.Denominator - right.Numerator * left.Denominator; 393 return result; 394 } 395 private static Fraction MultFraction(Fraction left, Fraction right) 396 { 397 var result = new Fraction(); 398 result.Denominator = left.Denominator * right.Denominator; 399 result.Numerator = left.Numerator * right.Numerator; 400 return result; 401 } 402 private static Fraction DivFraction(Fraction left, Fraction right) 403 { 404 var result = new Fraction(); 405 result.Denominator = left.Denominator * right.Numerator; 406 result.Numerator = left.Numerator * right.Denominator; 407 return result; 408 } 409 410 public static Fraction operator +(Fraction left, Fraction right) 411 { 412 return AddFraction(left, right); 413 } 414 public static Fraction operator -(Fraction left, Fraction right) 415 { 416 return SubFraction(left, right); 417 } 418 public static Fraction operator *(Fraction left, Fraction right) 419 { 420 return MultFraction(left, right); 421 } 422 public static Fraction operator /(Fraction left, Fraction right) 423 { 424 return DivFraction(left, right); 425 } 426 #endregion 427 } 428 }