276: Type-specific Flonum Libraries

by Peter McGoron

Status

This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-276@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

This SRFI is an updated version of SRFI 144 that allows an implementation to support multiple flonum representations. Each flonum has its own separate library. Each library also has the ability to inspect properties of the flonum operations, such as rounding mode and deviations from IEEE 754 arithmetic. New flonum operations are also available, such as serialization and operations corresponding to IEEE 754-2019 and C23.

Table of Contents

  1. Issues
  2. Rationale
    1. Speed and Portability vs. Ease of Use
    2. Criteria for inclusion
  3. Terminology
  4. Precise representation of flonums
  5. Argument conventions
  6. Library names
  7. Floating-point library
    1. NaN conventions
    2. Numeric constants
    3. Implementation constants
    4. Constructors
    5. Accessors
    6. Predicates
    7. Arithmetic
    8. Integer rounding
    9. Integer division
    10. Exponentials
    11. Logarithms
    12. Powers and roots
    13. Trigonometric functions
    14. Inverse trigonometric functions
    15. Hyperbolic functions
    16. Special functions
    17. Implementation query
    18. Serialization and deserialization
  8. Rounding mode libraries
  9. Floating point exception library
  10. Floating point environment library
  11. Implications of IEEE arithmetic for optimizers
  12. Examples
  13. Considerations for inexact number vectors
  14. Reader syntax suggestions
  15. Implementation
  16. Acknowledgements
  17. Appendix 1: Relationship between this SRFI and other standards
  18. Bibliography
  19. Copyright

Issues

Rationale

This section is non-normative.

Standard Scheme doesn’t give specifics about the precision and range of inexact numbers. From the R4RS onward, implementations could use s, f, d, and l to denote inexact constants of different precisions. The R6RS and SRFI 144 included “flonum” operations. However, these specifications do not specify what the format of the flonum is. The flonum might not be an IEEE format number and operations may differ from implementation to implementation.

This SRFI proposes a variant of SRFI 144 that is organized into representation-specific libraries. The functions exported from a specific library operate on a precisely defined number format. For example, if one wanted to operate on binary64 floating point numbers, one can import (srfi 276 binary64).

This SRFI also updates SRFI 144 to include the recommended procedures described in IEEE 754-2019, which are further included in C23. An implementation of this library can implement the new procedures as an FFI to C23: the definitions between this SRFI and C23 are harmonized.

Speed and Portability vs. Ease of Use

One reason to use type-specific procedures is speed: the function sqrt from (srfi 276 binary32) can be compiled to a single FSQRT instruction on a RISC-V processor. One could also compile multiple square roots to a single vectorized SQRTPS instruction on an x86_64 processor with SSE2.

Another reason to use type-specific procedures is portability. Given the same rounding mode, format, and IEEE 754 conformance flag below, operations like +, -, and √ will always return the same value given the same inputs.

Most programmers do not have the speed and portability of floating point operations as their top priorities. They want their floating point calculations to work well above blazing speeds or bit-for-bit reproducibility across architectures. Basically, floating-point should do “what they want.” A type-flexible system is more likely to do what the non-numerically inclined programmer wants: see Kahan 1997 p. 29 and Kahan and Darcy 1998 pp. 60ff.

Scheme’s module system, lack of special arithmetic syntax, and latent typing allow us to separate strict correctness and “do what I want.” Programmers who wish for their programs to do “what they want” should use Scheme’s generic arithmetic. An implementation is free to do things like widen operands or optimize expressions (for example, using the SSE2 instruction RSQRTPS for (/ 1 (sqrt x))) without worrying about strict reproducibility or the absolute fastest speed.

Criteria for inclusion

This SRFI defines procedures that mirror functions in C23’s math library, which are in turn based off of IEEE 754-2019’s recommended functions. These functions are expected to be widely available with stable implementations.

There are many, many special functions that exist, such as the functions in TR24747 and SciPy. They are not included in this SRFI, as they are very specialized and there might not be enough experience with them to standardize an interface and domain. Hopefully with this library as a standard component of Scheme implementations, the entire range of special functions can be implemented in pure Scheme.

Terminology

All references to IEEE 754 refer to its 2019 revision.

A representation is a type of inexact number that has fixed properties, like exponent range and mantissa width. Examples include binary32, binary64, and posit32 (Gustafson 2022).

An operation is correctly rounded if the returned value is the same as if the operation were calculated to infinite precision, and then rounded to fit in the resulting representation according to the current rounding mode.

Signaling NaN and quiet NaN are defined in the IEEE 754: implementations and representations should differentiate between the two. If an implementation or representation does not differentiate between the two, then all NaNs must be considered quiet. Thus, if an implementation has only quiet NaNs, then statements about the behavior of procedures on signaling NaNs do not apply.

A floating-point exception is a flag in an environment that is raised if a floating point operation does something exceptional, such as overflow. They are not the same as Scheme exceptions. Some operations raise a floating-point exception on certain arguments. Implementations that support the floating point exception library should raise floating-point exceptions when generic arithmetic is done in such a way that would raise the exception when using the floating point library.

If an implementation does not support the floating point environment library, then it does not need to implement the exception behavior as described in the library section. Additionally, an implementation may support other exception handling modes, such as raising a Scheme exception when a floating-point exception is raised. However, every implementation must support a non-stop mode, where all operations return values when given the correct argument types.

When an operation is implementation defined, then the implementation should document what choice or procedure they use for that behavior. For example, the implementation should document the default rounding mode.

Precise representation of flonums

The examples in the SRFI use an extension to the syntax of Scheme datums that explicitly states the representation of a flonum. This extension might be useful on implementations with wildly varying representations flonum, such as decimal floats or posit numbers.

The following modification to the grammar implements this representation:

  ⟨real R⟩ → … | ⟨real numeral R⟩
           | ⟨represented flonum R⟩
  ⟨real numeral R⟩ → ⟨sign⟩ ⟨ureal R⟩ | ⟨infnan⟩
  ⟨represented flonum R⟩ → #fl( ⟨representation name⟩ ⟨real numeral R)
  ⟨representation name⟩ → binary16 | binary32 | …

For example, #fl(binary256 1e400) reads as a finite number, while #fl(binary64 1e400) reads the same as #fl(binary64 +inf.0). The syntax allows for complex numbers to be written with mixed precision: for example, #fl(binary32 1.0)+#fl(binary64 2.0)i.

Implementing this reader syntax is not a requirement for supporting this SRFI. The sample implementation does not have an implementation of this syntax.

Argument conventions

The argument names have the same meanings as the R7RS; it is an error if the wrong arguments are passed to a procedure. It is an error if:

Square brackets [] are used to denote a group of arguments that are optional, but all arguments must be present or absent. If one pair of square brackets is nested in another pair, then the nested pair is optional even when the other arguments are supplied.

When endianness is not supplied in a procedure, it is the native endianness, which is an implementation-specified valid value.

Library names

The implementation must export the following libraries:

(srfi 276)
Floating point library of implementation-defined representation. This should be an IEEE-754 binary floating point representation.

The following library names, if available, must implement the library described in the sections below.

(srfi 276 binary16)
Operates on IEEE 754 binary16 values.
(srfi 276 binary32)
Operates on IEEE 754 binary32 (AKA single-precision floating-point) values.
(srfi 276 binary64)
Operates on IEEE 754 binary64 (AKA double-precision floating-point) values.
(srfi 276 binary128)
Operates on IEEE 754 binary128 values.
(srfi 276 binary256)
Operates on IEEE 754 binary256 values.

The following library names are reserved (where ⟨n⟩ is a base-10 numeral). They are reserved because some of the functions in the flonum library may not be appropriate for these format numbers. A future SRFI or Report will define operations on these representations.

(srfi 276 decimal⟨n⟩)
Operates on IEEE 754 decimal formats.
(srfi 276 complex-⟨format⟩⟨n⟩) where ⟨format⟩ is either binary or decimal
Operates on complex numbers represented as two values in that IEEE format.
(srfi 276 binary⟨n⟩) for ⟨n⟩ not previously defined
Reserved for future IEEE 754 revisions.

The following libraries are optional, but if present, must export identifiers that act as described:

(srfi 276 rounding-mode)
Procedures to query the rounding mode.
(srfi 276 with-rounding-mode)
Operation on the rounding mode in a dynamic extent.
(srfi 276 set-rounding-mode)
Operation on the global rounding mode.
(srfi 276 exceptions)
Operation regarding floating-point exceptions.
(srfi 276 environment)
Operation on the floating point environment.

