public static Fraction ToFract(double val, double precision) { // find nearest fraction ulong intPart = (ulong)val; val -= intPart; Fraction low = new Fraction(0, 1); // "A" = 0/1 (a/b) Fraction high = new Fraction(1, 1); // "B" = 1/1 (c/d) for (; ; ) { Debug.Assert(low.Val <= val); Debug.Assert(high.Val >= val); // b*m - a // x = ------- // c - d*m double testLow = low.Denom * val - low.Num; double testHigh = high.Num - high.Denom * val; // test for match: // // m - a/b < precision // // ==> // // b * m - a < b * precision // // which is happening here: check both the current A and B fractions. //if (testHigh < high.denom * Precision) if (testHigh < precision) // [i_a] speed improvement; this is even better for irrational 'val' { break; // high is answer } //if (testLow < low.denom * Precision) if (testLow < precision) // [i_a] speed improvement; this is even better for irrational 'val' { // low is answer high = low; break; } double x1 = testHigh / testLow; double x2 = testLow / testHigh; // always choose the path where we find the largest change in direction: if (x1 > x2) { //double x1 = testHigh / testLow; // safety checks: are we going to be out of integer bounds? if ((x1 + 1) * low.Denom + high.Denom >= long.MaxValue) { break; } ulong n = (ulong)x1; // lower bound for m //int m = n + 1; // upper bound for m // a + x*c // ------- = m // b + x*d ulong hNum = n * low.Num + high.Num; ulong hDenom = n * low.Denom + high.Denom; //ulong l_num = m * low.num + high.num; //ulong l_denom = m * low.denom + high.denom; ulong lNum = hNum + low.Num; ulong lDenom = hDenom + low.Denom; low.Num = lNum; low.Denom = lDenom; high.Num = hNum; high.Denom = hDenom; } else { //double x2 = testLow / testHigh; // safety checks: are we going to be out of integer bounds? if (low.Denom + (x2 + 1) * high.Denom >= ulong.MaxValue) { break; } ulong n = (ulong)x2; // lower bound for m //ulong m = n + 1; // upper bound for m // a + x*c // ------- = m // b + x*d ulong lNum = low.Num + n * high.Num; ulong lDenom = low.Denom + n * high.Denom; //ulong h_num = low.num + m * high.num; //ulong h_denom = low.denom + m * high.denom; ulong hNum = lNum + high.Num; ulong hDenom = lDenom + high.Denom; high.Num = hNum; high.Denom = hDenom; low.Num = lNum; low.Denom = lDenom; } Debug.Assert(low.Val <= val); Debug.Assert(high.Val >= val); } high.Num += high.Denom * intPart; return high; }
public static Fraction ToFract(double val, double precision) { // find nearest fraction ulong intPart = (ulong)val; val -= intPart; Fraction low = new Fraction(0, 1); // "A" = 0/1 (a/b) Fraction high = new Fraction(1, 1); // "B" = 1/1 (c/d) for (; ;) { Debug.Assert(low.Val <= val); Debug.Assert(high.Val >= val); // b*m - a // x = ------- // c - d*m double testLow = low.Denom * val - low.Num; double testHigh = high.Num - high.Denom * val; // test for match: // // m - a/b < precision // // ==> // // b * m - a < b * precision // // which is happening here: check both the current A and B fractions. //if (testHigh < high.denom * Precision) if (testHigh < precision) // [i_a] speed improvement; this is even better for irrational 'val' { break; // high is answer } //if (testLow < low.denom * Precision) if (testLow < precision) // [i_a] speed improvement; this is even better for irrational 'val' { // low is answer high = low; break; } double x1 = testHigh / testLow; double x2 = testLow / testHigh; // always choose the path where we find the largest change in direction: if (x1 > x2) { //double x1 = testHigh / testLow; // safety checks: are we going to be out of integer bounds? if ((x1 + 1) * low.Denom + high.Denom >= long.MaxValue) { break; } ulong n = (ulong)x1; // lower bound for m //int m = n + 1; // upper bound for m // a + x*c // ------- = m // b + x*d ulong hNum = n * low.Num + high.Num; ulong hDenom = n * low.Denom + high.Denom; //ulong l_num = m * low.num + high.num; //ulong l_denom = m * low.denom + high.denom; ulong lNum = hNum + low.Num; ulong lDenom = hDenom + low.Denom; low.Num = lNum; low.Denom = lDenom; high.Num = hNum; high.Denom = hDenom; } else { //double x2 = testLow / testHigh; // safety checks: are we going to be out of integer bounds? if (low.Denom + (x2 + 1) * high.Denom >= ulong.MaxValue) { break; } ulong n = (ulong)x2; // lower bound for m //ulong m = n + 1; // upper bound for m // a + x*c // ------- = m // b + x*d ulong lNum = low.Num + n * high.Num; ulong lDenom = low.Denom + n * high.Denom; //ulong h_num = low.num + m * high.num; //ulong h_denom = low.denom + m * high.denom; ulong hNum = lNum + high.Num; ulong hDenom = lDenom + high.Denom; high.Num = hNum; high.Denom = hDenom; low.Num = lNum; low.Denom = lDenom; } Debug.Assert(low.Val <= val); Debug.Assert(high.Val >= val); } high.Num += high.Denom * intPart; return(high); }