Other Herbie APIs

Herbie platforms are the main way to customize Herbie's behavior. The typical platform just defines the set of representations and operations that available to Herbie when compiling. However, platform files can technically contain arbitrary Racket code, and thus can call other Herbie APIs, including importing external libraries, defining new representations, and so on. This page documents such APIs. Note that a level of comfort with Racket is assumed.

Please note that all of the APIs on this page are considered unstable and may change version to version. If you run into issues, please file a bug.

Defining representations

Representations what Herbie calls different number formats. They play a central role in platforms. Concretely, a representation is a set of Racket values that represent both real numbers and bit patterns.

Specifically, a representation value needs to be convertible to Racket bigfloat values (which are basically MPFR floats) and also to ordinals, meaning integers between -2w−1 and 2w−1−1 for some bit width w.

Create representations with make-representation:

(make-representation
#:name name
#:total-bits width
#:bf->repr bf->repr
#:repr->bf repr->bf
#:ordinal->repr ordinal->repr
#:repr->ordinal repr->ordinal
#:special-value? special?)

The #:name should be either a symbol, or a list containing symbols and integers. The #:total-bits value should be a positive integer. The #:total-bits parameter determines the total ordinal range your format takes up, not just its significand range, so for example for double-precision floats you need a #:total-bits of 64.

The #:bf->repr and #:repr->bf values should be that convert between representation values and Racket bigfloats. The repr->bf function should use as large a bigfloat precision as needed to exactly represent the value, while the bf->repr function should round as per the current value of the bf-rounding-mode parameter.

All non-NaN bigfloat values should result in non-NaN representation values. For example, (bf->repr (bf "1e1000000")) should yield the largest real value in the representation. Infinite values, as in (bf->repr +inf.bf), should be interpreted as really large real values, not as infinite values. For example, the posit format has an "infinite" value, but it behaves more like a NaN, so converting bigfloat infinity to a posit should yield its largest real value instead.

The #:ordinal->repr and #:repr->ordinal functions represent ordinals as Racket integers between -2width−1 (inclusive) and 2width−1 (exclusive). Ordinals must be in real-number order; that is, if (repr->bf x) is less than (repr->bf y), then (repr->ordinal x) should be less than (repr->ordinal y).

The #:special function should return true for NaN values (or whatever your representation calls values that don't represent any real number) and false for all other values. Special values can be anywhere in the ordinal range, and you can have as many or as few of them as you want.

make-representation returns a representation object, which you can then use in define-representation and define-operation.

Defining Generators

Generators are helper functions for generating implementations in define-operation. For example, from-libm and from-rival are generators.

To define a generator, use define-generator:

(define-generator ((from-foo args ...) spec ctx)
body ...)

Here, from-foo is the name of your generator, and args are any additional arguments the generator takes. For example, from-libm takes one argument, the symbol name.

Then, inside the body, you can use those arguments as well as spec and ctx, to construct an actual implementation function.

The specification spec is a Racket tree made up of lists and symbols and numbers.

The signature ctx is "context" object; you can access its context-repr to get the operation's output representation, its context-vars to access its variable names (as a list of symbols), and its context-var-reprs to access its input representations (as a parallel list of representations). The context-lookup function can be used to look up a input argument's representation by name.