Skip to content

Update documentation and add StatusAnd::unwrap() #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const SRC_LIB_RS_CONTENTS: &str = include_str!("src/lib.rs");
const EXPECTED_SRC_LIB_RS_PREFIX: &str = "\
//! Port of LLVM's APFloat software floating-point implementation from the
//! following C++ sources (please update commit hash when backporting):
//! https://github.com/llvm/llvm-project/commit/";
//! <https://github.com/llvm/llvm-project/commit/";

fn main() {
// HACK(eddyb) disable the default of re-running the build script on *any*
Expand All @@ -16,11 +16,14 @@ fn main() {
.ok_or(())
.map_err(|_| format!("expected `src/lib.rs` to start with:\n\n{EXPECTED_SRC_LIB_RS_PREFIX}"))
.and_then(|commit_hash_plus_rest_of_file| {
Ok(commit_hash_plus_rest_of_file
commit_hash_plus_rest_of_file
.split_once('\n')
.ok_or("expected `src/lib.rs` to have more than 3 lines")?)
.ok_or("expected `src/lib.rs` to have more than 3 lines")?
.0
.strip_suffix(">")
.ok_or("expected trailing hyperlink anchor `>`".into())
})
.and_then(|(commit_hash, _)| {
.and_then(|commit_hash| {
if commit_hash.len() != 40 || !commit_hash.chars().all(|c| matches!(c, '0'..='9'|'a'..='f')) {
Err(format!("expected `src/lib.rs` to have a valid commit hash, found {commit_hash:?}"))
} else {
Expand Down
73 changes: 55 additions & 18 deletions src/ieee.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Support for floating point types compatible with IEEE 754.

use crate::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO};
use crate::{Float, FloatConvert, ParseError, Round, Status, StatusAnd};

Expand All @@ -8,6 +10,12 @@ use core::marker::PhantomData;
use core::mem;
use core::ops::Neg;

/// A floating point number that uses IEEE semantics.
///
/// Usually you will want to use the available type aliases of this type
/// (e.g., [`Single`], [`Double`]) rather than referencing it directly.
///
/// If `S` implements [`Semantics`], this type will implement [`Float`].
#[must_use]
pub struct IeeeFloat<S> {
/// Absolute significand value (including the integer bit).
Expand Down Expand Up @@ -84,7 +92,7 @@ pub enum NonfiniteBehavior {
/// Only the Float8E5M2 has this behavior. There is no Inf representation. A
/// value is NaN if the exponent field and the mantissa field are all 1s.
/// This behavior matches the FP8 E4M3 type described in
/// https://arxiv.org/abs/2209.05433. We treat both signed and unsigned NaNs
/// <https://arxiv.org/abs/2209.05433>. We treat both signed and unsigned NaNs
/// as non-signalling, although the paper does not state whether the NaN
/// values are signalling or not.
NanOnly,
Expand Down Expand Up @@ -276,46 +284,75 @@ impl<S> Clone for IeeeFloat<S> {
}

macro_rules! ieee_semantics {
($($name:ident = $sem:ident($bits:tt : $exp_bits:tt) $({ $($extra:tt)* })?),* $(,)?) => {
$(pub struct $sem;)*
$(pub type $name = IeeeFloat<$sem>;)*
$(impl Semantics for $sem {
const BITS: usize = $bits;
const EXP_BITS: usize = $exp_bits;
($(
$(#[$meta:meta])*
$name:ident = $sem:ident($bits:tt : $exp_bits:tt) $({ $($extra:tt)* })?
),* $(,)?) => {
$(
#[doc = concat!("Floating point semantics for [`", stringify!($name), "`].")]
///
/// See that type for more details.
pub struct $sem;

$(#[$meta])*
pub type $name = IeeeFloat<$sem>;

$($($extra)*)?
})*
impl Semantics for $sem {
const BITS: usize = $bits;
const EXP_BITS: usize = $exp_bits;

$($($extra)*)?
}
)*
}
}

ieee_semantics! {
/// IEEE binary16 half-precision (16-bit) floating point number.
Half = HalfS(16:5),

/// IEEE binary32 single-precision (32-bit) floating point number.
Single = SingleS(32:8),

/// IEEE binary64 double-precision (64-bit) floating point number.
Double = DoubleS(64:11),
Quad = QuadS(128:15),

// Non-standard IEEE-like semantics:
/// IEEE binary128 quadruple-precision (128-bit) floating point number.
Quad = QuadS(128:15),

// FIXME(eddyb) document this as "Brain Float 16" (C++ didn't have docs).
/// 16-bit brain floating point number.
///
/// This is not an IEEE kind but uses the same semantics.
BFloat = BFloatS(16:8),

// 8-bit floating point number following IEEE-754 conventions with bit
// layout S1E5M2 as described in https://arxiv.org/abs/2209.05433.
/// 8-bit floating point number with S1E5M2 bit layout.
///
/// Follows IEEE-754 conventions with S1E5M2 bit layout as described in
/// <https://arxiv.org/abs/2209.05433>.
Float8E5M2 = Float8E5M2S(8:5),

// 8-bit floating point number mostly following IEEE-754 conventions with
// bit layout S1E4M3 as described in https://arxiv.org/abs/2209.05433.
// Unlike IEEE-754 types, there are no infinity values, and NaN is
// represented with the exponent and mantissa bits set to all 1s.
/// 8-bit floating point number with S1E4M3 bit layout.
///
/// This type mostly follows IEEE-754 conventions with a
/// bit layout S1E4M3 as described in <https://arxiv.org/abs/2209.05433>.
/// Unlike IEEE-754 types, there are no infinity values, and NaN is
/// represented with the exponent and mantissa bits set to all 1s.
Float8E4M3FN = Float8E4M3FNS(8:4) {
const NONFINITE_BEHAVIOR: NonfiniteBehavior = NonfiniteBehavior::NanOnly;
},
}

// FIXME(eddyb) consider moving X87-specific logic to a "has explicit integer bit"
// associated `const` on `Semantics` itself.
/// Floating point semantics for [`X87DoubleExtended`].
///
/// See that type for more details.
pub struct X87DoubleExtendedS;

/// 80-bit floating point number that uses IEEE extended precision semantics, as used
/// by x87 `long double`.
pub type X87DoubleExtended = IeeeFloat<X87DoubleExtendedS>;

impl Semantics for X87DoubleExtendedS {
const BITS: usize = 80;
const EXP_BITS: usize = 15;
Expand Down
17 changes: 16 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Port of LLVM's APFloat software floating-point implementation from the
//! following C++ sources (please update commit hash when backporting):
//! https://github.com/llvm/llvm-project/commit/462a31f5a5abb905869ea93cc49b096079b11aa4
//! <https://github.com/llvm/llvm-project/commit/462a31f5a5abb905869ea93cc49b096079b11aa4>
//! * `llvm/include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits
//! * `llvm/lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules
//! * `llvm/unittests/ADT/APFloatTest.cpp` -> `tests` directory
Expand Down Expand Up @@ -64,6 +64,7 @@ bitflags! {
}
}

/// The result of a computation consisting of the output value and the exceptions, if any.
#[must_use]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct StatusAnd<T> {
Expand All @@ -72,12 +73,14 @@ pub struct StatusAnd<T> {
}

impl Status {
/// Add a value to this status to create a [`StatusAnd`].
pub fn and<T>(self, value: T) -> StatusAnd<T> {
StatusAnd { status: self, value }
}
}

impl<T> StatusAnd<T> {
/// Keep the existing status but apply a transformation to `value`.
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> StatusAnd<U> {
StatusAnd {
status: self.status,
Expand All @@ -86,6 +89,14 @@ impl<T> StatusAnd<T> {
}
}

impl<T: core::fmt::Debug> StatusAnd<T> {
/// Extract the inner value if there were no errors. If there were errors, panic.
pub fn unwrap(self) -> T {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really judge the usefulness(*) of this but I guess it's fine to include in the public API.

(*): I don't know the consumer base of this crate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the main use case of this crate is testing softfloat libraries, e.g. compiler_builtins and libm. This method is just a convenience because the vast majority of the time you don't expect NaN and just want the inner value, but NaN would be an error so you should at least detect it.

assert_eq!(self.status, Status::OK, "called `StatusAnd::unwrap()` on an error value. Value: {:?}", self.value);
self.value
}
}

#[macro_export]
macro_rules! unpack {
($status:ident|=, $e:expr) => {
Expand Down Expand Up @@ -145,6 +156,7 @@ pub const IEK_INF: ExpInt = ExpInt::max_value();
pub const IEK_NAN: ExpInt = ExpInt::min_value();
pub const IEK_ZERO: ExpInt = ExpInt::min_value() + 1;

/// An error which can occur when parsing a floating point number from a string.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ParseError(pub &'static str);

Expand Down Expand Up @@ -590,6 +602,7 @@ pub trait Float:
}
}

/// Convert between floating point types.
pub trait FloatConvert<T: Float>: Float {
/// Convert a value of one floating point type to another.
/// The return value corresponds to the IEEE754 exceptions. *loses_info
Expand All @@ -598,6 +611,8 @@ pub trait FloatConvert<T: Float>: Float {
/// original value (this is almost the same as return value==Status::OK,
/// but there are edge cases where this is not so).
fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<T>;

/// Convert with default [`NearestTiesToEven`](Round::NearestTiesToEven) rounding.
fn convert(self, loses_info: &mut bool) -> StatusAnd<T> {
self.convert_r(Round::NearestTiesToEven, loses_info)
}
Expand Down
8 changes: 8 additions & 0 deletions src/ppc.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
//! Support for floating point types that use PowerPC semantics.

use crate::ieee;
use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd};

use core::cmp::Ordering;
use core::fmt;
use core::ops::Neg;

/// A larger floating point number represented by two smaller floats.
#[must_use]
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct DoubleFloat<F>(F, F);

/// 128-bit floating point number comprised of two IEEE [`Double`](ieee::Double) values.
///
/// This is the "IBM Extended Double" format, described at
/// <https://www.ibm.com/docs/en/aix/7.3?topic=sepl-128-bit-long-double-floating-point-data-type>.
pub type DoubleDouble = DoubleFloat<ieee::Double>;

// These are legacy semantics for the Fallback, inaccrurate implementation of
Expand Down
Loading