quad-fp:   quadruple-precision floating point
1 Accuracy
2 Performance
3 Datatype
Quad-Flonum
Quad?
4 Conversion
double-flonum->quad-flonum
quad-flonum->double-flonum
string->quad-flonum
quad-flonum->string
quad-flonum->bytes
5 IEEE 754 behavior and errors
6 Arithmetic
qf+
qf-
qf*
qf/
7 Elementary functions
qfabs
qfsqrt
qfcbrt
qfpow
qfhypot
qfexp
qfexp2
qfexpm1
qflog
qflog2
qflog10
qflog1p
qfsin
qfcos
qftan
qfasin
qfacos
qfatan
qfatan2
qfsinh
qfcosh
qftanh
qfasinh
qfacosh
qfatanh
8 Rounding
qfceil
qffloor
qftrunc
qfround
qfrint
qfnearbyint
9 Special functions
qferf
qferfc
qflgamma
qftgamma
qfj0
qfj1
qfy0
qfy1
10 Other binary functions
qffmod
qfremainder
qfcopysign
qffdim
qfmax
qfmin
qfnextafter
qffma
11 Comparison
qf=
qf<
qf<=
qf>
qf>=
12 Classification
qfnan?
qfinfinite?
qffinite?
qfsignbit?
13 Constants
quad-pi
quad-pi/  2
quad-pi/  4
quad-1/  pi
quad-2/  pi
quad-2/  sqrt-pi
quad-e
quad-log2e
quad-log10e
quad-ln2
quad-ln10
quad-sqrt2
quad-1/  sqrt2
quad-max
quad-min
quad-epsilon
quad-denorm-min
quad-mant-dig
quad-dig
quad-decimal-dig
quad-min-exp
quad-max-exp
quad-min-10-exp
quad-max-10-exp
9.3.0.2

quad-fp: quadruple-precision floating point🔗ℹ

