christopher.lord.ac

christopher.lord.ac

Christopher Lord  //  I work on compilers for a major corporation, specialized in computer languages and the parsing and optimization thereof. In my spare cycles I hack on Haskell, Ruby, and Objective C. Outside of programming, I am an outdoorsman, a skilled photographer, a student of typography and design, and a patient, better driver. buzz.

Dec 2 / 4:09pm

Fractions in Ruby

For a cooking website I recently worked on, I needed to display decimal floating point numbers as fractions. I needed to come up with the fraction closest to what the user typed. For example, 0.33 should resolve to ⅓. When I googled for a solution, most of the code I found was slow, buggy, and too precise (returning 33/100 for the above example.)

I decided to widen my search to C, and found a piece of code on Stack Overflow written by David Eppstein in 1993. It uses the theory of continued fractions to approach the correct value, but stops when the denominator reaches some value. 
The limitation of such an algorithm is that we can't choose to leave out unnatural denominators (that's a topic for some future post: why don't people like to use 7 as a denominator?). 
Anyway, I ended up wrapping up Eppstein's code in a Ruby gem entitled "fraction" and throwing it up on Gemcutter in case anyone needs similar functionality. Using this gem is easy:
require 'fraction'
num, den = 0.33.fraction   # num==1, den==3

# You can also get the error
num,den,err = 0.33.fraction   #=> [1, 3, -0.0033333333333333]

# you can choose a different maximum denominator than the default value of 10:
num, den = 0.51.fraction(100) #[51, 100, 0.0]

You can get fraction from gemcutter

 

 

Here is a quick test of this code:

require 'rubygems'
require 'fraction'

# Google for "ruby fraction" and you find this code

class Float
  def number_decimal_places
    self.to_s.length-2
  end
  def to_fraction
    higher = 10**self.number_decimal_places
    lower = self*higher
    gcden = greatest_common_divisor(higher, lower)
    return (lower/gcden).round, (higher/gcden).round
  end
private
  def greatest_common_divisor(a, b)
     while a%b != 0
       a,b = b.round,(a%b).round
     end 
     return b
  end
end

starttime = Time.now
1_000_000.times do 
  n,d=0.5.to_fraction
end
endtime = Time.now
feeling_lucky=endtime-starttime
puts "I'm Feeling Lucky: " + feeling_lucky.to_s + "s"


starttime = Time.now
1_000_000.times do 
  n,d=0.5.fraction
end
endtime = Time.now
fractionmod=endtime-starttime
puts "fraction: " + fractionmod.to_s + "s"

starttime = Time.now
1_000_000.times do 
  n,d=[1,2]
end
endtime = Time.now
max_speed=endtime-starttime
n,d=(fractionmod-max_speed).fraction
puts "algorithm itself requires only #{n}/#{d} of a second for 1,000,000 iterations"

Filed under  //  ruby  

Comments (1)

Oct 8 / 4:39am

MacRuby » MacRuby 0.5 beta 1

The new MacRuby 0.5 runtime is built upon LLVM, a compiler infrastructure also sponsored by Apple. Thanks to LLVM, MacRuby is able to transform the Ruby abstract syntax tree (AST) from the parser directly into highly optimized machine code. MacRuby supports both Just in Time (JIT) and Ahead of Time (AOT) compilation. The JIT mode will compile down the code at runtime, and the AOT mode will allow you to save on disk the compilation result. AOT compilation makes MacRuby a true Ruby compiler.

I'm excited about the prospect of building cocoa apps in Ruby, and compiling everything down to objects. What's better than that? Well, they've fixed the concurrent performance of Ruby by sharding the global interpreter lock, and they threw in GCD for good measure. I believe developments like this herald the end of C/C++ hegemony. At some point in the near future, this will be the default method of building apps on OS X.

Filed under  //  os x   ruby  

Comments (0)