An implementation may provide libraries with different names than the ones above. Such a library should implement all of the procedures described below. For example, an implementation could provide (srfi 276 posit32) for operations on posits, with a similar API to the one below. However, posits do not have infinite values, so flinfinite? would not be exported.

Floating-point library

The floating-point libraries are an extension of SRFI 144. Unlike SRFI 144, this library can be used for representations of any radix, in particular 2 and 10. However, this library does not define quantization or reencoding procedures for decimal numbers. This library assumes that the floating point format is similar to the IEEE formats.

Rationale: For compatability with previous code and familiarity, the procedures retain their fl prefixes, even though there may be multiple procedures in different libraries with the same name. This is because most implementations only have one floating point type.

Previous versions of this SRFI removed the fl prefix and instead prefixed everything with :. This has been reverted because it optimized for the uncommon case, and not to mention was somewhat unsightly.

Throughout all code examples, when the floating point representation is important, the identifiers will be prefixed with f64: for binary64 numbers, and f32: for binary32 numbers. All other prefixes will be defined by import statements in the example code.

NaN conventions

This SRFI does not require that implementations support all the NaN operations and values that IEEE 754-2019 supports. Implementations that do support the full range of NaN values should implement SRFI 208, which is designed to support multiple inexact real formats.

By default, all operations that return flonums will return a quiet NaN if they are given a quiet NaN in any input. Any exceptions are noted. Implementations should propagate NaNs if they have one or more NaN inputs.

By default, giving a signaling NaN to a procedure will cause the invalidOperation floating point exception to be raised and will cause a NaN to be returned.

It is unspecified what the sign of a NaN is. Implementations should make +nan.0 have a positive sign, and -nan.0 have a negative sign, and have that sign be reflected in the machine representation of the NaN.

Numeric constants

The numeric constants of SRFI 144 are exported for the number type of this library. The following are renamed:

OldNew
fl-e-2fl-e^2
fl-e-pi/4fl-e^pi/4
fl-pi-squaredfl-pi^2
fl-e-eulerfl-e^euler

The identifiers fl-e, fl-1/e, fl-log2-e, fl-log10-e, fl-log-2, fl-1/log-2, fl-log-3, fl-log-pi, fl-log-10, fl-1/log-10, fl-pi, fl-1/pi, fl-2pi, fl-pi/2, fl-pi/4, fl-2/sqrt-pi, fl-degree, fl-2/pi, fl-sqrt-2, fl-sqrt-3, fl-sqrt-5, fl-sqrt-10, fl-1/sqrt-2, fl-cbrt-2, fl-cbrt-3, fl-4thrt-2, fl-phi, fl-log-phi, fl-1/log-phi, fl-euler, fl-sin-1, fl-cos-1, fl-gamma-1/2, fl-gamma-1/3, and fl-gamma-2/3 are defined by the same formulas as their corresponding SRFI 144 identifiers.

Implementation constants

(srfi 276 ⟨library⟩)
value
fl-radix

Radix of the floating point representation. For binary representations such as binary64, this is 2. The variable b is set to this value throughout this SRFI.

(srfi 276 ⟨library⟩)
value
fl-precision

Length of the significand of the floating point representation.

Note: This is not the same as the fixed number of bits in the machine format of the floating point representation. For example, binary64 numbers have a precision of 53, because the underlying number is 1 plus the 52 bits stored in the number, appropriately scaled. The precision of decimal64 numbers is 16 (16 decimal digits).
(srfi 276 ⟨library⟩)
value
fl-maximum-exponent

The maximum exponent a normalized floating point number, represented as (-1)s(s0.s1…)×2e can have, represented as an exact integer. For binary64 numbers, this is 1023.

(srfi 276 ⟨library⟩)
value
fl-minimum-normalized-exponent

The minimum exponent a normalized floating point number, represented as (-1)s(s0.s1…)×2e can have, represented as an exact integer. For IEEE 754 representations, this is (- 1 fl-maximum-exponent).

(srfi 276 ⟨library⟩)
value
fl-minimum-exponent

The minimum exponent of any non-zero floating point number.

(srfi 276 ⟨library⟩)
value
fl-greatest

The largest normalized floating point number.

(srfi 276 ⟨library⟩)
value
fl-least

The smallest positive floating point number. This number may not be normalized.

(srfi 276 ⟨library⟩)
value
fl-least-normal

The smallest positive normal floating point number.

(srfi 276 ⟨library⟩)
value
fl-epsilon

The difference between 1.0 and the least normalized value greater than 1.0 that is representable in this type. This is defined as (fl- (fladjacent 1.0 +inf.0) 1.0).

Note: This is sometimes called the machine epsilon, although some define it to be one half of the definition given in this SRFI. This definition is the same as C's definition.
(srfi 276 ⟨library⟩)
value
fl-byte-width

Size of the flonum in bytes as an exact integer. For the binary64 representation, this is 8.

Constructors

(srfi 276 ⟨library⟩)
procedure
(flonum z)

Convert a number into a flonum.

If z is not real, then a NaN is returned.

If z is exact, then it is converted to inexact before the following rules are applied.

If z is a signed zero, return a zero flonum with the sign of z.

If z is a finite number, and there is a flonum that is equal to it in the sense of =, return that flonum.

If z is a finite number that is between two flonums, return the closest flonum, rounding in an implementation defined manner.

If z is a finite number that is greater than all flonums, or less than all flonums, either the closest flonum or the appropriately signed infinity is returned.

If z is an infinite number, then return an infinite flonum with the appropriate sign.

If z is a NaN, return a NaN.

(srfi 276 ⟨library⟩)
procedure
(fladjacent fl1 fl2)

If fl1 = fl2, returns fl2.

Otherwise return the next representable flonum from fl1 in the direction of fl2.

(f64:fladjacent 1.0 +inf.0) ⇒ #fl(binary64 1.0000000000000002)
(srfi 276 ⟨library⟩)
procedure
(flcopysign fl1 fl2)

Return a number with the magnitude of fl1 and the sign of fl2.

Even when fl1 is a signaling NaN, this procedure does not signal invalidOperation.

(flcopysign +inf.0 -1.0) ⇒ -inf.0
(flcopysign +0.0 -1.0) ⇒ -0.0
(srfi 276 ⟨library⟩)
procedure
(make-flonum fl n)

Return fl×bn correctly rounded. The allowed values of n are implementation-dependent and may depend on fl. However, this procedure must round-trip with flnormalized-fraction-exponent.

This procedure raises no floating-point exceptions when given NaNs.

Note: In SRFI 144, this was given the semantics of the C function ldexp, where it always scaled by a power of 2. This has been changed to the behavior of scalbn, because it is more general. When the radix is 2, this procedure and make-flonum in SRFI 144 are equivalent.

Accessors

(srfi 276 ⟨library⟩)
procedure
(flinteger-fraction fl)

Returns two values, the integral part of fl as a flonum and the fractional part of fl as a flonum.

If fl is zero, the procedure returns fl for both values.

If fl is infinity, the integral part is fl, and the fractional part is (flcopysign 0.0 fl).

If fl is a NaN, then a NaN is returned for both values.

(srfi 276 ⟨library⟩)
procedure
(flexponent fl)

Returns the exponent of fl as a flonum. If fl is subnormal, it is treated as though it were normalized.

If fl is zero, then negative infinity is returned and a divide-by-zero floating point exception is signalled.

If fl is infinity, return fl.

(srfi 276 ⟨library⟩)
procedure
(flnormalized-fraction-exponent fl)

Returns two values, a correctly signed fraction y whose absolute value is in [1/b,1), and an exact integer exponent e such that fl = y×bn.

If fl is zero, returns fl and exact zero.

If fl is infinite, returns fl and an unspecified integer.

If fl is NaN, returns a NaN and an unspecified integer.

Note: When the radix is 2, this corresponds to flnormalized-fraction-exponent in SRFI 144 and frexp in C.
(srfi 276 ⟨library⟩)
procedure
(flinteger-exponent fl)
(srfi 276 ⟨library⟩)
value
fl-integer-exponent-zero
(srfi 276 ⟨library⟩)
value
fl-integer-exponent-nan

Returns the same as flexponent as an exact integer.

