Skip to content

ACP: Add floating point representation conversions #501

Open
@tczajka

Description

Proposal

Problem statement

There is currently no easy way to:

  • get an exponent and mantissa from a floating point number
  • construct a floating point number from an exponent and mantissa

Even creating a power of 2 in floating point is not easy even though it is a reasonably useful operation that can be computed exactly. powi can't be used because it has unspecified precision.

The only way is to directly deal with the internal bit representation and use to_bits and from_bits to convert.

Motivating examples or use cases

This would essentially be a replacement for f32::classify that keeps all the information about the number. f32::classify can be implemented in terms of to_repr.

Converting floating point to and from bignums (e.g. UBig::to_f32) could use this.

Implementing f32::div_euclid in std using integral division could use this.

Users could use this to inspect their floating point numbers, understand their behavior, debug their code, etc.

Solution sketch

pub enum FpRepresentation<Mantissa> {
    // number = 2^exponent * mantissa
    Finite {
        exponent: i32,
        mantissa: Mantissa,
        // This is redundant with the sign of `mantissa` except for negative zero
        sign: Sign,
    },
    Infinity { sign: Sign },
    Nan { nan_type: NanType, payload: Mantissa,  sign: Sign },
}

pub enum Sign {
    Positive,
    Negative,
}

pub enum NanType {
    Signaling,
    Quiet,
}

impl f32 {
    pub fn to_repr(self) -> FpRepresentation<i32> { ... }

    // Rounds the representation to the nearest representable number.
    // This will ignore the `sign` field for `Finite` numbers except when `mantissa` is 0.
    pub fn from_repr(repr: FpRepresentation<i32>) -> Self { ... }

    // Returns `None` if the number cannot be represented exactly
    pub fn from_repr_exact(repr: FpRepresentation<i32>) -> Option<Self> { ... }
}

Alternatives

Existing API (to_bits, from_bits) can be used to have this implemented as an external crate. It seems like it belongs to core because it deals with the essence of how floating point numbers are represented.

impl From<f32> for FpRepresentation could be implemented instead of to_repr.

impl TryFrom<FpRepresentation<i32>> for f32 could be implemented instead of from_repr_exact.

An equivalent of C functions ldexp and frexp could be implemented instead. The advantages of the proposed solution over these:

  • Enums are clearer than what these functions do for special values (NaN, infinities)
  • frexp returns mantissa as a floating point number but you typically want to deal with it as an integer (otherwise why convert?), which requires another conversion step

For ldexp the extra conversion step (integer -> fp) is less of a problem because it might be necessary anyway when the mantissa has more bits than can be represented. For example if you want to convert a 64-bit mantissa + exponent into f32 you would have to first convert the 64-bit number into f32 either way.

There could be a separate enum variant for subnormal numbers. This is an unnecessary complication. Subnormal numbers can be distinguished with the proposed API by the fact that they have a small mantissa (mantissa.abs() < 1 << (MANTISSA_DIGITS - 1)) and often don't need separate logic.

Links and related work

internals.rust-lang.org thread

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions