What if you do not have exp()?

Arjen Markus (14 july 2008) Recently, on the Tclers' chat, someone had a problem with the [expr] command as in their installation tclsh apparently did not have any math functions. While it was easy enough to provide an alternative for sqrt() (see Square root for instance), a function like exp() presents more difficulties. Or does it?

Not really: Abramowitz and Stegun come to the rescue, along with a few properties of the exponential function. The result is remarkably accurate. I leave the log, sin, cos, tan, acos, asin and atan functions as an exercise.


# approx_exp.tcl --
#     An approximation of the exponential function
#     (Based on Abramowicz and Stegun)
#

# exp --
#     Compute an approximation of exp(x)
#
# Arguments:
#     x      Value for which to evaluate the exponential function
#
# Returns:
#     Approximation of exp(x)
#
proc exp {x} {
    set ln2   0.693147805599543
    set E     2.718281828459045
    set sqrtE 1.6487212707001282

    #
    # As the valid range for the basic approximation is 0 - ln(2),
    # reduce the argument
    #
    set factor    1.0
    if { $x > 0.0 } {
        set reciproke 1
    } else {
        set reciproke 0
        set x [expr {-$x}]
    }
    while { $x > 1.0 } {
        set x [expr {$x - 1.0}]
        set factor [expr {$factor * $E}]
    }

    if { $x > $ln2 } {
        set x [expr {$x - 0.5}]
        set factor [expr {$factor * $sqrtE}]
    }

    #puts "Reduced x: $x $factor"
    set exp [expr {(1.0 + $x * (-0.9998684 + $x * (0.4982926 +
                        + $x * (-0.1595332 + $x * 0.0293641))))}]

    if { $reciproke } {
        set exp [expr {$factor / $exp}]
    } else {
        set exp [expr {$factor * $exp}]
    }
    return $exp
}

# main --
#     Put it to the test
#
puts [format "%12s %12s%12s%12s%12s" X "exp x" "exp(x)" "abs. diff" "rel. diff"]
foreach x {0.1 0.2 0.5 1.3 2.4 4.05 -0.1 -0.2 -0.5 1.3 2.4 4.05} {
    set exp1 [exp $x]
    set exp2 [expr {exp($x)}]
    puts [format "%12.4f:%12.4f%12.4f%12.4e%12.4e" $x $exp1 $exp2 [expr {$exp1-$exp2}] \
             [expr {$exp1/$exp2-1.0}]]
}