Shaobo He

 (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.

Examples:
> (require quad-fp)
> (define a (double-flonum->quad-flonum 1.0))
> (define b (double-flonum->quad-flonum 1e-20))
> (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🔗ℹ

The arithmetic operators are compiled as GCC __float128 operations; the elementary and special functions delegate to libquadmath. The exception is qfexp2, which uses powq(2,x) for compatibility with libquadmath versions that do not export exp2q. Accuracy therefore varies across GCC versions. The package’s property tests compare the core operations and functions for which there is a corresponding math/bigfloat operation at 113-bit precision. Their regression bounds are:

  • qf+, qf-, qf*, qf/, and qfabs must match math/bigfloat bit for bit for the generated finite inputs.

  • qfsqrt and qffma use the relative-error bound 2^{-110} (roughly 4–8 ulps, depending on the significand). They are bit-exact on recent libquadmath builds, while older implementations may be only faithfully rounded or may double-round.

  • The tested transcendental functions, including qflgamma, are held to relative error 2^{-106} (roughly 64–128 ulps). qftgamma varies much more across libquadmath versions and uses 2^{-90} (roughly 4–8 million ulps), which still requires about 27 correct decimal digits.

These are conservative regression-test bounds, not universal accuracy guarantees from libquadmath. Functions without a math/bigfloat counterpart, including the Bessel functions, are not covered by those property comparisons. When a reference result is zero, the suite treats the same numeric bound as an absolute rather than relative error limit. Do not rely on bit-for-bit reproducibility of transcendental results across libquadmath versions, especially for qftgamma.

2 Performance🔗ℹ

Every operation crosses the Racket/C boundary and allocates a result. That fixed cost is most visible for arithmetic and much less important for expensive transcendental functions. Exact timings depend on the Racket implementation, compiler, CPU, and libquadmath version, so measure on the deployment system:

make bench

QUADF_BENCH_ITERATIONS=500000 make bench

The benchmark reports nanoseconds per call, the Racket version, machine type, and iteration count. It measures the untyped quad-fp/quadf layer. The default quad-fp adds Typed Racket contracts at the boundary.

The untyped module uses ffi/unsafe, bypasses the public typed contracts, and exposes low-level representation accessors. Treat it as an optimization interface: use it only after profiling, pass values produced by this package, and do not depend on its extra exports remaining stable.

3 Datatype🔗ℹ

The type of quadruple-precision values. The public module is a Typed Racket facade over the untyped FFI core, so in typed code every operation here consumes and produces Quad-Flonums. The type is opaque — a value is created only by a conversion (such as double-flonum->quad-flonum or string->quad-flonum) or a named constant, never written as a literal. The module is equally usable from untyped Racket, where Quad-Flonum does not appear and the predicate Quad? takes its place.

procedure

(Quad? v)  boolean?

  v : any/c
Returns #t if v is a Quad-Flonum produced by this library, #f otherwise — Quad-Flonum’s runtime counterpart, and the predicate the procedure signatures below are written against.

The public entry point preserves its type information when used from Typed Racket:

#lang typed/racket/base
(require quad-fp)
(: square (Quad-Flonum -> Quad-Flonum))
(define (square x) (qf* x x))
(quad-flonum->string (square (string->quad-flonum "1.25")))

This usage is compiled and exercised by the package’s typed-client test.

4 Conversion🔗ℹ

procedure

(double-flonum->quad-flonum x)  Quad?

  x : flonum?

procedure

(quad-flonum->double-flonum q)  flonum?

  q : Quad?
Convert between a Racket double flonum? and a quad. Widening a double is exact; narrowing back to a double rounds to nearest.

procedure

(string->quad-flonum s)  Quad?

  s : string?

procedure

(quad-flonum->string q [precision])  string?

  q : Quad?
  precision : (integer-in 1 12000) = 36
Parse a C-locale number accepted by strtoflt128, and render a quad to a decimal string with up to precision significant digits. Accepted input includes decimal and hexadecimal floating-point syntax, signed infinities, and NaNs (including payload syntax). Decimal conversion is locale-independent and always uses . as the decimal separator.

Apart from surrounding C-locale whitespace, string->quad-flonum requires the entire input to be consumed as a valid number; malformed strings and strings with trailing nonnumeric text raise exn:fail:contract instead of silently producing the value of a numeric prefix. A syntactically valid value outside binary128’s range is not a parse error: overflow produces signed infinity and underflow produces signed zero.

quad-flonum->string accepts precisions from 1 through 12000 and sizes its output dynamically, so it returns the complete representation even when it is longer than a small fixed buffer. The maximum exceeds the roughly 11,600 significant digits needed by the longest exact finite binary128 expansion. The default of 36 is quad-decimal-dig, the number of digits sufficient to round-trip any finite binary128 value exactly. Textual NaN formatting does not preserve payload or signalling state.

procedure

(quad-flonum->bytes q)  bytes?

  q : Quad?
Returns the 16-byte object representation of q’s __float128 value in the platform’s native byte order. On x86-64 this is the value’s little-endian byte string. The representation is platform-dependent and is not a portable serialization format.

5 IEEE 754 behavior and errors🔗ℹ

Arithmetic and mathematical domain errors follow GCC/libquadmath floating-point semantics instead of raising Racket exceptions. For example, division by zero produces signed infinity, invalid operations produce NaN, and overflow or underflow produces infinity, subnormal values, or signed zero as appropriate. The binding does not expose C errno or floating-point exception flags.

NaN is unordered: every comparison involving NaN, including qf=, returns #f. Positive and negative zero compare equal, while qfsignbit? distinguishes them. qfmin and qfmax follow fminq/fmaxq: if exactly one operand is NaN they return the numeric operand. When both operands are zeros with different signs, the returned zero’s sign follows the libquadmath implementation; use qfcopysign when the sign is semantically important.

6 Arithmetic🔗ℹ

procedure

(qf+ a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qf- a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qf* a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qf/ a b)  Quad?

  a : Quad?
  b : Quad?
Quad-precision addition, subtraction, multiplication, and division.

7 Elementary functions🔗ℹ

procedure

(qfabs a)  Quad?

  a : Quad?

procedure

(qfsqrt a)  Quad?

  a : Quad?

procedure

(qfcbrt a)  Quad?

  a : Quad?

procedure

(qfpow a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfhypot a b)  Quad?

  a : Quad?
  b : Quad?
Absolute value, square and cube roots, a raised to b, and (qfsqrt (qf+ (qf* a a) (qf* b b))) without overflow.

procedure

(qfexp a)  Quad?

  a : Quad?

procedure

(qfexp2 a)  Quad?

  a : Quad?

procedure

(qfexpm1 a)  Quad?

  a : Quad?

procedure

(qflog a)  Quad?

  a : Quad?

procedure

(qflog2 a)  Quad?

  a : Quad?

procedure

(qflog10 a)  Quad?

  a : Quad?

procedure

(qflog1p a)  Quad?

  a : Quad?
Exponentials (qfexpm1 computes ex-1 accurately near 0) and logarithms (qflog1p computes log(1+x)). qfexp2 is implemented as powq(2,x), not exp2q.

procedure

(qfsin a)  Quad?

  a : Quad?

procedure

(qfcos a)  Quad?

  a : Quad?

procedure

(qftan a)  Quad?

  a : Quad?

procedure

(qfasin a)  Quad?

  a : Quad?

procedure

(qfacos a)  Quad?

  a : Quad?

procedure

(qfatan a)  Quad?

  a : Quad?

procedure

(qfatan2 a b)  Quad?

  a : Quad?
  b : Quad?
Trigonometric functions and their inverses; qfatan2 is the two-argument arctangent.

procedure

(qfsinh a)  Quad?

  a : Quad?

procedure

(qfcosh a)  Quad?

  a : Quad?

procedure

(qftanh a)  Quad?

  a : Quad?

procedure

(qfasinh a)  Quad?

  a : Quad?

procedure

(qfacosh a)  Quad?

  a : Quad?

procedure

(qfatanh a)  Quad?

  a : Quad?
Hyperbolic functions and their inverses.

8 Rounding🔗ℹ

procedure

(qfceil a)  Quad?

  a : Quad?

procedure

(qffloor a)  Quad?

  a : Quad?

procedure

(qftrunc a)  Quad?

  a : Quad?

procedure

(qfround a)  Quad?

  a : Quad?

procedure

(qfrint a)  Quad?

  a : Quad?

procedure

(qfnearbyint a)  Quad?

  a : Quad?
Round toward +∞, -∞, zero, and nearest (ties away from zero for qfround; using the current rounding mode for qfrint and qfnearbyint).

9 Special functions🔗ℹ

procedure

(qferf a)  Quad?

  a : Quad?

procedure

(qferfc a)  Quad?

  a : Quad?

procedure

(qflgamma a)  Quad?

  a : Quad?

procedure

(qftgamma a)  Quad?

  a : Quad?

procedure

(qfj0 a)  Quad?

  a : Quad?

procedure

(qfj1 a)  Quad?

  a : Quad?

procedure

(qfy0 a)  Quad?

  a : Quad?

procedure

(qfy1 a)  Quad?

  a : Quad?
Error functions, log-gamma and gamma, and Bessel functions of the first (qfj0, qfj1) and second (qfy0, qfy1) kind, orders 0 and 1. qflgamma returns log(|Gamma(a)|); the sign reported through C’s signgam is not exposed.

Concurrency note: libquadmath’s lgammaq, which implements qflgamma, writes the process-global C variable signgam. That makes qflgamma unsafe to run concurrently with another qflgamma call or with native code outside the package that calls lgammaq or accesses signgam. Serialize all such access at the application level.

10 Other binary functions🔗ℹ

procedure

(qffmod a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfremainder a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfcopysign a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qffdim a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfmax a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfmin a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qfnextafter a b)  Quad?

  a : Quad?
  b : Quad?

procedure

(qffma a b c)  Quad?

  a : Quad?
  b : Quad?
  c : Quad?
Floating-point remainder, IEEE remainder, sign copying, positive difference, maximum, minimum, next representable value toward b, and the fused multiply-add (qf+ (qf* a b) c), computed with a single rounding where libquadmath provides it (see Accuracy).

11 Comparison🔗ℹ

procedure

(qf= a b)  boolean?

  a : Quad?
  b : Quad?

procedure

(qf< a b)  boolean?

  a : Quad?
  b : Quad?

procedure

(qf<= a b)  boolean?

  a : Quad?
  b : Quad?

procedure

(qf> a b)  boolean?

  a : Quad?
  b : Quad?

procedure

(qf>= a b)  boolean?

  a : Quad?
  b : Quad?
Quad-precision comparisons, returning Racket booleans.

12 Classification🔗ℹ

procedure

(qfnan? a)  boolean?

  a : Quad?

procedure

(qfinfinite? a)  boolean?

  a : Quad?

procedure

(qffinite? a)  boolean?

  a : Quad?

procedure

(qfsignbit? a)  boolean?

  a : Quad?
Test for NaN, infinity, finiteness, and a set sign bit.

13 Constants🔗ℹ

value

quad-pi : Quad?

value

quad-pi/2 : Quad?

value

quad-pi/4 : Quad?

value

quad-1/pi : Quad?

value

quad-2/pi : Quad?

value

quad-2/sqrt-pi : Quad?

value

quad-e : Quad?

value

quad-log2e : Quad?

value

quad-log10e : Quad?

value

quad-ln2 : Quad?

value

quad-ln10 : Quad?

value

quad-sqrt2 : Quad?

value

quad-1/sqrt2 : Quad?

The mathematical constants from libquadmath’s M_*q macros (π, π/2, π/4, 1/π, 2/π, 2/√π, e, log₂e, log₁₀e, ln 2, ln 10, √2, 1/√2).

Largest finite, smallest positive normal, machine epsilon, and smallest positive subnormal (FLT128_MAX, FLT128_MIN, FLT128_EPSILON, FLT128_DENORM_MIN).

Format characteristics corresponding to libquadmath’s FLT128_* macros. quad-mant-dig is the 113-bit significand width. quad-dig is 33, the decimal precision: a decimal value with at most that many significant digits survives a decimal-to-binary128-to-decimal conversion unchanged. quad-decimal-dig is 36, the number of digits sufficient for every binary128 value to survive a binary128-to-decimal-to-binary128 round trip. The remaining constants give the binary and decimal exponent ranges.