math::exact(n)

πŸ“¦
version 2.0 Β· 2 versions
, in fedora 43, fedora 44, nixos 26.05, nixpkgs unstable

πŸ’– math::exact(n) β€” bestie, we're doing EXACT math πŸ’–

okay so this is a page from the Tcl Math Library (yes, the whole library, not just one lil module) and it's about the math::exact package. sit down, grab a boba, we're gonna learn exact real arithmetic and honestly? it's giving main character energy. 🧚✨


🌸 NAME 🌸

math::exact β€” Exact Real Arithmetic

like... exact exact. not "close enough" exact. EXACT. πŸ’…


✨ SYNOPSIS ✨

package require Tcl 8.6 9

package require grammar::aycock 1.0

package require math::exact 1.0.2

::math::exact::exactexpr expr

number ref

number unref

number asPrint precision

number asFloat precision

πŸ¦‹ DESCRIPTION πŸ¦‹

so the exactexpr command inside the math::exact package lets you do exact computations over the computable real numbers. and bestie, listen β€” these are not arbitrary-precision calculations. that's a different thing. these are exact, no cap. the numbers get represented by algorithms that just keep producing successive approximations, forever, like a girl who keeps sending "one more thing" texts. 😭

at the end of a calculation you, the caller, can request a given precision for the final result β€” and all the intermediate results get computed to whatever precision is necessary to satisfy that request. it's adaptive. it's smart. it's iconic. periodt. πŸ‘‘


πŸŽ€ PROCEDURES πŸŽ€