When fl is zero, returns fl-integer-exponent-zero, which is a negative exact integer less than the smallest exponent returned by a finite value.

When fl is NaN, returns fl-integer-exponent-nan, which is an exact integer whose magnitude is larger than the magnitude of any exponent returned by a finite value.

When fl is infinite, returns an unspecified integer larger than the magnitude of any exponent returned by a finite value.

Note: The SRFI 144 version of this procedure deferred to C, but C defines the constants in terms of maximum integers, which is not applicable to Scheme.
(srfi 276 ⟨library⟩)
procedure
(flsign-negative? fl)

Returns true if the sign of fl is negative, and false otherwise.

Note: In SRFI-144, the similar procedure flsignbit returned 1 for a negative sign and 0 otherwise.

Predicates

Unless otherwise specified, none of these procedures raise exceptions when given signaling NaNs.

(srfi 276 ⟨library⟩)
procedure
(flonum? obj)

Returns #t if obj is a flonum and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(fl=? fl …)
(fl<? fl …)
(fl>? fl …)
(fl<=? fl …)
(fl>=? fl …)

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing; they return #f otherwise. These predicates must be transitive.

When passed zero or one arguments, they always return true, even when that number is NaN.

When passed two or more arguments where one of them is a signaling NaN, the procedures raise the invalidOperation floating-point exception.

When all operations except fl=? are passed a quiet NaN, the procedures raise the invalidOperation floating-point exception.

If NaN is passed as an argument to these procedures (when the number of arguments is greater than 1), the procedure returns false. A NaN is not equal to itself.

Positive infinity compares greater than any finite number. Negative infinity compares less than any finite number.

(srfi 276 ⟨library⟩)
procedure
(fl!=? fl1 fl2)

This predicate returns #t when the two values are not equal. When passed a signaling NaN, this procedure raises the invalidOperation floating-point exception. When given NaN values, this procedure returns #f.

Rationale: Since NaNs are unordered, (not (fl=? fl1 fl2)) is not the same as (fl!=? fl1 fl2).

(srfi 276 ⟨library⟩)
procedure
(fltotal=? fl …)
(fltotal<? fl …)
(fltotal>? fl …)
(fltotal<=? fl …)
(fltotal>=? fl …)

Imposes a total ordering on flonums consistent with IEEE 754. The following describes the order:

  1. If (fl<? fl1 fl2) returns #t, then (fltotal<? fl1 fl2) returns #t.
  2. (fltotal<? -0.0 +0.0) returns #t.
  3. (fltotal<? +0.0 -0.0) returns #f.
  4. If fl1 and fl2 are eqv?, then (fltotal=? fl1 fl2) returns #t.
  5. Let nan be a particular NaN value. Then exactly one of the two statements must be true:
    1. For every non-NaN flonum fl, (fltotal<? nan fl) evaluates to #t.
    2. For every non-NaN flonum fl, (fltotal<? nan fl) evaluates to #f.
    Thus a NaN value is either less than all non-NaNs, or greater than all non-NaNs.
  6. If fl1 and fl2 are both non-eqv? NaNs, then it is unspecified which one occurs after the other in the ordering.

Although the ordering is underspecified, any implementation must make choices consistent with the fact that fltotal<? is a total ordering that respects trichotomy. Implementations should order NaNs according to their sign and if they are signalling/quiet, as described in IEEE 754, and should order NaNs with the same sign and signalling/quiet status by the total ordering of their unsigned integer payloads.

There is no requirement that the order be total when one of the arguments is a non-canonical flonum.

Rationale: A total ordering on flonums is useful for when the flonums are stored in a set structure.

When the numbers are in a IEEE 754 binary floating point representation, then for non-NaN flonums fl1 and fl2 that are not both zero, the regular and total orderings coincide. The definition is written so that the ordering is forwards compatible with IEEE 754 decimal floats, which have numerically equal yet bitwise-unequal numbers, and also non-canonical encodings. The total ordering of decimal floats is more complicated.

(srfi 276 ⟨library⟩)
procedure
(flunordered? fl1 fl2)

This procedure returns #t if one of its arguments is a NaN, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flinteger? fl)

Returns #t when the argument is an integer flonum, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flzero? fl)

Returns #t when the argument is a zero, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flpositive? fl)

Returns #t when the argument is greater than 0, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flnegative? fl)

Returns #t when the argument is less than 0, and #f otherwise.

(flnegative? -0.0) ⇒ #f
(srfi 276 ⟨library⟩)
procedure
(flodd? ifl)

Returns #t when the argument is odd, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(fleven? ifl)

Returns #t when the argument is even, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flfinite? fl)

Returns #t if fl has a finite value: that is, if it is not infinite or NaN, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flinfinite? fl)

Returns #t if fl is infinite, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flnan? fl)

Returns #t if fl is a NaN, and #f otherwise.

(srfi 276 ⟨library⟩)
procedure
(flnormal? fl)

Returns #t if fl is a normalized number: that is, it is not subnormal, zero, infinite, or NaN, and #f otherwise.

Note: This procedure was renamed from flnormalized? in SRFI 144.

(srfi 276 ⟨library⟩)
procedure
(flsubnormal? fl)

Note: This procedure was renamed from fldenormalized? in SRFI 144.

Returns #t if fl is a subnormal number, and #f otherwise.

Arithmetic

(srfi 276 ⟨library⟩)
procedure
(flmax fl …)
(flmax-abs fl …)
(flmax-filter-nans fl …)
(flmax-abs-filter-nans fl …)
(flmin fl …)
(flmin-abs fl …)
(flmin-filter-nans fl …) (flmin-abs-filter-nans fl …)

Returns the maximum/minimum value of the argument of the list of arguments. For the purposes of this procedure, negative zero is less than positive zero.

For the maximum procedures, if no value is given, they return negative infinity. For the minimum procedures, if no value is given, they return positive infinity.

If flmax or flmin have a NaN in any of its arguments, then a NaN is returned.

If either flmax-filter-nans or flmin-filter-nans have a NaN in its arguments, it is ignored unless all inputs are NaNs, in which case a NaN is returned.

The procedures with abs in the name compare two non-NaN arguments by absolute value. If the two arguments are equal in magnitude, they are then compared by the procedure without abs in the name.

Note: The flmax/flmin procedures correspond to the R6RS’s flmax/flmin, while the flmax-filter-nans/flmin-filter-nansprocedure corresponds to the flmax/flmin procedure in SRFI 144.
(srfi 276 ⟨library⟩)
procedure
(fl+ fl …)

Return the sum of the arguments. If no arguments are given, positive zero is returned. If one argument is given, that argument is returned. The following special cases apply:

  1. If any argument is a NaN, NaN is returned.
  2. Infinity added to a finite number returns that infinity.
  3. Two infinities of the same sign return an infinity of the same sign.
  4. Two infinities of opposite signs return a NaN.
  5. Let fl be a zero. Then (fl+ fl fl) returns fl.
  6. Let fl1 and fl2 be numbers with opposite signs that sum exactly to zero. Then the returned zero is negative in roundTowardsNegative mode, and positive in all other rounding modes.

The following examples assume that the rounding mode is not roundTowardsNegative.

(fl+ 0.0 -0.0) ⇒ +0.0
(fl+ -0.0 0.0) ⇒ +0.0
(fl+ -0.0 -0.0) ⇒ -0.0

When given two arguments, this procedure always returns the correctly rounded value. When given more than two arguments, the implementation may rearrange arguments or use an algorithm like compensated summation to minimize error.

(srfi 276 ⟨library⟩)
procedure
(fl- fl1 fl2 …)

When one argument is given, the value is returned with the sign bit flipped. When more than one argument is given, the procedure is the equivalent of (fl+ fl1 (fl- fl2) …).

(srfi 276 ⟨library⟩)
procedure
(fl* fl …)

When given no arguments, returns positive 1.0. When given one argument, returns that argument.

When given more than one argument, computes the product of the arguments. When no argument is NaN and no operation returns a NaN, the returned sign is the exclusive OR of the sign bits of each argument. The following special cases apply:

  1. If any argument is a NaN, NaN is returned.
  2. Infinity times a finite number returns an infinity.
  3. Infinity times a zero returns a NaN.

When given two arguments, this procedure always returns the correctly rounded value. When given more than two arguments, the implementation may rearrange arguments or use an alternative algorithm to minimize error.

(srfi 276 ⟨library⟩)
procedure
(fl/ fl1 fl2 …)

