Posted to tcl by kbk at Fri Jul 04 17:18:54 GMT 2008view pretty

namespace path {::tcl::mathop ::tcl::mathfunc}

# Take a decimal number and convert it to a rational. Return a two
# element list of numerator and denominator

proc to-rational {n} {
    if {![regexp {^([-+]?[0-9]*)(?:[.]([0-9]*))?$} $n -> integer fraction]} {
	return -code error "$n is not a number"
    }
    set denom [** 10 [string length $fraction]]

    # TODO - Reduce to lowest terms

    return [list $integer$fraction $denom]
}
puts [to-rational 6.25]

# Take a decimal number representing a percentage and convert it to a
# rational.  Return a two element list of numerator and denominator

proc percent {n} {
    lassign [to-rational $n] num denom

    # TODO - Reduce to lowest terms

    return [list $num [* 100 $denom]]
}

puts [percent 6.25]

# Multiply two rationals

proc rat-times {m n} {
    set result {}
    foreach x $m y $n {
	lappend result [* $x $y]
    }
    return $result
}

# What is 6.25% of 19.45?

set val [rat-times [percent 6.25] [to-rational 19.45]]
puts $val

# Take the integer part of a rational, with banker's rounding

proc rat-int {n} {
    lassign $n num denom
    set ipart [/ $num $denom]
    set fpart [% $num $denom]
    if {$fpart < 0} {
	incr ipart -1
	incr fpart $denom
    }
    if {2 * $fpart < $denom} {
	return $ipart
    } elseif {2 * $fpart > $denom} {
	return [+ $ipart 1]
    } else {
	return [+ $ipart [% $ipart 2]]
    }
}

# How many pennies is 6.25% times 19.45?

set resultCents [rat-int [rat-times {100 1} $val]]
puts $resultCents

# Convert an integer count of pennies to standard notation

proc penniesToDollars {cents} {
    # TODO - Doesn't handle + and - signs correctly
    if {[string length $cents] < 3} {
	set cents [string repeat [- 3 [string length $cents]] 0]$cents
    }
    return [string range $cents 0 end-2].[string range $cents end-1 end]
}
puts \$[penniesToDollars $resultCents]