diff options
author | Dominik Picheta <dominikpicheta@googlemail.com> | 2015-10-04 22:17:45 +0100 |
---|---|---|
committer | Dominik Picheta <dominikpicheta@googlemail.com> | 2015-10-04 22:17:45 +0100 |
commit | 6587f63672dacb732b879d517132bec8a4f862e4 (patch) | |
tree | 5366747bf93973c7f67480a772e2a966b2eb5bfa | |
parent | 8f2b15d170f2170f471711e0055d85f6d96c9a83 (diff) | |
parent | 77d4788655c261838e3fd48ec9efd92e91eb2173 (diff) | |
download | Nim-6587f63672dacb732b879d517132bec8a4f862e4.tar.gz |
Merge pull request #3415 from jlp765/rationals2
rationals add toRational(float) conversion
-rw-r--r-- | lib/pure/rationals.nim | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 7d9241412..72c64befc 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -39,6 +39,63 @@ proc toRational*[T:SomeInteger](x: T): Rational[T] = result.num = x result.den = 1 +proc toRationalSub(x: float, n: int): Rational[int] = + var + a = 0 + b, c, d = 1 + result = 0 // 1 # rational 0 + while b <= n and d <= n: + let ac = (a+c) + let bd = (b+d) + # scale by 1000 so not overflow for high precision + let mediant = (ac/1000) / (bd/1000) + if x == mediant: + if bd <= n: + result.num = ac + result.den = bd + return result + elif d > b: + result.num = c + result.den = d + return result + else: + result.num = a + result.den = b + return result + elif x > mediant: + a = ac + b = bd + else: + c = ac + d = bd + if (b > n): + return initRational(c, d) + return initRational(a, b) + +proc toRational*(x: float, n: int = high(int)): Rational[int] = + ## Calculate the best rational numerator and denominator + ## that approximates to `x`, where the denominator is + ## smaller than `n` (default is the largest possible + ## int to give maximum resolution) + ## + ## The algorithm is based on the Farey sequence named + ## after John Farey + ## + ## .. code-block:: Nim + ## import math, rationals + ## for i in 1..10: + ## let t = (10 ^ (i+3)).int + ## let x = toRational(PI, t) + ## let newPI = x.num / x.den + ## echo x, " ", newPI, " error: ", PI - newPI, " ", t + if x > 1: + result = toRationalSub(1.0/x, n) + swap(result.num, result.den) + elif x == 1.0: + result = 1 // 1 + else: + result = toRationalSub(x, n) + proc toFloat*[T](x: Rational[T]): float = ## Convert a rational number `x` to a float. x.num / x.den @@ -288,3 +345,8 @@ when isMainModule: assert toRational(5) == 5//1 assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 assert toInt(z) == 0 + + assert toRational(0.98765432) == 12345679 // 12500000 + assert toRational(0.1, 1000000) == 1 // 10 + assert toRational(0.9, 1000000) == 9 // 10 + assert toRational(PI) == 80143857 // 25510582 |