When given one argument, computes the reciprocal of the given argument. The reciprocal of infinity is the appropriately signed zero, and the reciprocal of zero is the appropriately signed infinity.

When given more than one argument, divides the first argument by the rest. The returned sign, when the number is not NaN, is the exclusive OR of the signs of the input arguments. The following special cases apply:

  1. If any argument is a NaN, NaN is returned.
  2. Infinity divided by a finite number returns an infinity.
  3. Infinity divided by a finite number returns an infinity and signals the invalid operation exception.
  4. Zero divided by zero returns a NaN.
  5. Infinity divided by infinity returns a NaN.

When given one or two arguments, the correctly rounded value is returned.

(srfi 276 ⟨library⟩)
procedure
(fl+* fl1 fl2 fl3)

Return fl1*fl2+fl3 correctly rounded.

When the value of the operation is exactly zero, it has the same rule for the sign of zero as the sum operation. If the value is rounded to zero, it takes the sign of the exact result.

If, interpreted separately, the sum or multiplication would return a NaN, then this operation will return a NaN.

(srfi 276 ⟨library⟩)
procedure
(flabs fl)

Returns the absolute value of fl. The absolute value of any zero is positive zero, and the absolute value of any infinity is positive infinity.

(srfi 276 ⟨library⟩)
procedure
(flabsdiff fl1 fl2)

Returns |fl1 - fl2|. The behavior on infinities, zeroes, and NaNs is equivalent to the behavior of (flabs (fl- fl1 fl2)).

(srfi 276 ⟨library⟩)
procedure
(flposdiff fl1 fl2)

Let x = fl1 - fl2. If x is positive, return it. If x is a NaN, return that NaN. Otherwise return positive zero.

(srfi 276 ⟨library⟩)
procedure
(flsgn fl)

Equivalent to (flcopysign 1.0 fl).

(srfi 276 ⟨library⟩)
procedure
(flnumerator fl)
(fldenominator fl)

Returns the numerator/denominator of fl as a flonum. The numerator and denominator must represent fl in lowest terms, although any pair of numbers that when divided yield fl in the current rounding mode is acceptable, subject to the constraints below.

The denominator is always positive. The numerator of an infinite flonum is itself. The denominator of an infinite or zero flonum is 1.0. The numerator and denominator of a NaN is a NaN.

Integer rounding

(srfi 276 ⟨library⟩)
procedure
(flfloor fl)
(flceiling fl)
(fltruncate fl)
(flround fl)
(flround-away fl)

These procedures find an integer near fl. When given an infinity or zero, these procedures always return that value. When given a NaN, they return a NaN.

The flfloor procedure returns the greatest integer flonum not larger than fl.

The flceiling procedure returns the least integer flonum not larger than fl.

The fltruncate procedure returns the closest integer flonum whose absolute value is not not larger than the absolute value of fl.

The flround procedure returns the closest integer flonum: when fl is halfway between two integers, it rounds to even.

The flround-away procedure returns the closest integer flonum: when fl is halfway between two integers, it rounds to the number further from zero.

(flround-away 2.5) ⇒ 3.0
(flround 2.5) ⇒ 2.0
(flround-away 3.5) ⇒ 4.0
(flround 3.5) ⇒ 4.0

Note: The flround procedure in the R6RS and SRFI 144 implements Scheme’s round ties-to-even behavior, which is the behavior of roundeven in C11.

Integer division

The following special cases apply to the procedures in this section:

OperationSide conditionsExceptionsReturn
(⟨prefix⟩-remainder ±0.0 y) y ≠ 0.0 ±0.0
(⟨prefix⟩-remainder x y) y = 0 invalidOperation NaN
(⟨prefix⟩-remainder x y) x infinite invalidOperation NaN
(⟨prefix⟩-remainder x ±inf.0) x is finite x
(⟨prefix⟩-quotient x ±inf.0) x is finite (flcopysign 0.0 x)
(srfi 276 ⟨library⟩)
procedure
(fltruncate-quotient fl1 fl2)
(fltruncate-remainder fl1 fl2)

Let q = truncate(fl1/fl2) and let r be a flonum such that fl1 = qfl2 + r.

Then fltruncate-quotient returns q and fltruncate-remainder returns r. The remainder is always correctly rounded.

Rationale: These procedures are renamed from flquotient and flremainder in SRFI 144.

(srfi 276 ⟨library⟩)
procedure
(flremquo fl1 fl2)

Let q = roundtoeven(fl1/fl2) and let r be a flonum such that fl1 = qfl2 + r.

This procedure returns two values: a correctly signed integer whose magnitude is congruent to the magnitude of q modulo k, where k ≥ 3, and the correctly rounded remainder.

(srfi 276 ⟨library⟩)
procedure
(flround-quotient fl1 fl2)
(flround-remainder fl1 fl2)

Let q = roundtoeven(fl1/fl2) and let r be a flonum such that fl1 = qfl2 + r.

Then flround-quotient returns q and flround-remainder returns r. The remainder is always correctly rounded.

Exponentials

(srfi 276 ⟨library⟩)
procedure
(flexp fl)
(flexp2 fl)
(flexp10 fl)

Returns afl, where a is respectively the mathematical constant e, 2, or 10.

If the argument is positive infinity, the result is positive infinity. If the argument is negative infinity, the result is positive 1.

(srfi 276 ⟨library⟩)
procedure
(flexp-1 fl)
(flexp2-1 fl)
(flexp10-1 fl)

Returns afl − 1, where a is respectively the mathematical constant e, 2, or 10. It is expected that this will be more accurate than (fl- (flexp fl) 1.0) etc when fl is small.

If the argument is positive infinity, the result is positive infinity. If the argument is negative infinity, the result is positive 0.

(srfi 276 ⟨library⟩)
procedure
(flexpt fl1 fl2)

Returns fl1fl2. The following special cases apply:

OperationSide conditionsExceptionsReturn
(flexpt x ±0.0) x is not NaN 1.0
(flexpt ±0.0 y) y < 0, y odd divideByZero ±inf.0
(flexpt ±0.0 −inf.0) +inf.0
(flexpt ±0.0 +inf.0) +0.0
(flexpt ±0.0 y) y > 0, y odd ±inf.0
(flexpt −1.0 ±inf.0) 1.0
(flexpt +1.0 y) y is not NaN 1.0
(flexpt x +inf.0) −1 < x < 1 +0.0
(flexpt x +inf.0) x < −1 or for 1 < x +inf.0
(flexpt x −inf.0) −1 < x < 1 +inf.0
(flexpt x +inf.0) x < −1 or for 1 < x +0.0
(flexpt +inf.0 y) y < 0 +0.0
(flexpt +inf.0 y) y > 0 +inf.0
(flexpt −inf.0 y) y < 0, y odd -0.0
(flexpt −inf.0 y) y > 0, y odd -inf.0
(flexpt −inf.0 y) y < 0, y finite, not odd +0.0
(flexpt −inf.0 y) y > 0, y finite, not odd +inf.0
(flexpt ±0.0 y) y < 0 and y finite, not odd divideByZero +inf.0
(flexpt ±0.0 y) y > 0, y finite and not odd +0.0
(flexpt x, y) finite x < 0, finite non-integer y invalid operation NaN

Let nan be a quiet NaN. It is unspecified if (flexpt nan ±0.0) returns 1.0 or a NaN. It is unspecified if (flexpt 1.0 nan) returns 1.0 or a NaN.

Rationale: IEEE 754 mandates that (flexpt nan ±0.0) and (flexpt 1.0 nan) returns 1.0. This rule allows for the usual Scheme rule, where an inexact operation on NaNs returns NaN, while also allowing for an implementation to FFI into an IEEE 754 math library, which provides this rule.

Logarithms

(srfi 276 ⟨library⟩)
procedure
(fllog fl)
(fllog2 fl)
(fllog10 fl)

Returns loga(fl), where a is respectively the mathematical constant e, 2, or 10.

If the argument is positive infinity, the result is positive infinity. If the argument is negative 1, the result is negative infinity, and a divide-by-zero exception is raised. If the argument is less than negative 1, NaN is returned, and an invalid exception is raised.

(srfi 276 ⟨library⟩)
procedure
(fllog+1 fl)
(fllog2+1 fl)
(fllog10+1 fl)