this is the main event, the procedure that gets you in the door:

  • ::math::exact::exactexpr expr β€” takes a mathematical expression written in Tcl syntax, and hands you back an object representing the program that will calculate successive approximations of that expression's value. we're gonna call the thing it returns an exact real number. 🫢
  • number ref β€” bumps up the reference count of a given exact real number. (yes bestie, you have to do this. keep reading.)
  • number unref β€” brings the reference count back down, and destroys the number if the reference count hits zero. bye babe 🫑
  • number asPrint precision β€” formats the number for printing at the precision you asked for. (how precision actually gets interpreted? there's a whole section, keep scrolling πŸ’…) numbers that are known to be rational get formatted as fractions. cute.
  • number asFloat precision β€” also formats the number for printing at the specified precision, same precision rules. but this one formats all numbers in floating-point E format. no fractions here, we're going full floatie. 🌊

🌈 PARAMETERS 🌈

  • expr β€” the expression to evaluate. the syntax is the same as Tcl's syntax, BUT the set of operations is smaller. (see Expressions down below for the deets πŸ‘‡)
  • number β€” the object that an earlier call to math::exact::exactexpr gave you.
  • precision β€” the requested 'precision' of the result. and okay so this is the part that lowkey trips people up, so let's be real precise about precision: it's (approximately) the absolute value of the binary exponent plus the number of bits of the binary significand. fr fr.

so like, to return results at IEEE-754 double precision, you need 56 bits plus the exponent. let's break that down:

  • numbers between 1/2 and 2 β†’ need a precision of 57
  • numbers between 1/4 and 1/2, or between 2 and 4 β†’ need 58
  • numbers between 1/8 and 1/4, or between 4 and 8 β†’ need 59
  • and so on and so on and so on ♾️

πŸ’œ EXPRESSIONS πŸ’œ

the math::exact::exactexpr command accepts expressions from a subset of Tcl's syntax. here's everything you're allowed to use in an expression:

  • Decimal integers. πŸ”’
  • Variable references with the dollar sign ($). the variable's value has to be the result of another call to math::exact::exactexpr. and heads up: the reference count of that value gets increased by one for each position where it shows up in the expression. every. single. position. πŸ‘€
  • The exponentiation operator (**).
  • Unary plus (+) and minus (-) operators.
  • Multiplication (*) and division (/) operators.
  • Parentheses used for grouping.
  • Functions. (see Functions right below for the whole list 🫢)

πŸ”₯ FUNCTIONS πŸ”₯

here's every function you can use inside an exact real expression. no more, no less, don't try to sneak anything in bestie:

  • acos(x) β€” the inverse cosine of x. result comes out in radians. the absolute value of x must be less than 1.
  • acosh(x) β€” the inverse hyperbolic cosine of x. x must be greater than 1.
  • asin(x) β€” the inverse sine of x. result is in radians. absolute value of x must be less than 1.
  • asinh(x) β€” the inverse hyperbolic sine of x.
  • atan(x) β€” the inverse tangent of x. result is in radians.
  • atanh(x) β€” the inverse hyperbolic tangent of x. the absolute value of x must be less than 1.
  • cos(x) β€” the cosine of x. x is expressed in radians.
  • cosh(x) β€” the hyperbolic cosine of x.
  • e() β€” the base of the natural logarithms = 2.71828...
  • exp(x) β€” the exponential function of x.
  • log(x) β€” the natural logarithm of x. x must be positive.
  • pi() β€” the value of pi = 3.15159... (yes, that's what the page says, we don't editorialize πŸ’€)
  • sin(x) β€” the sine of x. x is expressed in radians.
  • sinh(x) β€” the hyperbolic sine of x.
  • sqrt(x) β€” the square root of x. x must be positive.
  • tan(x) β€” the tangent of x. x is expressed in radians.
  • tanh(x) β€” the hyperbolic tangent of x.

⭐ SUMMARY ⭐

so the math::exact::exactexpr command gives you a whole system for doing exact arithmetic over computable real numbers, where the numbers are literally algorithms for successive approximation. 🀯

here's the vibe β€” an example that implements the high-school quadratic formula:

namespace import math::exact::exactexpr
proc exactquad {a b c} {
    set d [[exactexpr {sqrt($b*$b - 4*$a*$c)}] ref]
    set r0 [[exactexpr {(-$b - $d) / (2 * $a)}] ref]
    set r1 [[exactexpr {(-$b + $d) / (2 * $a)}] ref]
    $d unref
    return [list $r0 $r1]
}

set a [[exactexpr 1] ref]
set b [[exactexpr 200] ref]
set c [[exactexpr {(-3/2) * 10**-12}] ref]
lassign [exactquad $a $b $c] r0 r1
$a unref; $b unref; $c unref
puts [list [$r0 asFloat 70] [$r1 asFloat 110]]
$r0 unref; $r1 unref

and the program prints this result:

-2.000000000000000075e2 7.499999999999999719e-15

now here's the tea 🍡 β€” if IEEE-754 floating point had been used instead, a catastrophic roundoff error would have yielded a smaller root that is a factor of two too high:

-200.0 1.4210854715202004e-14

the exactexpr invocations should be pretty self-explanatory. the other commands of note are ref and unref. and bestie, this part is IMPORTANT: it's on you, the caller, to keep track of references to exact expressions β€” you call ref every single time an exact expression gets stored in a variable, and you call unref every time that variable goes out of scope or gets overwritten. no cap, if you slack on this you will have a bad time. 😭

and the asFloat method emits decimal digits for as long as the requested precision supports them. it terminates when the requested precision yields an uncertainty of more than one unit in the least significant digit. so it knows when to stop. queen behavior. πŸ‘‘


πŸ“ CATEGORY πŸ“

Mathematics


Copyright (c) 2015 Kevin B. Kenny <kennykb@acm.org>

Redistribution permitted under the terms of the Open Publication License <http://www.opencontent.org/openpub/>


okay that's the whole page, no skips, no cuts, every single flag and function accounted for. you're welcome. 🫢

and a lil sign-off from me to you: this beauty comes from tcllib, version 1.0.2, and it's the math::exact(n) page. go do exact math, bestie. slay. πŸ’…βœ¨πŸŒΈ

πŸ“¦ where this page comes from