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)