Returns loga(fl + 1), where a is respectively the mathematical constant e, 2, or 10. It is expected that this will be more accurate than (fllog (fl+ 1.0 fl)) etc when fl is small.

If the argument is positive infinity, the result is positive infinity. If the argument is zero, the result is negative infinity, and a divide-by-zero exception is raised. If the argument is negative, NaN is returned, and an invalid exception is raised.

Note: The procedure fllog+1 is renamed from the procedure fllog1+ in SRFI 144.

(srfi 276 ⟨library⟩)
procedure
(make-fllog-base fl)

Returns a procedure that computes the base fl logarithm of its input, with similar rules applied to infinities, NaNs, and negative arguments as fllog. It is an error if fl is less than 1.0.

Powers and roots

(srfi 276 ⟨library⟩)
procedure
(flcbrt fl)

Calculate fl1/3. When passed a zero or an infinity, returns the argument.

(srfi 276 ⟨library⟩)
procedure
(flcompound fl n)

Calculate (1 + fl)n. It is expected that this function is more accurate than (flexpt (fl+ 1.0 fl) n) when fl is small. The following special cases apply:

OperationSide conditionsExceptionsReturn
(flcompound x 0.0) x ≥ -1 1.0
(flcompound x n) x < -1, n ≠ 0 invalidOperation NaN
(flcompound -1.0 n) n < 0 divideByZero +inf.0
(flcompound -1.0 n) n > 0 +0.0
(flcompound +inf.0 n) n > 0 +inf.0
(flcompound +inf.0 n) n < 0 +0.0

Let nan be a quiet NaN. It is unspecified if (flcompound nan ±0.0) returns 1.0 or NaN.

(srfi 276 ⟨library⟩)
procedure
(flhypot fl1 fl2)

Calculate √(fl12 + fl22).

If one argument is zero, the returned value is the absolute value of the other argument.

If one argument is infinity, then the returned value is infinity, even if another value is NaN.

(srfi 276 ⟨library⟩)
procedure
(flrsqrt fl)

Calculates 1/√fl. When passed zero, returns the appropriately signed infinity and raises the divideByZero floating point exception. When passed positive infinity, returns positive zero. When passed a negative number, returns a NaN and raises the invalidOperation floating point exception.

Note: Reciprocal square root is available in many CPUs as a hardware operation.

(srfi 276 ⟨library⟩)
procedure
(flsqrt fl)

Returns √fl. This function always returns a correctly rounded result. When passed a zero, returns that zero. When passed positive infinity, returns positive infinity. When passed a negative number, returns NaN and raises the invalidOperation floating-point exception.

Trigonometric functions

(srfi 276 ⟨library⟩)
procedure
(flsin fl)
(flcos fl)
(fltan fl)

Return the respective value at that point for the trigonometric function, with the argument measured in radians.

When an infinity is passed to any of these functions, NaN is returned and the invalidOperation floating point exception is raised.

When sin or tan are given zero, return that zero.

(srfi 276 ⟨library⟩)
procedure
(flsinpi fl)

Return sin(πfl).

When given a zero, return that zero. When given a positive integer, return positive zero. When given a negative integer, return negative zero.

When an infinity is passed to this function, NaN is returned and the invalidOperation floating point exception is raised.

(srfi 276 ⟨library⟩)
procedure
(flcospi fl)

Return cos(πfl).

Let fl = n + ½, where n is an integer. Then this function returns positive zero.

When an infinity is passed to this function, NaN is returned and the invalidOperation floating point exception is raised.

(srfi 276 ⟨library⟩)
procedure
(fltanpi fl)

Return the value of tan(πfl). The following special cases apply:

OperationSide conditionsExceptionsReturn
(fltanpi ±0.0) ±0.0
(fltanpi n) n is positive and even, or negative and odd +0.0
(fltanpi n) n is positive and odd, or negative and even +0.0
(fltanpi x) x = n + 1/2 for even n divideByZero +inf.0
(fltanpi x) x = n + 1/2 for odd n divideByZero +inf.0

Inverse trigonometric functions

(srfi 276 ⟨library⟩)
procedure
(flasin fl)
(flasinpi fl)

Compute the arcsine of the argument. When given a zero, return that zero. When given a value whose absolute value is greater than 1, return a NaN and raise the invalidOperation floating point exception.

The function flasinpi is defined as asin(fl)/π.

(srfi 276 ⟨library⟩)
procedure
(flacos fl)
(flacospi fl)

Compute the arccosine of the argument. When given a zero, positive one. When given a value whose absolute value is greater than 1, return a NaN and raise the invalidOperation floating point exception.

The function flacospi is defined as acos(fl)/π.

(srfi 276 ⟨library⟩)
procedure
(flatan [fl1] fl1)
(flatanpi [fl1] fl1)

When given one argument, compute the arctan of the argument. At infinity, return the closest approximation to ±π/2.

When given two arguments, compute the atan2 function. When one of the arguments is a zero, returns the appropriate value from the atan table in the R7RS. The following special cases are added to the table:

OperationSide conditionsReturn
(flatan y -inf.0) finite nonzero y (flcopysign π/2 y)
(flatan y +inf.0) finite nonzero y (flcopysign 0.0 y)
(flatan ±inf.0 x) finite x (flcopysign π/2 ±inf.0)
(flatan ±inf.0 -inf.0) (flcopysign 3π/4 ±inf.0)
(flatan ±inf.0 +inf.0) (flcopysign π/4 ±inf.0)

The function flatanpi is the same as flatan except that the output of atan is divided by π.

Hyperbolic functions

(srfi 276 ⟨library⟩)
procedure
(flsinh fl)

Return sinh(fl).

When passed zero, return that zero. When passed infinity, return that infinity.

(srfi 276 ⟨library⟩)
procedure
(flcosh fl)

Return cosh(fl).

When passed zero, return 1. When passed infinity, return positive infinity.

(srfi 276 ⟨library⟩)
procedure
(fltanh fl)

Return tanh(fl).

When passed zero, return that zero. When passed infinity, return (flcopysign 1.0 fl).

(srfi 276 ⟨library⟩)
procedure
(flasinh fl)

Return asinh(fl).

When passed zero, return that zero. When passed infinity, return that infinity.

(srfi 276 ⟨library⟩)
procedure
(flacosh fl)

Return acosh(fl).

When passed 1, return positive zero. When passed positive infinity, return positive infinity.

When passed a value less than 1, return a NaN and raise the invalidOperation floating-point exception.

(srfi 276 ⟨library⟩)
procedure
(flatanh fl)

Return acosh(fl). The following special cases apply:

OperationSide conditionsExceptionsReturn
(atanh ±0.0) ±0.0
(atanh ±1.0) divideByZero ±inf.0
(atanh x) |x| > 1 invalidOperation NaN

Special functions

(srfi 276 ⟨library⟩)
procedure
(flerf fl)

Calculate

erf ( x ) = 2 π 0 1 e x2 d x

The following special cases apply:

OperationSide conditionsReturn
(flerf ±0.0) ±0.0
(flerf ±inf.0) ±1.0
(srfi 276 ⟨library⟩)
procedure
(flerfc fl)

Calculate 1 − erf(fl). The following special cases apply:

OperationSide conditionsReturn
(flerfc -inf.0) +2.0
(flerf +inf.0) +0.0
(srfi 276 ⟨library⟩)
procedure
(flgamma fl)

Computes

Γ ( x ) = 0 t z 1 e t dt

The following special cases apply:

OperationSide conditionsExceptionsReturn
(flgamma ±0.0) divideByZero ±inf.0
(flgamma x) x is a negative and an integer or infinity invalidOperation NaN
(flgamma +inf.0) +inf.0
(srfi 276 ⟨library⟩)
procedure
(fllog-gamma fl)

Returns two values: loge|Γ(fl)| and the sign of Γ(fl) times 1.0. The following special cases apply:

OperationSide conditionsExceptionsReturn
(fllog-gamma 1.0) 0.0
(fllog-gamma 2.0) 0.0
(fllog-gamma x) x is a negative integer or 0 invalidOperation NaN
(fllog-gamma ±inf.0) +inf.0
(srfi 276 ⟨library⟩)
procedure
(flfirst-bessel fl n)
(flsecond-bessel fl n)

Calculate the nth bessel function of the first/second kind, respectively.

OperationSide conditionsExceptionsReturn
(flfirst-bessel +inf.0 n) invalidOperation NaN
(flsecond-bessel +inf.0 n) invalidOperation NaN
(flsecond-bessel x n) x is zero divideByZero -inf.0
(flsecond-bessel x n) x is negative invalidOperation NaN

