## Tricky catch

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)

 Category Mathematics