Repository navigation
Category Math Library
Home › All Categories › Math Library
#import "std/numeric"
#import "std/money"
using numeric, moneyThe built-in numeric surface is ABS SGN SQR SIN COS TAN ASIN ACOS ATN ATAN2 EXP LOG INT FIX FRAC RND RANDOMIZE and the operators. No MIN, no MAX, no ROUND, no pi, no way to ask whether a DOUBLE is NaN, no money type, no complex numbers, no vectors, no statistics, and no way to tell a failed parse from a legitimate zero.
These modules add those. Everything is a library behind an #import -- std/numeric, and std/money for the money types. Nothing becomes a keyword and nothing existing changes.
Four things the language gets wrong today. All four verified by compiling them, not asserted.
^ always yields DOUBLE. There is no integer exponentiation: a ^ b converts to floating point, calls pow, converts back. Above 2^53 the result is wrong even when operands and true result all fit in a LONGINT. Constant folding conceals it -- a literal 2 ^ 62 is exact -- so it appears only once the operands become variables.
dim as int64 b = 3, e = 38
print b ^ e ' 1350851717672992000 -- exact answer ends 089Use IPow( 3LL, 38LL ).
MOD truncates. Every wrapping index, hash bucket and day-of-week computation wants 2.
print -7 mod 3 ' -1Use FloorMod( -7, 3 ).
VAL cannot fail. Garbage and a legitimate zero are the same answer, and val("12abc") is 12 -- a prefix parse with the tail silently discarded.
print val( "abc" ) ' 0
print val( "0" ) ' 0Use TryParseDouble( s ).
STR does not round-trip. The value that comes back is not the value that went in. str(1.0/3.0) happens to survive, so the failure is data-dependent and shows up later, in a file already on disk.
print str( 0.1 + 0.2 ) ' 0.3
print val( str( 0.1+0.2 ) ) = 0.1+0.2 ' FALSEUse ToString( d ).
None of these four are fixed in place. ^, MOD, VAL, STR, RND and RANDOMIZE are left exactly as they are. Each defect gets a correct neighbour under a different name.
dim as int32 min = 1, max = 2, round = 3, clamp = 4 ' still compilesThose four are the most obvious names here and all four are legal identifiers in tiko. Plenty of code uses them as variable names, so everything belongs to the module and using numeric is your decision. Your own local shadows the library cleanly and the qualified name still works:
#import "std/numeric"
using numeric
dim as float64 e = 2.0
print e, numeric.E ' 2 2.718281828459045Because these modules add no keyword and no grammar, they cannot change any existing diagnostic.
PI TAU E SQRT2 LN2 LN10 DEG2RAD RAD2DEG
EPSILON_DOUBLE MAX_DOUBLE MIN_POSITIVE_DOUBLE
EPSILON_SINGLE MAX_SINGLE MIN_POSITIVE_SINGLE
The non-finite values are functions, not constants -- const as double N = 0.0/0.0 is a compile-time division by zero and does not compile:
NaN( ) Infinity( ) NegInfinity( )
NaNF( ) InfinityF( ) NegInfinityF( )
MinSubnormalDouble( ) MinSubnormalSingle( )
Integer bounds are not duplicated here; fblimits.bi already supplies FB.MIN_VALUE_LONGINT and friends. The module includes it for its own use, but an import does not pass it on -- #include it yourself.
IsNaN( x ) IsInfinite( x ) IsFinite( x )
IsNormal( x ) IsSubnormal( x ) IsNegative( x )
Classify( x ) -> FP_NAN FP_INFINITE FP_ZERO FP_SUBNORMAL FP_NORMAL
SINGLE and DOUBLE. These read the exponent and mantissa fields rather than comparing. x <> x is a correct NaN test only while the compiler may not assume finiteness.
One of these reads wrong at first glance:
IsNegative( -0.0 ) ' TRUE
-0.0 < 0.0 ' false
-0.0 = 0.0 ' trueIsNegative is a sign-bit query. The two zeros are equal as numbers and distinguishable as bit patterns, and this is what tells them apart.
Min( a, b ) Max( a, b ) Clamp( v, lo, hi )
Lerp( a, b, t ) Sign( x ) as LONG
Every integer type plus SINGLE and DOUBLE (Lerp is floating point only).
-
NaN propagates:
Min( NaN( ), 1.0 )is NaN. C's fmin returns the non-NaN operand; that is a defensible choice and it is not this one, because silently discarding a NaN turns a detectable error into an undetectable wrong answer. - Clamp with lo > hi returns hi. Deterministic rather than right, because there is no right answer. A NaN v falls through and comes back unchanged.
-
Sign returns LONG always -- that is the difference from SGN, whose result is the argument's own type and so cannot index an array or drive a SELECT CASE.
Sign( NaN( ) )is 0. -
Lerp is
a + t*(b-a): monotonic, and not exact at t = 1 (may miss b by one ulp). The exact-at-endpoints form is not monotonic; both cannot be had at once, and C++20's std::lerp chooses the other one and pays a branch. - Min and Max are functions, so both arguments are evaluated.
Round( x ) half away from zero: 2.5 -> 3, -2.5 -> -3
RoundHalfEven( x ) banker's: 2.5 -> 2, -2.5 -> -2
Round( x, digits ) 0..17 decimal places
RoundHalfEven( x, digits )
Floor( x ) Ceil( x ) Truncate( x )
Floor and Ceil exist even though INT is floor: INT is floor and FIX is truncation, but the names do not say so and code often uses INT believing it truncates.
RoundHalfEven is what financial code wants -- half-up drifts upward across a ledger. It is implemented rather than delegated to C's rint, because rint honours the current rounding mode and would stop being deterministic the moment fenv.tiko's rounding control is used.
The digits form scales, and the scaling is visible:
Round( 2.675, 2 ) ' 2.68, not 2.67The double nearest 2.675 is 2.67499999999999982..., so an exact decimal round gives 2.67 -- but 2.675*100 rounds up to exactly 267.5, so the tie rule fires. Inherent to any scale-and-round implementation. For money, use Finance.
Outside 0..17 the result is NaN; returning the input would hide the mistake.
Cbrt Hypot Exp2 Expm1 Log2 Log10 Log1p LogB Pow
Sinh Cosh Tanh Asinh Acosh Atanh
Fma CopySign NextAfter ScaleB Frexp Modf Ulp
DegToRad RadToDeg
Pages: Cbrt · Hypot · Exp2 · Expm1 · Log2 · Log10 · Log1p · LogB · Pow · Sinh · Cosh · Tanh · Asinh · Acosh · Atanh · Fma · CopySign · NextAfter · ScaleB · Frexp · Modf · Ulp · DegToRad · RadToDeg
Real overload sets: a SINGLE argument takes the SINGLE path and calls the f variant, it does not widen. That is the specific defect that makes crt/math.bi unusable in practice -- sqrt there takes a DOUBLE, so every single-precision call widens and narrows silently, twice.
Hypot does not overflow for a representable result:
Hypot( 1e200, 1e200 ) ' finite
sqr( 1e200*1e200 + 1e200*1e200 ) ' +infLog1p and Expm1 are the accurate-near-zero pair:
log( 1.0 + 1e-17 ) ' 0.0 -- the 1.0 swallowed it
Log1p( 1e-17 ) ' 1e-17Pow is not a replacement for ^; it is the double-to-double operation under a name. Integer exponentiation is IPow.
Three conventions, all named:
FloorDiv / FloorMod sign follows the DIVISOR: FloorMod(-7,3) = 2
EuclidDiv / EuclidMod always non-negative: EuclidMod(-7,-3) = 2
DivMod( a, b ) returns a TUPLE
var (q, r) = DivMod( total, perPage )None of them is spelled Mod, so none can be confused with the operator. DivMod is where the tuple feature earns its keep: one idiv produces both.
Tuple identity is structural, so DivMod on LONG and on LONGINT return different types and neither converts to the other.
IPow( base, exp ) EXACT -- IPow( 3LL, 38LL ) = 1350851717672992089
IPowChecked( base, exp ) Optional
ISqrt( n ) exact floor of the true root
GCD( a, b ) LCM( a, b )
ISqrt is exact over the whole domain, which int( sqr( n ) ) is not: the DOUBLE conversion loses bits above 2^53 and the result can land one below the floor.
AddChecked SubChecked MulChecked DivChecked NegChecked AbsChecked
IPowChecked -> Optional( of T ), empty on overflow
AddSaturating SubSaturating MulSaturating NegSaturating AbsSaturating
AddWrapping SubWrapping MulWrapping NegWrapping
Every integer type. AbsChecked is not redundant:
abs( MIN_VALUE_LONGINT ) ' MIN_VALUE_LONGINT itself -- NEGATIVEThat is the classic overflow bug and it is silent today.
The limitation, stated plainly: these protect the call sites you thought about. Overflow bugs live at the call sites nobody thought about, which is why Rust traps by default in debug builds and Swift traps always. A global -checked mode is declined on cost; this does not pretend to be it.
The wrapping family is exactly what the operators already do. It exists so that deliberate wrapping -- a hash mix, a checksum, a ring index -- can be spelled as deliberate.
PopCount LeadingZeros TrailingZeros BitWidth Parity
RotateLeft RotateRight ByteSwap BitReverse IsPow2 NextPow2
UBYTE / USHORT / ULONG / ULONGINT, with signed overloads that reinterpret. Width comes from the operand's own type: LeadingZeros of a UBYTE counts within 8 bits.
LeadingZeros( 0 ) returns the operand's FULL WIDTH
RotateLeft( x, 64 ) on a ULONGINT is x, not undefined
Both are decisions, not descriptions: the x86 instructions are undefined at zero without BMI, and the C rotate idiom is undefined at n = 0.
The portable implementation is the contract. Per-backend intrinsics would be an optimisation underneath a defined result, never the definition.
No INTEGER / UINTEGER overloads. INTEGER is the same width as LONGINT on every tiko target, so they add nothing.
The money types live in their own category. std/money's Currency and Decimal are documented under Finance -- exact fixed-point money in eight bytes, and the 128-bit .NET decimal. They are part of the same library and ship in the same tree; they are filed separately because money is a subject of its own, not a corner of arithmetic.
Strict by default:
TryParseDouble( s [, flags] ) TryParseSingle( s [, flags] )
TryParseLongInt( s [, base] ) TryParseULongInt( s )
TryParseLong( s ) TryParseULong( s )
All return Optional. The whole string must be consumed:
| Input | Result | Input | Result |
|---|---|---|---|
"42" |
42 | " 42" |
empty |
"42abc" |
empty | "1,234" |
empty |
"1e" |
empty | "" |
empty |
"nan" / "inf"
|
the value | "1.#INF" |
the value |
tiko's own PRINT spellings parse, so a program can read back what it wrote. Leniency is opted into, never out of:
PARSE_TRIM PARSE_ALLOW_TRAILING PARSE_NO_SPECIAL
Integer overflow is detected, not clamped -- strtoll saturates and sets errno, which is the same "cannot fail" shape being replaced.
ToString( v ) shortest string that reads back EXACTLY
ToString( v, fmt ) controlled
ToString( int64 )
ToString( 0.1 + 0.2 ) ' 0.30000000000000004
str( 0.1 + 0.2 ) ' 0.3Seventeen characters where STR gives three, and it is the correct answer: the two values differ and a format that prints them the same is lying.
type NumFormat
style NUM_SHORTEST NUM_FIXED NUM_SCIENTIFIC NUM_GENERAL NUM_PERCENT
digits -1 = natural
grouping thousands separators
decimalSep default "."
groupSep default ","
prefix suffix plusSign
end type
ToString( 1234567.891, f ) ' 1,234,567.89Locale is explicit or absent -- an implicitly locale-dependent number format is how a program writes a CSV its own importer cannot read on another machine.
dim r as Rng = RngSeeded( 12345 )
print r.NextInt( 1, 6 ) ' inclusive both ends, UNBIASED
print r.NextDouble( ) ' [0, 1)RND and RANDOMIZE are untouched. fbprng.bi already offers five algorithms behind RANDOMIZE seed, algorithm. Three things are missing and the first is structural:
- RND is one global stream. Two subsystems that both want reproducible randomness cannot have it; a test cannot seed its own generator without disturbing the program's.
-
There is no unbiased integer range.
int( rnd * n )is the usual idiom and it is biased. Nobody notices, which is what makes it worth fixing. - There are no distributions and no shuffle.
The generator is xoshiro256**, four 64-bit words, so an Rng lives in a UDT without a pointer. An Rng is a value: copying it forks the stream, which is what makes a reproducible sub-simulation possible. There is no default global Rng.
NextULongInt NextULong NextDouble NextDoubleRange NextSingle
NextInt( lo, hi ) INCLUSIVE both ends, matching tiko's TO
NextBoolean NextBooleanP( p )
NextNormal NextNormalP( mean, sd ) NextExponential( lambda )
NextPoisson( lambda )
Shuffle( r, a( ) ) Choice( r, a( ) )
CryptoBytes( ptr, n ) CryptoULongInt( ) RngFromEntropy( )
NextInt is unbiased by Lemire's multiply-and-reject: one multiply, no division in the common case. NextDouble is [0,1) with 53 bits of entropy; 1.0 is never returned.
Shuffle is Fisher-Yates descending with an inclusive bound -- the form that produces every permutation with equal probability. The other forms are subtly wrong and are what a hand-rolled shuffle usually is.
CryptoBytes is a separate surface and not an Rng. No seed, no reproducibility, no state. One type with two contradictory contracts would invite exactly the mistake this separation prevents. RngFromEntropy( ) is the bridge.
Sum( a( ) ) compensated -- Neumaier
NaiveSum( a( ) ) the fast one, named so you meant it
Mean Variance VariancePopulation StdDev StdDevPopulation
Median Percentile MinOf MaxOf Range Covariance Correlation
Count
DOUBLE, SINGLE, LONG and LONGINT arrays, any lbound.
Why summation is the interesting part: naive summation accumulates rounding error at each step, and over a million values the result can be wrong in the fourth significant digit -- invisibly, because the answer stays plausible.
a( 0 ) = 1e16, then 100000 ones
Sum -> 10000000000100000 exact
NaiveSum -> 10000000000000000 off by 100000
Sum accumulates in DOUBLE regardless of element type: the sum of a million SINGLEs is not a single-precision quantity.
Variance is Welford, not E[x^2] - E[x]^2. The textbook form subtracts two large nearly-equal numbers and can return a negative variance for data with a large mean and small spread. The sample form (n-1) is the default, because almost every array is a sample; the population form is available under a name that says which it is.
Median and Percentile do not sort in place -- they copy. Asking a question does not modify your data. Percentile is the R type-7 / NumPy definition; nine definitions exist and disagree.
Mean of an empty sequence is NaN, not zero -- zero is a legitimate mean.
dim acc as RunningStats
acc.Add( x )
acc.Mean( ) acc.Variance( ) acc.StdDev( ) acc.MinValue( ) acc.MaxValue( )
acc.Merge( other ) acc.Reset( ) acc.Count( )Constant space, one pass, Welford internally. Merge combines two accumulators exactly (Chan's formula), so a fan-out across threads reduces without re-reading the data.
dim as Vec3 a = Vec3f_( 1, 2, 3 )
print Dot( a, b ), Length( a )
dim as Mat4 mvp = proj * view * model
dim as Vec3 p = TransformPoint( mvp, v )Vec2f Vec3f Vec4f Mat4f Quatf ' and the ...d doubles
Vec2 Vec3 Vec4 Mat4 Quat ' aliases for the SINGLE forms
Concrete types, because Vec( of T, N ) is not expressible: generics take type parameters only and there is no way to bind one to the value 3.
Two conventions, stated because half of all matrix bugs are these:
-
Column-major storage, column-vector convention.
m * vtransforms v andproj * view * modelapplies model first, matching OpenGL and GLSL. - TransformPoint and TransformVector are separate. A point translates; a direction does not.
Dot Cross Mul Length LengthSquared Distance DistanceSquared
Normalize Lerp Reflect Angle
Mat4fIdentity Translation Scaling RotationX/Y/Z Perspective Orthographic
Transpose Determinant TryInverse( m, out ) TransformPoint TransformVector
QuatfFromAxisAngle Conjugate Rotate ToMatrix Slerp
-
No
*between two vectors: it would have to mean the dot product or the component-wise product, both are wanted, and neither is obviously the default.Dot( a, b )andMul( a, b )say which. - Normalize of a zero vector returns zero, not NaN. That is the case that actually occurs -- a zero-length normal from a degenerate triangle -- and propagating NaN through a render loop turns one bad triangle into a blank frame.
-
LengthSquared is the one to use for comparisons:
Length(a) < Length(b)costs two square roots for a question that needs none. - Slerp takes the short path: q and -q are the same rotation, and without negating one operand it takes the long way round three-quarters of the time.
Complex numbers are not here any more -- they moved to their own file, complex.tiko, below. They were generated from the same macro text as Vec/Mat/Quat on the grounds that they are the same kind of small operator-overloaded value type; that was true of the arithmetic and false of everything else, and a program that wants Dot should not pay for twelve inverse hyperbolic functions.
dim as Complex z = CSet( 3, 4 )
print CAbs( z ) ' 5
print CToString( z * z ) ' -7+24i
dim as Complex w = CExp( z )Complex ' one type, 16 bytes, DOUBLE only
CSet CPolar CZero COne CImaginaryOne CNaN CInfinity
CReal CImag CSetReal CSetImag CSwap
CIsNaN CIsInfinite CIsFinite CIsReal CIsZero
CAbs CNorm CArg CLogAbs CConj CNegate CReciprocal CSgn
CExp CLog CLog10 CSqrt CPow CNthRoot
CSin CCos CTan CSec CCsc CCot CAsin CAcos CAtan CAsec CAcsc CAcot
CSinh CCosh CTanh CSech CCsch CCoth CAsinh CAcosh CAtanh CAsech CAcsch CAcoth
CToString CToStringPolar
= <> + - * / ^ each in all three arrangements: Complex with Complex,
float64 with Complex, Complex with float64
Every function carries a C prefix. Exp, Log, Sqr, Sin, Cos, Tan, Abs, Atn and Str are intrinsics, and when this library was headers in namespace FB, a function of one of those names shadowed the intrinsic for every later header -- measured, it broke what is now random.tiko -- after which the intrinsic could not be called back. The names stayed when the headers became std/numeric. Given that the transcendentals must be prefixed, the prefix is applied uniformly: a mixed scheme would need the reader to know which of Sin and Real is an intrinsic in order to guess the spelling. The type is Complex, unprefixed.
CStr is a keyword, so the string form is CToString( z ) -- which also names what it is: the round-tripping form, not STR.
DOUBLE only, and no macro. With one element type the bodies are written once, plainly, and are greppable by name.
Numerics from .NET, MIT. System.Numerics.Complex supplies Smith division, the scaled Abs, Asin_Internal, the stabilised Tan and the predicate set. It is not followed where it is beaten.
Two things the old vector header used to get wrong, and both are fixed:
-
/was the naive( ac + bd ) / ( c^2 + d^2 ). Its denominator overflows once |d| passes about 1.3e154, so( 1e200 + 1e200i ) / ( 1e200 + 1e200i )came back NaN instead of 1. Smith's formula divides through by the larger component first and has no range restriction. All three division overloads get it. -
CSqrt was the polar form,
FromPolar( sqr(|z|), Arg(z)/2 ). That is .NET Framework 4.7's, which .NET Core 3.0 replaced because it loses most of its significant digits near the negative real axis, where Arg is the difference of two nearly-equal quantities.
Each was put there by a measured failure. A differential run against Python's cmath -- that is, against C99 -- over 3,030 (function, operand) pairs found eight defects, seven of them inherited and one in the platform:
-
CSqrt( -4 - 0i )returned +2i. .NET testsimaginary == 0and hand back the positive root for both sides of their own cut. - CAsin and CAcos are NaN at exactly +-1: Asin_Internal divides by zero there, so .NET's own
Asin( 1 )is NaN rather than PI/2. - .NET tests
imaginary < 0where it has to test the sign bit. A negative zero is not less than zero, so .NET'sAsin( 2 - 0i )has a positive imaginary part where C99 gives a negative one. - CAtanh beyond +-1 cannot be reached through CAtan: forming
1 + izadds +0.0 to -0.0, IEEE gives +0.0, and the bit that decides the side of the cut is gone before the logarithm sees it. - CAtan of a small pure imaginary cancels:
atan( 1e-8 i )came out 9.999999994736e-9 for an answer that is 1e-8. - mingw-w64's
asinh( +0.0 )returns -0.0. asinh is odd and owes the sign back; that one is the platform's, not .NET's.
After the nine, 3,030 of 3,030 comparisons agree, signed zeros included, except 22 CReciprocal results that differ only in the sign of an exactly-zero component -- C99 Annex G does not specify those and the magnitudes are equal.
-
No ordering operators:
<on complex numbers has no meaning. CSgn returns z/|z|, the unit vector in z's direction, andCZero( )for zero -- not a LONG from comparing real parts and then imaginary ones, which would be such an ordering under another name. -
The special values are functions, not constants:
const as Complex Ncannot be initialised from 0.0/0.0, which is a compile-time division by zero. Same decision, same reason, asNaN( )in math.tiko. - CIsNaN is not "either part is NaN". One infinite part and one NaN is infinite, because the infinity already answers every question anyone asks of the number.
Two things deliberately not done, stated rather than left to be discovered:
-
No infinity recovery on
*. C99's__muldc3rescues( inf + 0i ) * ( 2 + 0i )from NaN with a branch on every multiply; .NET does not, and neither does this. Pinned by a test so that adding it later is a deliberate act. -
CPow( 0, w )is 0 for every w, andCPow( 0, 0 )is 1. Those are .NET's answers and the last two are wrong -- 0 to a negative power is infinite. Kept for compatibility with .NET, and pinned.
The vendored test suites have no suite for this header, so there is no tiko result to report for it.
scope
dim r as ScopedRounding = ScopedRounding( ROUND_TOWARD_NEGATIVE )
' ... directed-rounding computation ...
end scope ' restored on EVERY exit pathGetRoundingMode( ) SetRoundingMode( m )
ROUND_TO_NEAREST ROUND_TOWARD_NEGATIVE ROUND_TOWARD_POSITIVE ROUND_TOWARD_ZERO
ClearFpExceptions( ) ClearFpException( e )
TestFpException( e ) RaiseFpException( e )
FP_INVALID FP_DIVBYZERO FP_OVERFLOW FP_UNDERFLOW FP_INEXACT FP_ALL_EXCEPT
ScopedRounding is a constructor, not a factory: a factory returning by value builds a temporary, copies it, and then destroys the temporary -- restoring the mode before the guarded region runs.
The hazard, stated rather than hidden. The optimiser may move floating-point operations across a call, and moving one across a rounding-mode change silently produces the wrong answer. C solves this with #pragma STDC FENV_ACCESS.
The rounding mode is per-thread on every supported platform.
The constants above are the library's own, and are deliberately not the C library's. The C FE_* values are not a property of the platform: MSVC, and mingw-w64 since v8, define them in terms of the CRT's abstract _RC_* / _SW_* codes, while glibc, the BSDs, djgpp and mingw-w64 v7 use the x87 control- and status-word encoding directly. Which family applies is decided by the C runtime the program finally links against -- mingw-w64 changed sides between v7 and v8 without the target changing -- and the compiler cannot know that at compile time, so no #ifdef on target can be right. fenv.tiko therefore detects the family at startup, by offering the CRT the x87 spelling of FE_DOWNWARD and seeing whether it is accepted, and translates. Getting this wrong is silent, not loud: the two families overlap numerically without agreeing, so x87 FP_DIVBYZERO (&h04) reads as FP_OVERFLOW to a _SW_* CRT and fetestexcept cheerfully answers about the wrong flag.
What is reproducible: two classes with two different guarantees, and the difference matters if you ship the same program for more than one target.
| Class | Guarantee | Covers |
|---|---|---|
| Exact | Identical results everywhere. | All integer arithmetic, all of money.Currency, all of money.Decimal, rounding, classification, Min/Max/Clamp/Sign, the bit family, RNG streams, and vector/matrix arithmetic. |
| Bounded | Accurate to a stated ulp bound; the platform libm is the dependency. | Every transcendental, Sqr, Hypot, Slerp, everything in Complex beyond the four operators, StdDev's final root, and decimal->binary parsing. |
The second class cannot be the first without shipping correctly-rounded implementations of every transcendental -- a multi-year project that no mainstream language ships by default. glibc, MSVCRT and mingw-w64 do not agree to the last bit and are not required to.
So a financial total is reproducible across your builds and a Sin is not, and this library says which is which instead of implying both.
All measured.
-
Four decimal places on Currency, fixed. The central limitation of the eight-byte money type, and the reason
Decimalexists beside it -- see Finance. - Accuracy of the libm surface is the platform's; results are not identical across targets. Rounding, classification and all integer/Currency work are.
-
Round( x, digits )decides the tie on the scaled value, soRound( 2.675, 2 )is 2.68 where an exact decimal round gives 2.67. - Shortest-round-trip formatting is a loop (format, parse back, retry at higher precision), not Ryu. Same result, up to 17 snprintf/strtod pairs per call.
- Decimal->binary conversion on parse is strtod's. The validation is this library's and is strict; the last bit is the platform's.
- Statistics take plain arrays, not the iterator protocol.
- Checked arithmetic is opt-in per call site. It does not catch what you did not audit.
- LONG DOUBLE is not covered; crt/longdouble.bi declares the
lvariants as DOUBLE on this target. - Nothing folds at compile time:
Round( 2.5 )on a literal is a run-time call, because constant folding covers operators, not user functions.
The fbcunit suites for these modules are vendored in tiko-lang under tests/vendor/fbc-tests/math/:
| Suite | What it covers |
|---|---|
| core.bas | constants, classification (exhaustive over all 2^32 SINGLE bit patterns, plus the population of each class as a checkable property of IEEE 754), selection, rounding at every tie, the two bugs in int(x+0.5) pinned, the libm identities |
| integer.bas | all three modulo conventions over every sign combination, DivMod, IPow against an independent reference with 3^38 and 7^22 pinned, ISqrt by its defining property, checked arithmetic at both boundaries of every integer type |
| bits.bas | exhaustive over the whole USHORT and UBYTE domains against naive bit-at-a-time references, the zero and full-width cases, rotation round trips |
| currency.bas | round-trip over the corpus, strict parse rejects, a+b-b = a bitwise, division ties, Invalid propagation through every operator, Apportion summing exactly to the total, the accumulate-vs-round-each-line case |
| text.bas | the four defects demonstrated against VAL and STR, strict parse table, round-trip over a large stratified SINGLE sample and a curated DOUBLE set, shortest-form checks, integer overflow detection, NumFormat |
| random.bas | identical streams from identical seeds, chi-squared uniformity, unbiased range, NextDouble never 1.0, distributions against analytic mean and variance, and all 24 permutations of 4 elements observed with equal frequency |
| stats.bas | compensation demonstrated by asserting NaiveSum fails the same test, Welford beating the textbook formula on data chosen to make it return a negative variance, non-mutation, Merge |
| vector.bas | identities only -- |normalize(v)|=1, cross orthogonal, M*M-inverse=I, Det(AB)=Det(A)Det(B), q*conj(q)=1, slerp endpoints, exp(log(z))=z |
| fenv.bas | each mode observably changes a result, ScopedRounding restores on fallthrough / EXIT / RETURN / nested scopes, each exception flag raised by what must raise it |
It decides five assertions across two suites: code evaluated on the x87 with the precision control at 64-bit mantissa keeps an intermediate that is never stored in 80 bits. C99 permits this (FLT_EVAL_METHOD = 2).
This bites the tests that demonstrate a defect rather than a result. Each is the counter-example half of a pair -- the naive form getting it wrong, beside the library getting it right -- and each was written assuming the intermediate had been rounded to double. In 80 bits the naive form quietly gets the right answer and stops demonstrating anything:
| Expression | Unstored | Stored |
|---|---|---|
int( justunder + 0.5 ) |
0 | 1 |
sqr( big*big + big*big ) |
1.41e200 | inf |
log( 1.0 + 1e-17 ) |
1e-17 | 0 |
(3LL ^ 38LL) as longint |
...089 | ...000 |
So rounding_beats_the_idiom_ and libm_surface_ in core.bas, and ipow_exact_ in integer.bas, store the intermediate first. The store is load-bearing and is asserted in its own right, so a future compiler that keeps 80 bits fails at the cause rather than confusingly downstream.
Stated rather than implied:
- No differential test against OleAut32
VarCy*or .NET decimal was run for Currency; it is owed.
Every entry, grouped by task. Numbers and math lists them beside the rest of the area.
| Function | What it does | From |
|---|---|---|
| Round | Rounds to the nearest whole number or number of decimals, halves away from zero. | std/numeric |
| RoundHalfEven | Rounds to the nearest whole number or number of decimals, halves to the even side. | std/numeric |
| Floor | Rounds down, towards negative infinity, so Floor(-2.1) is -3. | std/numeric |
| Ceil | Rounds up, towards positive infinity, so Ceil(-2.7) is -2. | std/numeric |
| Truncate | Drops the fraction, rounding towards zero, so Truncate(-2.7) is -2. | std/numeric |
| Modf | Splits a number into whole and fractional parts that both keep its sign. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Sign | Returns -1, 0 or 1 for the sign of a number, always as a Long. | std/numeric |
| Min | Returns the smaller of two values; a NaN argument gives NaN. | std/numeric |
| Max | Returns the larger of two values; a NaN argument gives NaN. | std/numeric |
| Clamp | Limits a value to a range: below lo gives lo, above hi gives hi. | std/numeric |
| Lerp | Returns the point a fraction t of the way from a to b; t is not clamped. | std/numeric |
| Function | What it does | From |
|---|---|---|
| IPow | Raises a whole number to a whole-number power exactly, with no trip through Double. | std/numeric |
| ISqrt | Returns the exact whole-number square root, rounded down. | std/numeric |
| Hypot | Returns the square root of xx + yy without overflowing for large values. | std/numeric |
| Cbrt | Returns the cube root of a number, negative numbers included. | std/numeric |
| Exp2 | Returns 2 raised to a power. | std/numeric |
| Expm1 | Returns e raised to a power, minus one, accurately for powers near zero. | std/numeric |
| Log2 | Returns the base-2 logarithm of a number. | std/numeric |
| Log10 | Returns the base-10 logarithm of a number. | std/numeric |
| Log1p | Returns the natural logarithm of one plus a number, accurately near zero. | std/numeric |
| Pow | Returns one floating-point number raised to the power of another. | std/numeric |
| Function | What it does | From |
|---|---|---|
| DegToRad | Converts an angle from degrees to radians. | std/numeric |
| RadToDeg | Converts an angle from radians to degrees. | std/numeric |
| Sinh | Returns the hyperbolic sine of a number. | std/numeric |
| Cosh | Returns the hyperbolic cosine of a number. | std/numeric |
| Tanh | Returns the hyperbolic tangent of a number, which lies between -1 and 1. | std/numeric |
| Asinh | Returns the inverse hyperbolic sine of a number. | std/numeric |
| Acosh | Returns the inverse hyperbolic cosine of a number of 1 or more. | std/numeric |
| Atanh | Returns the inverse hyperbolic tangent of a number between -1 and 1. | std/numeric |
| Function | What it does | From |
|---|---|---|
| FloorDiv | Divides whole numbers rounding towards negative infinity, so FloorDiv(-7, 2) is -4. | std/numeric |
| FloorMod | Returns the remainder with the divisor's sign, so FloorMod(-1, 5) is 4. | std/numeric |
| EuclidDiv | Divides whole numbers so that the matching EuclidMod remainder is never negative. | std/numeric |
| EuclidMod | Returns a remainder that is never negative, whatever the signs. | std/numeric |
| DivMod | Returns the quotient and the remainder of one division together, as a tuple. | std/numeric |
| GCD | Returns the greatest common divisor of two whole numbers. | std/numeric |
| LCM | Returns the least common multiple of two whole numbers, or 0 if either is 0. | std/numeric |
| Function | What it does | From |
|---|---|---|
| AddChecked | Adds two integers, returning an empty Optional if the result would overflow. | std/numeric |
| SubChecked | Subtracts two integers, returning an empty Optional if the result would overflow. | std/numeric |
| MulChecked | Multiplies two integers, returning an empty Optional if the result would overflow. | std/numeric |
| DivChecked | Divides two integers, returning an empty Optional on division by zero or overflow. | std/numeric |
| NegChecked | Negates an integer, returning an empty Optional for the most negative value. | std/numeric |
| AbsChecked | Returns the absolute value as an Optional, empty for the most negative value. | std/numeric |
| IPowChecked | Raises an integer to a power, returning an empty Optional if the result overflows. | std/numeric |
| AddSaturating | Adds two integers, clamping the result to the type's minimum or maximum. | std/numeric |
| SubSaturating | Subtracts two integers, clamping the result to the type's minimum or maximum. | std/numeric |
| MulSaturating | Multiplies two integers, clamping the result to the type's minimum or maximum. | std/numeric |
| NegSaturating | Negates an integer; the most negative value becomes the maximum instead. | std/numeric |
| AbsSaturating | Returns the absolute value; the most negative value becomes the maximum instead. | std/numeric |
| AddWrapping | Adds two integers and wraps around on overflow, on purpose. | std/numeric |
| SubWrapping | Subtracts two integers and wraps around on overflow, on purpose. | std/numeric |
| MulWrapping | Multiplies two integers and wraps around on overflow, on purpose. | std/numeric |
| NegWrapping | Negates an integer within its type, so the most negative value stays as it is. | std/numeric |
| Function | What it does | From |
|---|---|---|
| TryParseDouble | Parses a whole string as a Double, returning an empty Optional if it is not one. | std/numeric |
| TryParseSingle | Parses a whole string as a Single, returning an empty Optional if it is not one. | std/numeric |
| TryParseLong | Parses a whole string as a Long, returning an empty Optional if invalid or too big. | std/numeric |
| TryParseULong | Parses a whole string as a ULong, returning an empty Optional if invalid or too big. | std/numeric |
| TryParseLongInt | Parses a whole string as a LongInt, in an optional base; empty if invalid or too big. | std/numeric |
| TryParseULongInt | Parses a whole string as a ULongInt, returning an empty Optional if invalid or too big. | std/numeric |
| ToString | Returns a number as the shortest text that reads back as exactly the same value. | std/numeric |
| CurrentLocaleFormat | Returns a NumFormat holding this machine's decimal and thousands separators. | std/numeric |
| Function | What it does | From |
|---|---|---|
| RngSeeded | Creates a random generator from a seed, so the same seed repeats the same sequence. | std/numeric |
| RngFromEntropy | Creates a random generator seeded by the operating system, different on every run. | std/numeric |
| Rng.NextInt | Returns a random whole number from lo to hi, both included, without bias. | std/numeric |
| Rng.NextDouble | Returns a random Double from 0 up to but not including 1, with 53 random bits. | std/numeric |
| Rng.NextDoubleRange | Returns a random Double from lo up to but not including hi. | std/numeric |
| Rng.NextSingle | Returns a random Single from 0 up to but not including 1. | std/numeric |
| Rng.NextULong | Returns a random 32-bit unsigned value. | std/numeric |
| Rng.NextULongInt | Returns a random 64-bit unsigned value, the generator's raw output. | std/numeric |
| Rng.NextBoolean | Returns true or false with equal chance. | std/numeric |
| Rng.NextBooleanP | Returns true with probability p, from 0 for never to 1 for always. | std/numeric |
| Rng.NextNormal | Returns a random value from the normal distribution, mean 0 and standard deviation 1. | std/numeric |
| Rng.NextNormalP | Returns a random value from a normal distribution with the given mean and deviation. | std/numeric |
| Rng.NextExponential | Returns a random waiting time between events at rate lambda; its mean is 1/lambda. | std/numeric |
| Rng.NextPoisson | Returns a random count of events in one interval at rate lambda; its mean is lambda. | std/numeric |
| Shuffle | Shuffles an array in place so that every order is equally likely. | std/numeric |
| Choice | Returns one element of an array picked at random, by reference. | std/numeric |
| Function | What it does | From |
|---|---|---|
| CryptoBytes | Fills a buffer with secure random bytes from the operating system; false on failure. | std/numeric |
| CryptoULongInt | Returns a secure random 64-bit value from the operating system. | std/numeric |
| Function | What it does | From |
|---|---|---|
| IsNaN | Returns true if the value is NaN, which an equality test cannot detect. | std/numeric |
| IsInfinite | Returns true if the value is plus or minus infinity. | std/numeric |
| IsFinite | Returns true if the value is neither infinite nor NaN. | std/numeric |
| IsNormal | Returns true if the value is finite, not zero and not subnormal. | std/numeric |
| IsSubnormal | Returns true if the value is nonzero but below the smallest normal number. | std/numeric |
| IsNegative | Returns true if the sign bit is set, which includes negative zero. | std/numeric |
| Classify | Returns whether a value is NaN, infinite, zero, subnormal or normal. | std/numeric |
| NaN | Returns a Double that is NaN, not a number. | std/numeric |
| NaNF | Returns a Single that is NaN, not a number. | std/numeric |
| Infinity | Returns positive infinity as a Double. | std/numeric |
| InfinityF | Returns positive infinity as a Single. | std/numeric |
| NegInfinity | Returns negative infinity as a Double. | std/numeric |
| NegInfinityF | Returns negative infinity as a Single. | std/numeric |
| MinSubnormalDouble | Returns the smallest Double above zero. | std/numeric |
| MinSubnormalSingle | Returns the smallest Single above zero. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Frexp | Splits a number into a fraction from 0.5 to 1 and a power of two. | std/numeric |
| ScaleB | Multiplies a number by a power of two exactly, by adjusting its exponent. | std/numeric |
| Ulp | Returns the gap between a number and the next one its type can hold. | std/numeric |
| Fma | Returns x * y + z with a single rounding at the end. | std/numeric |
| LogB | Returns the binary exponent of a number as a floating-point value. | std/numeric |
| CopySign | Returns a number's size with the sign of a second number, zero's sign included. | std/numeric |
| NextAfter | Returns the next representable number after x in the direction of y. | std/numeric |
| Function | What it does | From |
|---|---|---|
| GetRoundingMode | Returns the direction floating-point results are currently rounded in. | std/numeric |
| SetRoundingMode | Sets the direction floating-point results are rounded in; returns true on success. | std/numeric |
| ClearFpExceptions | Clears every floating-point exception flag. | std/numeric |
| ClearFpException | Clears one floating-point exception flag and leaves the others alone. | std/numeric |
| TestFpException | Returns true if a floating-point exception flag has been raised since it was cleared. | std/numeric |
| RaiseFpException | Raises a floating-point exception flag by hand; returns true on success. | std/numeric |
| Function | What it does | From |
|---|---|---|
| PopCount | Counts the bits that are set. | std/numeric |
| LeadingZeros | Counts the zero bits above the highest set bit; zero gives the type's full width. | std/numeric |
| TrailingZeros | Counts the zero bits below the lowest set bit; zero gives the type's full width. | std/numeric |
| BitWidth | Returns how many bits the value needs, or 0 for zero. | std/numeric |
| Parity | Returns 1 if an odd number of bits are set, or 0 if an even number are. | std/numeric |
| BitReverse | Reverses the order of all the bits in the value's type. | std/numeric |
| ByteSwap | Reverses the order of the bytes, converting between big-endian and little-endian. | std/numeric |
| IsPow2 | Returns true if the value is a power of two; zero is not one. | std/numeric |
| NextPow2 | Returns the smallest power of two at or above the value; zero gives 1. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Sum | Adds up an array with compensated summation, so small values are not lost. | std/numeric |
| NaiveSum | Adds up an array with a plain loop: faster than Sum, but small values can be lost. | std/numeric |
| Count | Counts the elements in an array. | std/numeric |
| Mean | Returns the average of an array, or NaN if it is empty. | std/numeric |
| Median | Returns the middle value of an array, without reordering it. | std/numeric |
| Percentile | Returns the value below which p percent of an array falls, with p from 0 to 100. | std/numeric |
| Variance | Returns the sample variance of an array, dividing by n-1. | std/numeric |
| VariancePopulation | Returns the population variance of an array, dividing by n. | std/numeric |
| StdDev | Returns the sample standard deviation of an array, dividing by n-1. | std/numeric |
| StdDevPopulation | Returns the population standard deviation of an array, dividing by n. | std/numeric |
| MinOf | Returns the smallest value in an array, or NaN if it is empty. | std/numeric |
| MaxOf | Returns the largest value in an array, or NaN if it is empty. | std/numeric |
| Range | Returns the largest value in an array minus the smallest. | std/numeric |
| Covariance | Returns the sample covariance of two arrays of the same length. | std/numeric |
| Correlation | Returns the Pearson correlation of two arrays, from -1 to 1. | std/numeric |
| Function | What it does | From |
|---|---|---|
| RunningStats.Add | Adds one value to a running count, mean, variance, minimum and maximum. | std/numeric |
| RunningStats.Merge | Combines another accumulator's values into this one, exactly. | std/numeric |
| RunningStats.Reset | Empties the accumulator so that it can be reused. | std/numeric |
| RunningStats.MinValue | Returns the smallest value added so far. | std/numeric |
| RunningStats.MaxValue | Returns the largest value added so far. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Dot | Returns the dot product of two vectors; zero means they are perpendicular. | std/numeric |
| Cross | Returns a Vec3 perpendicular to both inputs; swapping them flips its direction. | std/numeric |
| Mul | Multiplies two vectors component by component. | std/numeric |
| Length | Returns the length of a vector. | std/numeric |
| LengthSquared | Returns the squared length of a vector, the cheap way to compare lengths. | std/numeric |
| Distance | Returns the distance between two points. | std/numeric |
| DistanceSquared | Returns the squared distance between two points, the cheap way to test a range. | std/numeric |
| Normalize | Returns a vector of length 1 in the same direction; a zero vector stays zero. | std/numeric |
| Reflect | Reflects a vector off a surface, given the surface's normalised normal. | std/numeric |
| Angle | Returns the angle between two vectors in radians, from 0 to pi. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Determinant | Returns the determinant of a matrix; zero means it has no inverse. | std/numeric |
| Transpose | Returns a matrix with its rows and columns swapped. | std/numeric |
| TryInverse | Writes the inverse of a matrix into a second one, or returns false if there is none. | std/numeric |
| TransformPoint | Applies a matrix to a point, translation included. | std/numeric |
| TransformVector | Applies a matrix to a direction, leaving out the translation. | std/numeric |
| Conjugate | Returns the conjugate of a quaternion, the reverse rotation when it is normalised. | std/numeric |
| Rotate | Rotates a vector by a normalised quaternion. | std/numeric |
| ToMatrix | Converts a quaternion into the matching rotation matrix. | std/numeric |
| Slerp | Blends between two rotations at constant speed, taking the shorter way round. | std/numeric |
| Function | What it does | From |
|---|---|---|
| Complex | Holds a complex number as two Doubles, re and im. | std/numeric |
| Complex operators | Adds, subtracts, multiplies, divides, raises and compares complex numbers. | std/numeric |
| CSet | Builds a complex number from its real and imaginary parts. | std/numeric |
| CPolar | Builds a complex number from a modulus and an angle in radians. | std/numeric |
| CZero | Returns the complex number 0. | std/numeric |
| COne | Returns the complex number 1. | std/numeric |
| CImaginaryOne | Returns the imaginary unit i. | std/numeric |
| CNaN | Returns a complex number whose two parts are both NaN. | std/numeric |
| CInfinity | Returns a complex number whose two parts are both positive infinity. | std/numeric |
| CReal | Returns the real part of a complex number. | std/numeric |
| CImag | Returns the imaginary part of a complex number, without the i. | std/numeric |
| CSetReal | Sets the real part of a complex variable in place. | std/numeric |
| CSetImag | Sets the imaginary part of a complex variable in place. | std/numeric |
| CSwap | Swaps the values of two complex variables. | std/numeric |
| CToString | Returns a complex number as text such as 3+4i that reads back exactly. | std/numeric |
| CToStringPolar | Returns a complex number as polar text, such as 5 * exp(0.927i). | std/numeric |
| Function | What it does | From |
|---|---|---|
| CIsNaN | Returns true if a complex number is NaN; one infinite part makes it infinite instead. | std/numeric |
| CIsInfinite | Returns true if either part of a complex number is infinite. | std/numeric |
| CIsFinite | Returns true if both parts of a complex number are finite. | std/numeric |
| CIsReal | Returns true if the imaginary part of a complex number is zero. | std/numeric |
| CIsZero | Returns true if both parts of a complex number are zero. | std/numeric |
| CAbs | Returns the modulus, the distance from zero, without overflowing for large parts. | std/numeric |
| CNorm | Returns the modulus squared, the cheap way to compare sizes. | std/numeric |
| CArg | Returns the phase angle of a complex number in radians, from -pi to pi. | std/numeric |
| CLogAbs | Returns the logarithm of the modulus, without overflowing for large parts. | std/numeric |
| CConj | Returns the conjugate, the same number with its imaginary part negated. | std/numeric |
| CNegate | Returns a complex number with both parts negated. | std/numeric |
| CReciprocal | Returns one divided by a complex number; zero gives zero. | std/numeric |
| CSgn | Returns a complex number scaled to modulus 1, or zero for zero. | std/numeric |
| Function | What it does | From |
|---|---|---|
| CExp | Returns e raised to a complex power. | std/numeric |
| CLog | Returns the natural logarithm of a complex number, or its logarithm to a given base. | std/numeric |
| CLog10 | Returns the base-ten logarithm of a complex number. | std/numeric |
| CSqrt | Returns the principal square root of a complex number or of a negative real. | std/numeric |
| CPow | Raises a complex or real number to a complex or real power. | std/numeric |
| CNthRoot | Returns any one of the n nth roots of a complex number. | std/numeric |
| Function | What it does | From |
|---|---|---|
| CSin | Returns the sine of a complex number. | std/numeric |
| CCos | Returns the cosine of a complex number. | std/numeric |
| CTan | Returns the tangent of a complex number, finite even for a large imaginary part. | std/numeric |
| CSec | Returns the secant of a complex number, one over the cosine. | std/numeric |
| CCsc | Returns the cosecant of a complex number, one over the sine; zero gives zero. | std/numeric |
| CCot | Returns the cotangent of a complex number, one over the tangent. | std/numeric |
| CAsin | Returns the inverse sine of a complex number, or of any real number. | std/numeric |
| CAcos | Returns the inverse cosine of a complex number, or of any real number. | std/numeric |
| CAtan | Returns the inverse tangent of a complex number. | std/numeric |
| CAsec | Returns the inverse secant of a complex number, or of any real number. | std/numeric |
| CAcsc | Returns the inverse cosecant of a complex number, or of any real number. | std/numeric |
| CAcot | Returns the inverse cotangent of a complex number; zero gives zero. | std/numeric |
| CSinh | Returns the hyperbolic sine of a complex number. | std/numeric |
| CCosh | Returns the hyperbolic cosine of a complex number. | std/numeric |
| CTanh | Returns the hyperbolic tangent of a complex number. | std/numeric |
| CSech | Returns the hyperbolic secant of a complex number, one over its cosh. | std/numeric |
| CCsch | Returns the hyperbolic cosecant of a complex number, one over its sinh; zero gives zero. | std/numeric |
| CCoth | Returns the hyperbolic cotangent of a complex number, one over its tanh. | std/numeric |
| CAsinh | Returns the inverse hyperbolic sine of a complex number. | std/numeric |
| CAcosh | Returns the inverse hyperbolic cosine of a complex number, or of any real number. | std/numeric |
| CAtanh | Returns the inverse hyperbolic tangent of a complex number, or of any real number. | std/numeric |
| CAsech | Returns the inverse hyperbolic secant of a complex number. | std/numeric |
| CAcsch | Returns the inverse hyperbolic cosecant of a complex number. | std/numeric |
| CAcoth | Returns the inverse hyperbolic cotangent of a complex number. | std/numeric |
tiko-lang · Home · Feature Index · Keyword Index · License
FreeBASIC manual pages reproduced under the GNU FDL 1.3.
- Generics
- Interfaces
- For Each
- Iterators
- Tuples
- Array Results
- Defer
- Lambdas
- Block Strings
- Partial Types
- String Slices
- Embed
- Typed Variadics
- C Header Import
- Foreign Library Linking
- UString
- Fixed-Width Types
- Nullptr
- String Library
- Format
- Array
- Map
- Set
- LinkedList
- OrderedMap
- OrderedSet
- Sorting
- Optional
- Result
- Smart Pointers
- Date and Time (Chrono)
- Filesystem
- File Streams
- Config (INI and TOML)
- Codec
- Hash
- UUID
- OS
- Process
- Net
- HTTP and TLS
- Math Library
- Finance
- Logging
- Terminal TUI Library
- PsControls
- tkControls
- sys/win
- Goroutines and Channels
- LLVM Backend
- tiko build
- TIKO_COMPILER
- Variable Declarations
- Standard Data Types
- User Defined Types
- Procedures
- Operators
- Control Flow Statements
- Converting Data Types
- Array Functions
- String Functions
- Mathematical Functions
- Bit Manipulation
- Date and Time Functions
- File I/O Functions
- Console Functions
- User Input (Runtime Library)
- User Input Functions (Graphics Library)
- Screen Functions
- 2D Drawing Functions
- Graphics Library Reference
- Graphics Keyword List
- Memory Functions
- Threading Support Functions
- Error Handling Functions
- Operating System Functions
- Preprocessor
- Intrinsic Defines
- Miscellaneous Keywords
- Modularizing