Implementation query

(srfi 276 ⟨library⟩)
procedure
(flfeatures)

Returns a list containing information about the floating-point operations in this library. The following symbols have defined meanings. An implementation may add other features, which should be symbols.

subnormals-are-zero
Subnormal numbers are treated as zero. (This is sometimes called “DAZ,” or “denormals are zero” mode, for historical reasons.)
flush-to-zero
An operation that would underflow and create a subnormal number instead creates a zero. (This is sometimes called “FTZ.”)
ieee-754-2019
Arithmetic compiles with IEEE 754. In particular, the operations that IEEE 754 requires to be correctly rounded are correctly rounded. Must not appear when subnormals-are-zero or flush-to-zero appear.
non-stop
Arithmetic is non-stop (see above).
fast-fma
The function (fl+* x y z) is at least as fast as or faster than (fl+ (fl* x y) z). (Fused multiply-add must be rounded correctly when IEEE 754 compliance mode is on, regardless of whether fast-fma is available.)
⟨name⟩-correctly-rounded where ⟨name⟩ is a procedure from the library
The function ⟨name⟩ is always correctly rounded. (When ieee-754-2019 appears, then features corresponding to functions the IEEE 754 be correctly rounded must not appear.)

Note: DAZ/FTZ modes are usually enabled by the compiler, or are baked-in features of the architecture. As such, this SRFI does not provide a portable way to manipulate this mode.

This should not be confused with the features procedure in the R7RS. This is a run-time procedure that reports on the run-time environment, and the flags may change over the runtime of the program. These flags are not accessible through cond-expand.

Serialization and deserialization

(srfi 276 ⟨library⟩)
procedure
(bytevector-flonum-ref bv k [endianness])

It is an error if k to k + flbyte-width are not valid indices of bv. If endianness is not supplied, it is an error if k is not a multiple of flbyte-width.

Read the bytes in bv at k as a flonum of this type, with the endianness.

If the value is a NaN, then the NaN should not be coerced into another NaN.

(import (scheme base) (prefix (srfi 276 binary64) f64:))

