Hi All,
I need to convert a float to a fraction.. So 1.5 to 1 1/2..
The rational class would represent at least 3/2 well.. but I was surprised to
find that there is no way to easily go from float to rational..
Am I missing an easier way?
Thanks in advance
Luke Galea wrote:
Hi All,
I need to convert a float to a fraction.. So 1.5 to 1 1/2..
The rational class would represent at least 3/2 well.. but I was surprised to find that there is no way to easily go from float to rational..
Am I missing an easier way?
Thanks in advance
I wrote a small float => rational method:
class Float
def to_r
if self.nan?
return Rational(0,0) # Div by zero error
elsif self.infinite?
return Rational(self<0 ? -1 : 1,0) # Div by zero error
end
s,e,f = [self].pack("G").unpack("B*").first.unpack("AA11A52")
s = (-1)**s.to_i
e = e.to_i(2)
if e.nonzero? and e<2047
Rational(s)* Rational(2)**(e-1023)*Rational("1#{f}".to_i(2),0x10000000000000)
elsif e.zero?
Rational(s)* Rational(2)**(-1024)*Rational("0#{f}".to_i(2),0x10000000000000)
end
end
end
···
--
Jannis Harder
there is no built-in way (that I know of), but here are two methods I
wrote a while back that should cover all the bases:
···
On 5/4/05, Luke Galea <lgalea@gmmsolutions.com> wrote:
Hi All,
I need to convert a float to a fraction.. So 1.5 to 1 1/2..
The rational class would represent at least 3/2 well.. but I was surprised to
find that there is no way to easily go from float to rational..
Am I missing an easier way?
----
require 'mathn'
class Float
def to_r
n = 1
n *= 2 until (self*n) % 1 == 0
(self*n).to_i/n
end
def round_to_r
i, d = to_s.split /\./
i.to_i * 10**d.size + d.to_i / 10**d.size
end
end
----
#to_r directly converts the float to a rational, and includes any
intrinsic inaccuracies. This will be *exactly* equal to the original
float.
2.125.to_r
==>17/8
0.2.to_r
==>3602879701896397/18014398509481984
#round_to_r uses the displayed representation of the float to generate
a value that, while not always being the actual value of the float, is
much better for display, or if you know you want it rounded a tiny
bit.
0.2.round_to_r
==>1/5
0.23.round_to_r
==>23/100
It could deal with being a little smarter, for catching repeating
digits and the like.
cheers,
Mark
Jannis Harder wrote:
Luke Galea wrote:
Hi All,
I need to convert a float to a fraction.. So 1.5 to 1 1/2..
The rational class would represent at least 3/2 well.. but I was surprised to find that there is no way to easily go from float to rational.. Am I missing an easier way?
Thanks in advance
I wrote a small float => rational method:
[snipped code]
That a neat way of extracting the exponent! ![:wink: :wink:](https://emoji.discourse-cdn.com/twitter/wink.png?v=12)
Be carefull, howevery - this will not always give
the expected results. Try e.g.
puts (0.2).to_r
This yields: 3602879701896397/18014398509481984
which of course is nearly 2/5, but since
0.2 has infinitly many digits when converted
to a binary representation, a rounding error occurs.
greetings, Florian Pflug
Jannis Harder wrote:
Luke Galea wrote:
Hi All,
I need to convert a float to a fraction.. So 1.5 to 1 1/2..
The rational class would represent at least 3/2 well.. but I was surprised to find that there is no way to easily go from float to rational.. Am I missing an easier way?
Thanks in advance
I wrote a small float => rational method:
Here is another one - it's probably much slower, but works
better for corner-cases like (0.2).to_r
class Numeric
def to_r
chain_fractions.reverse.inject(nil) do |r,a|
Rational(a) + (r.nil? ? 0 : (Rational(1) / r))
end
end
end
class Integer
def chain_fractions
[self]
end
end
class Float
def chain_fractions
remainder = self
if ! block_given? then
coefficients = Array::new
end
while (true) do
if block_given? then
yield remainder.floor
else
coefficients << remainder.floor
end
break if remainder == remainder.floor
remainder = 1 / (remainder - remainder.floor)
end
coefficients
end
end
greetings, Florian Pflug
Florian G. Pflug schrieb:
That a neat way of extracting the exponent! ![:wink: :wink:](https://emoji.discourse-cdn.com/twitter/wink.png?v=12)
Be carefull, howevery - this will not always give
the expected results. Try e.g.
puts (0.2).to_r
This yields: 3602879701896397/18014398509481984
which of course is nearly 2/5, but since
0.2 has infinitly many digits when converted
to a binary representation, a rounding error occurs.
For serious use he'd probably better using "continued fractions" based
conversion algorithm (check out any googled side with this search term)
+ error term
def to_r(eps = 10**(-13))
...
end
/Christoph
Hello,
Florian G. Pflug schrieb:
<snip>
For serious use he'd probably better using "continued fractions" based
conversion algorithm (check out any googled side with this search term)
+ error term
def to_r(eps = 10**(-13))
...
end
<snip>
Knuth has a discussion of this in `Seminumerical Algorithms, The Art
of Computer Programming, vol. 2.'
In the third edition, it is in sec. 4.5.3 (pp. 356ff). See also
exercise 4.5.3.2.
Best regards,
···
On Fri, May 06, 2005 at 03:07:40AM +0900, Christoph wrote:
--
Zane Dodson
Zane Dodson schrieb:
Knuth has a discussion of this in `Seminumerical Algorithms, The Art
of Computer Programming, vol. 2.'
In the third edition, it is in sec. 4.5.3 (pp. 356ff). See also
exercise 4.5.3.2
Florian's solution is of course nothing but continued fraction
- without the (relative) error term he could be into a long wait
calling #to_r unless he is very lucky ..
/Christoph
Thanks to those who mentioned the "continued fractions" method. Here's
a new implementation:
···
On 5/5/05, Christoph <chr_mail@gmx.net> wrote:
Zane Dodson schrieb:
>Knuth has a discussion of this in `Seminumerical Algorithms, The Art
>of Computer Programming, vol. 2.'
>
>In the third edition, it is in sec. 4.5.3 (pp. 356ff). See also
>exercise 4.5.3.2
>
>
Florian's solution is of course nothing but continued fraction
- without the (relative) error term he could be into a long wait
calling #to_r unless he is very lucky ..
----
require 'mathn'
class Numeric
def inverse
1/self
end
end
class Float
def to_r
n = 1
n *= 2 until (self*n) % 1 == 0
(self*n).to_i/n
end
def round_to_r
return self.to_i if self % 1 == 0
n = self
ops =
count = 0
until ((n%1).round - n%1).abs < 1e-8 || count > 20 ||
n.abs == 1.0/0.0 || n == 0.0/0.0
int, dec = n.divmod 1
ops.concat [[:+, int.to_i], [:inverse]]
n = 1/dec
count += 1
end
n = n.round
ops.reverse.inject(n.round){|n, op| n.send(*op)}
end
end
----
Use Float#to_r for an exact representation of the float value, or
Float#round_to_r for an extremely close representation of it.
cheers,
Mark
Thanks for all the great responses!
So.. I guess the next question is: Does everyone think this is a worthwhile
addition to the Ruby STDLib?If so, how do we go about getting it added to
Rational.rb?
···
On Thursday 05 May 2005 17:27, Mark Hubbart wrote:
On 5/5/05, Christoph <chr_mail@gmx.net> wrote:
> Zane Dodson schrieb:
> >Knuth has a discussion of this in `Seminumerical Algorithms, The Art
> >of Computer Programming, vol. 2.'
> >
> >In the third edition, it is in sec. 4.5.3 (pp. 356ff). See also
> >exercise 4.5.3.2
>
> Florian's solution is of course nothing but continued fraction
> - without the (relative) error term he could be into a long wait
> calling #to_r unless he is very lucky ..
Thanks to those who mentioned the "continued fractions" method. Here's
a new implementation:
----
require 'mathn'
class Numeric
def inverse
1/self
end
end
class Float
def to_r
n = 1
n *= 2 until (self*n) % 1 == 0
(self*n).to_i/n
end
def round_to_r
return self.to_i if self % 1 == 0
n = self
ops =
count = 0
until ((n%1).round - n%1).abs < 1e-8 || count > 20 ||
n.abs == 1.0/0.0 || n == 0.0/0.0
int, dec = n.divmod 1
ops.concat [[:+, int.to_i], [:inverse]]
n = 1/dec
count += 1
end
n = n.round
ops.reverse.inject(n.round){|n, op| n.send(*op)}
end
end
----
Use Float#to_r for an exact representation of the float value, or
Float#round_to_r for an extremely close representation of it.
cheers,
Mark