KBK (11 January 2002) - Having seen RS's fraction procedures in Bag of Algorithms, I saw a challenge: write a procedure that comes as close as possible to converting ANY float to a fraction. This actually isn't as hard as might seem.
The following code was my first attempt, which stayed on the Wiki for over a year. It does the job reasonably well, albeit slowly. (There's a faster version over in Fraction math.) One thing that might interest Tcl'ers about it is the way that catch is used as a control structure. The throw inside the quotient_rep procedure is used as a convenient way of breaking out of the 'for' and 'while' loops simultaneously.
It's interesting to walk through the code when quotient_rep is given an integer as a parameter. It gets a division by zero, which gets caught and handled. In fact, if integer overflow threw an error, there would be no need for any termination test in the while { 1 } loop at all!
# Convert a floating point number to a pair of integers
# that are the numerator and denominator of its fractional
# representation. The optional arg "maxint" is the largest
# integer that will appear in the fraction, e.g.,
# [quotient_rep 3.14159 1000] -> {355 113}
proc quotient_rep { num { maxint 2147483647 } } {
set i [expr { int( $num ) }]
set f [expr { $num - $i }]
set retval [list $i 1]
set clist {}
catch {
while { 1 } {
set clist [linsert $clist 0 $i]
set n 0
set d 1
foreach c $clist {
if { ( 1.0 * $c * $d + $n ) > $maxint } {
throw "break two loops at once!"
} else {
set newd [expr { $c * $d + $n }]
set n $d
set d $newd
}
}
set retval [list $d $n]
# the following calculations can overflow or
# zero divide, but the outer catch does the
# right thing!
set num [expr { 1.0 / $f }]
set i [expr { int( $num ) }]
set f [expr { $num - $i }]
}
}
return $retval
}(The Zen joke that the [throw] command is undefined was intentional)