quad-fp: quadruple-precision floating point
| (require quad-fp) | package: quad-fp |
A toy Racket FFI binding to GCC’s libquadmath, exposing IEEE 754 quadruple-precision (128-bit, __float128) floating point — roughly 34 significant decimal digits, versus the ~16 of Racket’s native double flonums.
Values are opaque: a quad is shuttled across the FFI boundary as 128 bits and is only ever produced or consumed by the operations below. The qf… operations mirror libquadmath’s …q C functions — qfsqrt wraps sqrtq, qffma wraps fmaq, and so on — so libquadmath’s own documentation maps directly onto this API.
(require quad-fp) (define a (double-flonum->quad-flonum 1.0)) (define b (double-flonum->quad-flonum 1e-20)) ; In double, 1.0 + 1e-20 == 1.0; in quad the addend survives: (qf= (qf+ a b) a) ; => #f (quad-flonum->string (qf* quad-pi quad-pi)) ; => "9.86960440108935861883449099987615081"
The binding loads a native shared library (libquadf) that is compiled from source at install time, so a C toolchain with libquadmath must be present. See the package README for details.
1 Accuracy
Every result comes straight from libquadmath, so the accuracy is libquadmath’s and varies slightly across GCC versions. The package’s property tests, which check each operation against math/bigfloat at 113-bit precision, characterize it as follows:
qf+, qf-, qf*, and qf/ are correctly rounded on every libquadmath: each result is the exact value rounded to the nearest binary128, matching math/bigfloat bit for bit.
qfsqrt and qffma are correctly rounded on recent libquadmath, but only faithfully rounded (within one unit in the last place) on older builds. There sqrtq refines a double seed by Newton’s method with no final correcting step, and fmaq may double-round by up to one ulp — while still computing a true, cancellation-safe fused multiply-add.
The transcendental and special functions are not guaranteed correctly rounded; in practice they are within a few ulps. The gamma functions qftgamma and qflgamma are the least accurate and vary the most from one libquadmath to the next.
A quad result may therefore differ in its last bit or two from one produced by another correctly-rounded library.
2 Performance
Every operation crosses the Racket/C boundary and allocates its result — a fixed overhead of roughly 40–70 nanoseconds per call on x86-64, independent of the operation. The relative cost therefore depends on how much work the operation itself does. Arithmetic and qfabs are cheap in libquadmath (around 10 ns), so the boundary dominates: expect about 4–6× the cost of the same operation in C. qfsqrt and every transcendental or special function take hundreds to thousands of nanoseconds, leaving the boundary in the noise — within roughly 15%, and under 5% for the dearest ones such as qferf, qfpow, and qftgamma.
The pattern, measured on one x86-64 machine (gcc -O3, approximate ns per call):
operation |
| raw C |
| quad-fp/quadf |
| ratio |
| 7 |
| 38 |
| 5.5× | |
| 13 |
| 59 |
| 4.6× | |
| 14 |
| 65 |
| 4.6× | |
| 15 |
| 66 |
| 4.3× | |
| 308 |
| 350 |
| 1.14× | |
| 443 |
| 487 |
| 1.10× | |
| 469 |
| 540 |
| 1.15× | |
| 549 |
| 591 |
| 1.08× | |
| 1212 |
| 1256 |
| 1.04× | |
| 2837 |
| 2899 |
| 1.02× |
Absolute numbers are machine-dependent; the ratios are the portable part.
Those figures are for the untyped layer, quad-fp/quadf. The default quad-fp additionally wraps each export in the Typed Racket require/typed contract — another flat ~45 ns per call, a Quad? check on every argument and result. That roughly doubles the cheap arithmetic operations but is negligible for the expensive ones, and it applies to typed and untyped callers alike. Code that is bottlenecked on quad arithmetic and does not need the static types can (require quad-fp/quadf) to bypass it.
3 Datatype
type
4 Conversion
procedure
(string->quad-flonum s) → Quad?
s : string?
procedure
(quad-flonum->string q [precision]) → string?
q : Quad? precision : exact-integer? = 36
procedure
(quad-flonum->bytes q) → bytes?
q : Quad?
5 Arithmetic
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
6 Elementary functions
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
7 Rounding
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
a : Quad?
procedure
(qfnearbyint a) → Quad?
a : Quad?
8 Special functions
9 Other binary functions
procedure
a : Quad? b : Quad?
procedure
(qfremainder a b) → Quad?
a : Quad? b : Quad?
procedure
(qfcopysign a b) → Quad?
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
(qfnextafter a b) → Quad?
a : Quad? b : Quad?
procedure
a : Quad? b : Quad? c : Quad?
10 Comparison
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
procedure
a : Quad? b : Quad?
11 Classification
procedure
a : Quad?
procedure
(qfinfinite? a) → boolean?
a : Quad?
procedure
a : Quad?
procedure
(qfsignbit? a) → boolean?
a : Quad?
12 Constants
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value
value