racket/collects/math/scribblings/math-utils.scrbl
Neil Toronto aed3b39546 Added flexp2', fllog2', `fllogb'; refactored and documented flonum testing
Note: With this refactoring, `math/utils' no longer depends on `rackunit'.

* (flexp2 x) computes (flexpt 2.0 x) but in about 1/3 the time for integer
  `x' using a lookup table. Written for exact argument reduction in `fllog2'
  after discovering that (flexpt 2.0 x) was the main performance bottleneck.

* (fllog2 x) computes (/ (fllog x) (fllog 2.0)) with near perfect accuracy.
  Invented an algorithm to compute it with at least 8 extra bits before
  final rounding; quite pleased with the result. Needed `fllog2' to ensure
  (fllogb 2.0 x) would be exact when `x' is a power of two.

* (fllogb b x) computes (/ (fllog x) (fllog b)) with better accuracy, and
  also handles limit values in a way that's consistent with the mathematical
  limits. When those are ambiguous, it's consistent with `flexpt', which
  follows IEEE 754 and C99. Otherwise returns +nan.0. See docs for details.

* `bflogb' is currently just for testing `fllogb'.

* Refactored FPU testing and documented it. So far, the only documented way
  to do it is by calling `test-floating-point', which runs a comprehensive
  deterministic+randomized suite of tests and returns a list representing
  failed tests. I'll document individual tests after I document flonum
  expansions and result/error functions like `fl+/error'.

* Added `fllog2' and `fllogb' to the flonum tests.
2013-01-28 17:44:33 -07:00

100 lines
4.3 KiB
Racket

#lang scribble/manual
@(require scribble/eval
racket/sandbox
(for-label racket/base racket/future
math
(only-in typed/racket/base
Real Boolean Integer Natural Number Listof
Positive-Flonum Float-Complex Any List Positive-Integer))
"utils.rkt")
@(define untyped-eval (make-untyped-math-eval))
@title[#:tag "utils"]{Stuff That Doesn't Belong Anywhere Else}
@(author-neil)
@defmodule[math/utils]
@section[#:tag "utils:parallel"]{Parallelization}
@defparam[max-math-threads num Positive-Integer]{
The maximum number of threads a parallelized @racketmodname[math] function
will use. The default value is @racket[(max 1 (processor-count))].
}
@section[#:tag "utils:dft"]{Discrete Fourier Transform Conventions}
@defparam[dft-convention lst (List Real Real)]{
A parameter controlling the convention used for scaling discrete Fourier transforms, such as those
performed by @racket[array-fft]. The default value is @racket['(1 -1)], which represents the convention
used in signal processing.
In general, if @racket[lst] is @racket[(list a b)] and @racket[n] is the length of a transformed
array axis or vector, then
@itemlist[@item{Each sum is scaled by @racket[(expt n (/ (- a 1) 2))].}
@item{Each exponential in the sum has its argument scaled by @racket[b].}]
Conveniently, a Fourier transform with convention @racket[(list (- a) (- b))] is the inverse
of a Fourier transform with convention @racket[(list a b)].
See Mathematica's
@hyperlink["http://reference.wolfram.com/mathematica/tutorial/FourierTransforms.html"]{documentation
on @tt{Fourier}}, from which this excellent idea was stolen.
}
@defproc[(dft-inverse-convention) (List Real Real)]{
Returns the convention used for inverse Fourier transforms, given the current convention.
}
@section[#:tag "utils:fpu-test"]{Floating-Point Compliance Testing}
@defproc[(test-floating-point [n Natural]) (Listof (List Any Any))]{
Runs a comprehensive test of the system's IEEE 754 (floating-point) compliance, and reports
unexpected inaccuracies and errors.
In each test, a function is applied to some carefully chosen values, as well as @racket[n] additional
random values.
Its corresponding @tech{bigfloat} function is applied to the same values, and the answers are
compared.
Each test returns a list of failures, which are appended and returned.
Each failure in a failure list is formatted
@racketblock[(list (list name args ...) reason)]
where @racket[name] is the name of a function, such as @racket['fl+], @racket[args ...] are the
arguments it was applied to, and @racket[reason] is the reason for the failure.
If @racket[reason] is a flonum, the failure was due to inaccuracy. For example,
@racketblock[(list (list 'fl+ 4.5 2.3) 0.76)]
means the result of @racket[(fl+ 4.5 2.3)] was off by @racket[0.76] @tech{ulps}.
The threshold for reporting unexpected inaccuracy depends on the function tested.
All the arithmetic and irrational functions exported by @racketmodname[racket/flonum], for example,
must have no more than @racket[0.5] ulps error in order to be compliant.
Two other possible failure reasons are
@racketblock[(list 'different-zero 0.0 -0.0)
(list 'different-zero -0.0 0.0)]
The first zero is the answer returned by the function, and the second zero is the expected answer.
Other possible failure reasons have the form
@racketblock[(list 'not-fl2? x y)]
meaning that the result @racket[(values x y)] is not a valid flonum expansion.
Such reasons are only given for failures of functions whose names begin with @tt{fl2} or contain
@tt{/error}.
These functions are currently undocumented, but are used to implement many
@racketmodname[math/flonum], @racketmodname[math/special-functions], and
@racketmodname[math/distributions] functions.
Tests of functions that operate on and return flonum expansions are the strictest tests, requiring
hardware arithmetic to be perfectly IEEE 754 compliant.
They reliably fail on seemingly innocuous noncompliant behavior, such as computing intermediate
results with 80-bit precision.
}
@defparam[print-fp-test-progress? print? Boolean]{
When @racket[(print-fp-test-progress?)] is @racket[#t], floating-point tests print and flush a
representation of their progress as they run. The default value is @racket[#t].
}
@(close-eval untyped-eval)