(define bv (make-bytevector f64:flbyte-width))
(bytevector-u8-set! bv 0 #b01000000)
(bytevector-u8-set! bv 1 #b00001001)
(bytevector-u8-set! bv 2 #b00100001)
(bytevector-u8-set! bv 3 #b11111011)
(bytevector-u8-set! bv 4 #b01010100)
(bytevector-u8-set! bv 5 #b01000100)
(bytevector-u8-set! bv 6 #b00101101)
(bytevector-u8-set! bv 7 #b00011000)
(f64:bytevector-flonum-ref bv 0 'big) ⇒ #fl(binary64 3.141592653589793116)

Rationale: Some implementations, in particular those that use NaN boxing, may only be able to represent a limited set of NaNs. Different systems may have different canonical NaNs. For these reasons portable code should not expect that different NaNs are distinguishable.

(srfi 276 ⟨library⟩)
procedure
(bytevector-flonum-set! bv k fl [endianness])

It is an error if k to k + byte-width are not valid indices of bv. If endianness is not supplied, it is an error if k is not a multiple of byte-width.

Write fl to bv at k with endianness.

This procedure and bytevector-flonum-ref must to round-trip on all non-NaNs. That is, given a non-NaN flonum fl,


  (let ((bv (make-bytevector flbyte-width)))
    (bytevector-flonum-set! bv 0 fl)
    (eqv? fl (bytevector-flonum-ref bv 0)))

always evaluates to #t. These procedures should round-trip NaNs.

(import (scheme base) (prefix (srfi 276 binary32) f32))

(define bv (make-bytevector f32:flbyte-width))
(f32:bytevector-flonum-set! bv 0 #fl(binary32 1.41421353816986083984) 'little)
bv⇒ #u8(#xf3 #x04 #xb5 #x3f)
(srfi 276 ⟨library⟩)
procedure
(string->flonum string [radix])

It is an error if radix is not 2, 8, 10, or 16. The value of radix defaults to 10.

Read string as a number in that representation. This procedure must round-trip with number->string.

(import (scheme base)
        (prefix (srfi 276 binary64) f64)
        (prefix (srfi 276 binary128) f128))
(f128:string->flonum "1e400") ⇒ #fl(binary128 1e400)
(f64:string->flonum "1e400") ⇒ #fl(binary64 +inf.0)
(let ((v #fl(binary128 1e400)))
  (eqv? (f128:string->flonum
         (number->string v))
        v)) ⇒ #t

Note: There is no flonum->string procedure, as number->string is already polymorphic.

Rounding mode libraries

(srfi 276 rounding-mode)
procedure
(current-rounding-mode)

Returns the current rounding mode. This SRFI defines the following symbols which can be returned from this procedure. An implementation may add other rounding modes, which should be symbols. For example, an implementation with support for GNU MPFR may add MPFR's additional rounding modes.

round-to-nearest/ties-to-even
Operations are rounded to the nearest representable value, with ties broken by returning the value with an even least significant digit. For representations where that is ambiguous, the returned value is the larger of the tie in magnitude. (IEEE 754 roundTiesToEven)
round-to-nearest/ties-to-away
Operations are rounded to the nearest representable value, with ties broken by returning the tie value with the largest magnitude. (roundTiesToAway)
round-towards-positive
Operations are rounded to the closest representable value not less than the infinitely precise value. (IEEE 754 roundTowardsPositive)
round-towards-negative
Operations are rounded to the closest representable value not greater than the infinitely precise value. (IEEE 754 roundTowardsNegative)
round-towards-zero
Operations are rounded to the closest representable value not greater than in magnitude the infinitely precise value. (IEEE 754 roundTowardsZero)

Note: The rounding mode is independent of the behavior of the integer rounding functions and the integer division functions.

(srfi 276 rounding-mode)
procedure
(rounding-mode? obj)

Returns #t if obj is a valid rounding mode, and #f otherwise.

(srfi 276 with-rounding-mode)
syntax
(with-rounding-mode rounding-mode body …)

Evaluate rounding-mode to a rounding mode. Then evaluate body … with that rounding mode in its dynamic extent. If the dynamic extent is exited, the rounding mode is restored to what it was previously. If the dynamic extent is re-entered, the rounding mode is restored back to the value that rounding-mode was evaluated to.

As an example, here is how a simplified form of fltruncate-quotient could be implemented:

(define (fltruncate-quotient fl1 fl2)
  (let ((trial (fl/ fl1 fl2)))
    (if (flinfinite? trial)
        trial
        (with-rounding-mode 'round-towards-zero
          (fltruncate (fl/ fl1 fl2))))))

Rationale: If the operations in the dynamic extent are just procedures from this SRFI, then instead of setting the global rounding mode, then implementations on certain processors like RISC-V can compile the functions to instructions with specific rounding modes. See Zurstraßen 2023.

(srfi 276 set-rounding-mode)
procedure
(set-rounding-mode! rounding-mode)

Sets the global rounding mode to rounding-mode. On implementations with multithreading, the rounding mode should be specific to a thread and not to the whole program.

For example, with-rounding-mode could be implemented as

(define-syntax with-rounding-mode
  (syntax-rules ()
    ((_ rounding-mode body1 body2 ...)
     (let ((r rounding-mode)
           (p (flrounding-mode)))
       (dynamic-wind
        (lambda () (set-rounding-mode! r))
        (lambda () body1 body2 ...)
        (lambda () (set-rounding-mode! p)))))))

Floating-point exception library

(srfi 276 exceptions)
procedure
(flexception-name? obj)

Returns #t if the object is a floating point exception, and #f otherwise. The following are the standard floating point exceptions:

invalid-operation
Raised if an operation had no useful result in the extended real number line.
divide-by-zero
Raised if an operation that produces an infinite result from finite operands.
overflow
Raised if an operation would return a finite number, but it is larger in magnitude than any flonum.
underflow
Raised if an operation returned an operation that was non-zero, lower in magnitude than the smallest normal flonum.
inexact
Raised if an operation returned a rounded result that is different from the exact result.

An implementation may add more exceptions, which should be symbols.

(srfi 276 exceptions)
procedure
(current-flexceptions)

Returns an immutable list of the current raised exceptions.

(srfi 276 exceptions)
procedure
(possible-flexceptions)

Returns an immutable list of the exceptions that could possibly be raised.

(srfi 276 exceptions)
procedure
(flexception-raised? exception)

Returns #t if exception was raised in the current exception environment.

(srfi 276 exceptions)
procedure
(raise-flexceptions! exception …)

Raise each exception.

(srfi 276 exceptions)
procedure
(lower-flexceptions! exception …)

Lower each exception.

(srfi 276 exceptions)
syntax
(with-flexceptions exception-list body1 body2 …)

Evaluate exception-list to a list of exceptions. The body is evaluated in an environment that starts out with the exceptions that were in the list of exceptions. Raising and lowering exceptions affects the exceptions in the dynamic extent, and not the exceptions anywhere else.

Floating point environment library

An implementation may add more objects to the floating point environment. For example, it may add a toggle to signal Scheme exceptions when a floating-point exception is raised. However, it must preserve the property that flenvironment=? decides the equivalence of environments.

(srfi 276 environment)
procedure
(current-flenvironment)

Returns the current floating point environment. The floating point environment encompasses all modifications possible at runtime.

(srfi 276 environment)
procedure
(flenvironment? obj)

Returns #t if obj is an environment, and #f otherwise.

(srfi 276 environment)
procedure
(flenvironment=? flenvironment1 flenvironment2 flenvironment3 …)

Returns #t if all flenvironments are equal. Two environments are equal if, barring Scheme exceptions being signalled, two procedures in this SRFI given the same arguments would return the same results.

(srfi 276 environment)
procedure
(flenvironment-rounding-mode flenvironment)

Returns the rounding mode in the flenvironment.

(srfi 276 environment)
procedure
(flenvironment-raised-exceptions flenvironment)

Returns an immutable list of raised exceptions in the flenvironment.

(srfi 276 environment)
procedure
(make-flenvironment rounding-mode exception-list)

It is an error if exception-list is not a list of exception names.

Constructs an environment with the rounding-mode, and with the exceptions in exception-list raised.

(srfi 276 environment)
procedure
(default-flenvironment)

Return the default environment. At runtime this is an implementation-specified environment, and it may be modified by with-flenvironment.

(srfi 276 environment)
procedure
(restore-flenvironment! flenvironment)

Set flenvironment as the current floating-point environment.

(srfi 276 environment)
syntax
(with-flenvironment flenvironment body1 body2 …)

Evaluate flenvironment to a floating point environment. When the dynamic extent of the body is first entered, the floating point environment is flenvironment. In addition, throughout the extent of the floating-point environment, the default environment is flenvironment.

Implications of IEEE arithmetic for optimizers

When an implementation advertises that it implements, e.g. flsqrt with one rounding, then it must not reorder or optimize the program if it would return a different result. For example, (fl/ 1.0 (flsqrt x)) may return a different result if implemented as two operations literally, versus as one inverse square root operation. Implementations may offer modes that optimize mathematical operations at the expense of reproducibility.

Given the same rounding mode, input values, with ieee-754-2019 and non-stop as features, any set of operations that are correctly rounded will produce the same answers on one correctly conforming implementation as on another with the same rounding mode, input values, and features implicating correct rounding.

Examples

This section is non-normative.

(import (scheme base)
        (prefix (srfi 276 binary32) f32)
        (srfi 151)
        (rnrs bytevectors) ; for bytevector serialization operations
)

(unless (member 'ieee-754-2019 (f32:flfeatures))
  (error "requires IEEE 754 arithmetic"))

(define (f32:kahan-sum lst)
  (do ((sum #fl(binary32 0.0))
       (c #fl(binary32 0.0))
       (lst lst (cdr lst)))
      ((null? lst) sum)
    (let* ((y (fl32:fl- (car lst) c))
           (t (fl32:fl+ sum y)))
      (set! c (fl32:fl- (fl32:fl- t sum) y))
      (set! sum t))))

(define (f32:fast-inverse-square-root number)
  (let ((bv (make-bytevector f32:flbyte-width))
        (x2 (f32:fl* number #fl(binary32 0.5))))
    (f32:bytevector-flonum-set! bv 0 number)
    (let* ((i (bytevector-u32-native-ref bv 0))
           (i (- #x5f3759df (arithmetic-shift i -1))))
      (bytevector-u32-native-set! bv 0)
      (let ((y (f32:bytevector-flonum-ref bv 0)))
        (f32:fl* y (f32:fl- #fl(binary32 1.5) (f32:fl* x2 y y)))))))

This code will always calculate the correct results with the desired algorithmic properties on any conforming implementation that implements the binary32 format and follows the syntax recommendations. In particular, a conforming implementation will not re-order operations in such a way to make the output values differ.

Considerations for inexact number vectors

SRFI 4 specifies f32vectors and f64vectors, and SRFI 160 specifies c64vectors and c128vectors. Implementors should make the elements of each vector the corresponding representation in the table. If the corresponding cond-expand feature is available, then the elements of the number vector must be that type.

VectorRepresentationcond-expand feature
f32vectorbinary32f32vector-is-binary32
f64vectorbinary64f64vector-is-binary64
c64vector each part is binary32c64vector-is-binary32
c128vectoreach part is binary64c128vector-is-binary64

Reader syntax suggestions

On implementations with binary floating point of the corresponding precisions, the exponent specifiers in the table should map to the corresponding representation:

ExponentRepresentation
sbinary16
fbinary32
dbinary64

Because there is not a lot of hardware with binary128 support, this SRFI makes no recommendation for the l exponent. Some formats that could be used for l include binary128, x87 long double, and so-called “double-double” arithmetic (see Dekker 1971 and Joldes, Muller, and Popescu 2017).

Implementation

An implementation should use the floating-point operations available in hardware as much as possible. Most implementations only have one floating-point type (binary64), and those implementations can copy most of their SRFI 144 implementation to (srfi 276 binary64) with minor renamings.

The simplest way to implement the inspection portion of this SRFI is an FFI to C’s fenv.h. Checking the FTZ/DAZ mode (for example, on Intel CPUs) requires intrinsics to check the MXCSR register.

Although it is possible to implement bytevector-flonum-ref and bytevector-flonum-set! in terms of flnormalized-fraction-exponent and make-flonum, it is much easier to manipulate the byte representation of the flonum directly.

A sample implementation will be provided that wraps MIT Scheme’s floating-point environment API.

Implementations that implement SRFI 276 are encouraged to also export SRFI 144 as a compatability library.

Acknowledgements

Thanks to those in Working Group 2 for discussing the semantics of this SRFI. In particular, I would like to thank Zhu Zihao for lots of information gathering.

I thank Bradley Lucier for his input.

I thank the authors of SRFI 144, as this work builds on theirs.

I also thank William Kahan, whose work on IEEE 754 and his many complaints about how programming language designers fail to understand it influenced the design of this SRFI (even if I could not incorporate all of his suggestions).

Appendix 1: Relationship between this SRFI and other standards

This section is non-normative.

This table documents procedures and constants in this SRFI that are based off of functions and constants in C23 and IEEE 754. The section and paragraph number of each function/constant in the latest C23 draft (see Meneide 2024) is given.

SRFIC23IEEE 754
fl-radix FLT_RADIX (5.2.5.3.3 ¶ 31) b
fl-precision FLT_MANT_DIG (5.2.5.3.3 ¶ 31) p
fl-maximum-exponent FLT_MAX_EXP (5.2.5.3.3 ¶ 31) emax
fl-minimum-normalized-exponent FLT_MIN_EXP (5.2.5.3.3 ¶ 31) emin
fl-greatest FLT_MAX (5.2.5.3.3 ¶ 32)
fl-least FLT_TRUE_MIN (5.2.5.3.3 ¶ 33)
fl-least-normal FLT_MIN (5.2.5.3.3 ¶ 33)
fl-epsilon FLT_EPSILON (5.2.5.3.3 ¶ 33)
fl-byte-width sizeof float
fladjacent nextafter (7.12.11.3) nextUp, nextDown
flcopysign copysign (7.12.11.1)
make-flonum scalbn (7.12.7.19) scaleB
flinteger-fraction modf (7.12.6.18)
flexponent logb (7.12.6.17) logB
flnormalized-fraction-exponent frexp (7.12.6.7)
flinteger-exponent ilogb (7.12.6.8) logB
fl-integer-exponent-zero FP_ILOGB0 (7.12 ¶ 18)
fl-integer-exponent-nan FP_ILOGBNAN (7.12 ¶ 18)
flsign-negative? signbit (7.12.3.7) isSignMinus
fl=? = (F.9.4) compareQuietEqual
fl<? fl<=?, etc. <, <=, etc. (F.9.4) compareSignalingGreater,
compareSignalingGreaterEqual,
etc.
fl!=? != (F.9.4) compareQuietNotEqual
total=?, etc. totalorder (F.10.12.2) totalOrder
flunordered? isunordered (7.12.17.6) compareQuietUnordered
flzero? iszero (7.12.3.10) isZero
flfinite? isfinite (7.12.3.3) isFinite
flinfinite? isinf (7.12.3.4) isInfinite
flnan? isnan (7.12.3.5) isNaN
flnormal? isnormal (7.12.3.6) isNormal
flsubnormal? issubnormal (7.12.3.9) isSubnormal
flmax, flmin fmaximum,
fminimum
(7.12.12.4, F.10.9.4)
maximum,
minimum
max-abs, min-abs fmaximum_mag,
fminimum_mag
(7.12.2.8)
maximumMagnitude,
minimumMagnitude
max-filter-nans,
min-filter-nans
fmaximum_num,
fminimum_num
(7.12.12.5, F.10.9.5)
maximumNumber,
minimumNumber
max-abs-filter-nans,
min-abs-filter-nans
fmaximum_num_mag,
fminimum_num_mag
(7.12.12.7)
maximumMagnitudeNumber,
minimumMagnitudeNumber
fl+ + (6.5.7) addition
fl- - (6.5.7) subtraction, negate
fl* * (6.5.6) multiplication
fl/ / (6.5.6) multiplication
fl+* fma (7.12.13.1) fusedMultiplyAdd
flabs fabs (7.12.7.3) abs
flposdiff fdim (7.12.12.1)
flfloor floor (7.12.9.2) roundToIntegralTowardNegative
flceiling ceil (7.12.9.1) roundToIntegralTowardPositive
fltruncate trunc (7.12.9.9) roundToIntegralTowardZero
flround roundeven (7.12.9.8) roundToIntegralTiesToEven
flround-away round (7.12.9.6) roundToIntegralTiesToAway
fltruncate-remainder fmod (7.12.10.1)
flremquo remquo (7.12.10.3)
flround-remainder remainder (7.12.10.2) remainder
flexp exp (7.12.6.1) exp
flexp2 exp2 (7.12.6.4) exp2
flexp10 exp10 (7.12.6.2) exp10
flexp-1 expm1 (7.12.6.6) expm1
flexp2-1 exp2m1 (7.12.6.5) exp2m1
flexp10-1 exp10m1 (7.12.6.3) exp10m1
flexpt pown (7.12.7.6),
pow (7.12.7.5)
pown, pow
fllog log (7.12.6.11) log
fllog2 log2 (7.12.6.15) log2
fllog10 log10 (7.12.6.12) log10
fllog+1 logp1 (7.12.6.14) logp1
fllog2+1 log2p1 (7.12.6.16) log2p1
fllog10+1 log10p1 (7.12.6.13) log10p1
flcbrt cbrt (7.12.7.1)
flcompound compoundn (7.12.7.2) compound
flhypot hypot (7.12.7.4) hypot
flrsqrt rsqrt (7.12.7.9) rSqrt
flsqrt sqrt (7.12.7.10) squareRoot
flsin sin (7.12.4.6) sin
flcos cos (7.12.4.5) cos
fltan tan (7.12.4.7) tan
flsinpi sinpi (7.12.4.6) sinPi
flcospi cospi (7.12.4.5) cosPi
fltanpi tanpi (7.12.4.7) tanPi
flasin asin (7.12.4.2) asin
flacos acos (7.12.4.1) acos
flatan atan (7.12.4.3) atan
flasinpi asinpi (7.12.4.9) asinPi
flacospi acospi (7.12.4.8) acosPi
flatanpi atanpi (7.12.4.10) atanPi
flsinh sinh (7.12.5.5) sinh
flcosh cosh (7.12.4.4) cosh
fltanh tanh (7.12.4.6) tanh
flasinh asinh (7.12.5.2) asinh
flacosh acosh (7.12.5.1) acosh
flatanh atanh (7.12.5.3) atanh
flerf erf (7.12.8.1)
flerfc erfc (7.12.8.2)
flgamma tgamma (7.12.8.4)
fllog-gamma lgamma (7.12.8.3)
current-rounding-mode fegetmode (7.6.5.1) saveModes
set-rounding-mode! fesetmode (7.6.5.4) restoreModes
current-flexceptions,
flexception-raised?
fetestexcept (7.6.4.7) testFlags
raise-flexceptions! fesetexcept (7.6.4.4) raiseFlags
lower-flexceptions! feclearexcept (7.6.4.1) lowerFlags

In addition, some procedures and constants come from POSIX:

SRFIPOSIX
fl-eM_E
fl-log2-eM_LOG2E
fl-log10-eM_LOG10E
fl-log-2M_LN2
fl-log10M_LN10
fl-piM_PI
fl-pi/2M_PI_2
fl-pi/4M_PI_4
fl-1/piM_1_PI
fl-2/piM_2_PI
fl-2/sqrt-piM_2_SQRTPI
fl-sqrt-2M_SQRT2
fl-1/sqrt-2M_SQRT1_2
flfirst-bessel j0, j1, jn
flsecond-bessel y0, y1, yn
fllog-gamma lgamma, signgam

Bibliography

  1. Taylor Campbell. 2014. Uniform random floats: How to generate a double-precision floating-point number in [0, 1] uniformly at random given a uniform random source of bits. Retrieved from https://mumble.net/~campbell/2014/04/28/uniform-random-float on 2026-06-20.
  2. T. J. Dekker. 1971. A Floating-Point Technique for Extending the Available Precision. Numer. Math, 18, 224-242. doi:10.1007/BF01397083.
  3. Laurent Fousse et al. 2007. MPFR: A multiple-precision binary floating-point library with correct rounding. ACM Trans. Math. Softw., 33, 2. doi:10.1145/1236463.1236468. The version referenced in this SRFI is 4.2.2.
  4. Frédéric Goualard. 2022 Drawing random floating-point numbers from an interval. ACM Transactions on Modeling and Computer Simulation, 32 (3). hal-03282794v2
  5. John Gustafson et al. 2022. Standard for Posit Arithmetic. Retrieved from https://posithub.org/docs/posit_standard-2.pdf on 2026-06-20.
  6. IEEE Computer Society. 2019. IEEE Standard for Floating-Point Arithmetic (IEEE STD 754-2019). doi:10.1109/IEEESTD.2019.8766229. ISBN 978-1-5044-5924-2.
  7. ISO. ISO/IEC 24747:2009 — Extensions to the C Library to support mathematical special functions. 2009. Latest publically available draft N1292.
  8. Jean Heyd Meneide and Freek Wiedijk (editors). Information technology — Programming languages — C. ISO/IEC 9899:2024. International Standards Organization. Latest publically available draft N3220.pdf.
  9. Mioara Joldes, Jean-Michel Muller, Valentina Popescu. 2017. Tight and rigorous error bounds for basic building blocks of double-word arithmetic. ACM Trans. Math. Soft., 44, 2, 15. doi:10.1145/3121432. hal-01351529v3.
  10. William Kahan. 1997. Lecture Notes on the Status of IEEE Standard 754 for Binary Floating-Point Arithmetic. Retrieved from https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF on 2026-06-20.
  11. William Kahan and Joseph Darcy. 1998. How Java’s Floating-Point Hurts Everyone. Retrieved from https://people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf on 2026-06-20.
  12. Massachusetts Institute of Technology. 2022. Fixnum and Flonum Operations in MIT/GNU Scheme. Retrieved from https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/Fixnum-and-Flonum-Operations.html on 2026-06-20.
  13. Scipy. Special functions (1.18.0). Retrieved from https://docs.scipy.org/doc/scipy-1.18.0/reference/special.html on 2026-08-04.
  14. Niko Zurstraßen. 2023. Evaluation of the RISC-V Floating Point Extensions F/D. Retrieved from https://www.chciken.com/risc-v/2023/08/06/evaluation-riscv-fd.html on 2026-06-20.

© 2026 Peter McGoron.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: Arthur A. Gleckler