Add an 8-bit float type for testing purposes
Introduce `f8`, which is an 8-bit float compliant with IEEE-754. This type is useful for testing since it is easily possible to enumerate all values.
This commit is contained in:
487
library/compiler-builtins/libm/crates/libm-test/src/f8_impl.rs
Normal file
487
library/compiler-builtins/libm/crates/libm-test/src/f8_impl.rs
Normal file
@@ -0,0 +1,487 @@
|
||||
//! An IEEE-compliant 8-bit float type for testing purposes.
|
||||
|
||||
use std::cmp::{self, Ordering};
|
||||
use std::{fmt, ops};
|
||||
|
||||
use crate::Float;
|
||||
|
||||
/// Sometimes verifying float logic is easiest when all values can quickly be checked exhaustively
|
||||
/// or by hand.
|
||||
///
|
||||
/// IEEE-754 compliant type that includes a 1 bit sign, 4 bit exponent, and 3 bit significand.
|
||||
/// Bias is -7.
|
||||
///
|
||||
/// Based on <https://en.wikipedia.org/wiki/Minifloat#Example_8-bit_float_(1.4.3)>.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct f8(u8);
|
||||
|
||||
impl Float for f8 {
|
||||
type Int = u8;
|
||||
type SignedInt = i8;
|
||||
type ExpInt = i8;
|
||||
|
||||
const ZERO: Self = Self(0b0_0000_000);
|
||||
const NEG_ZERO: Self = Self(0b1_0000_000);
|
||||
const ONE: Self = Self(0b0_0111_000);
|
||||
const NEG_ONE: Self = Self(0b1_0111_000);
|
||||
const MAX: Self = Self(0b0_1110_111);
|
||||
const MIN: Self = Self(0b1_1110_111);
|
||||
const INFINITY: Self = Self(0b0_1111_000);
|
||||
const NEG_INFINITY: Self = Self(0b1_1111_000);
|
||||
const NAN: Self = Self(0b0_1111_100);
|
||||
const PI: Self = Self::ZERO;
|
||||
const NEG_PI: Self = Self::ZERO;
|
||||
const FRAC_PI_2: Self = Self::ZERO;
|
||||
|
||||
const BITS: u32 = 8;
|
||||
const SIG_BITS: u32 = 3;
|
||||
const SIGN_MASK: Self::Int = 0b1_0000_000;
|
||||
const SIG_MASK: Self::Int = 0b0_0000_111;
|
||||
const EXP_MASK: Self::Int = 0b0_1111_000;
|
||||
const IMPLICIT_BIT: Self::Int = 0b0_0001_000;
|
||||
|
||||
fn to_bits(self) -> Self::Int {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn to_bits_signed(self) -> Self::SignedInt {
|
||||
self.0 as i8
|
||||
}
|
||||
|
||||
fn is_nan(self) -> bool {
|
||||
self.0 & Self::EXP_MASK == Self::EXP_MASK && self.0 & Self::SIG_MASK != 0
|
||||
}
|
||||
|
||||
fn is_infinite(self) -> bool {
|
||||
self.0 & Self::EXP_MASK == Self::EXP_MASK && self.0 & Self::SIG_MASK == 0
|
||||
}
|
||||
|
||||
fn is_sign_negative(self) -> bool {
|
||||
self.0 & Self::SIGN_MASK != 0
|
||||
}
|
||||
|
||||
fn exp(self) -> Self::ExpInt {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn from_bits(a: Self::Int) -> Self {
|
||||
Self(a)
|
||||
}
|
||||
|
||||
fn normalize(_significand: Self::Int) -> (i32, Self::Int) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl f8 {
|
||||
pub const ALL_LEN: usize = 240;
|
||||
|
||||
/// All non-infinite non-NaN values of `f8`
|
||||
pub const ALL: [Self; Self::ALL_LEN] = [
|
||||
// -m*2^7
|
||||
Self(0b1_1110_111), // -240
|
||||
Self(0b1_1110_110),
|
||||
Self(0b1_1110_101),
|
||||
Self(0b1_1110_100),
|
||||
Self(0b1_1110_011),
|
||||
Self(0b1_1110_010),
|
||||
Self(0b1_1110_001),
|
||||
Self(0b1_1110_000), // -128
|
||||
// -m*2^6
|
||||
Self(0b1_1101_111), // -120
|
||||
Self(0b1_1101_110),
|
||||
Self(0b1_1101_101),
|
||||
Self(0b1_1101_100),
|
||||
Self(0b1_1101_011),
|
||||
Self(0b1_1101_010),
|
||||
Self(0b1_1101_001),
|
||||
Self(0b1_1101_000), // -64
|
||||
// -m*2^5
|
||||
Self(0b1_1100_111), // -60
|
||||
Self(0b1_1100_110),
|
||||
Self(0b1_1100_101),
|
||||
Self(0b1_1100_100),
|
||||
Self(0b1_1100_011),
|
||||
Self(0b1_1100_010),
|
||||
Self(0b1_1100_001),
|
||||
Self(0b1_1100_000), // -32
|
||||
// -m*2^4
|
||||
Self(0b1_1011_111), // -30
|
||||
Self(0b1_1011_110),
|
||||
Self(0b1_1011_101),
|
||||
Self(0b1_1011_100),
|
||||
Self(0b1_1011_011),
|
||||
Self(0b1_1011_010),
|
||||
Self(0b1_1011_001),
|
||||
Self(0b1_1011_000), // -16
|
||||
// -m*2^3
|
||||
Self(0b1_1010_111), // -15
|
||||
Self(0b1_1010_110),
|
||||
Self(0b1_1010_101),
|
||||
Self(0b1_1010_100),
|
||||
Self(0b1_1010_011),
|
||||
Self(0b1_1010_010),
|
||||
Self(0b1_1010_001),
|
||||
Self(0b1_1010_000), // -8
|
||||
// -m*2^2
|
||||
Self(0b1_1001_111), // -7.5
|
||||
Self(0b1_1001_110),
|
||||
Self(0b1_1001_101),
|
||||
Self(0b1_1001_100),
|
||||
Self(0b1_1001_011),
|
||||
Self(0b1_1001_010),
|
||||
Self(0b1_1001_001),
|
||||
Self(0b1_1001_000), // -4
|
||||
// -m*2^1
|
||||
Self(0b1_1000_111), // -3.75
|
||||
Self(0b1_1000_110),
|
||||
Self(0b1_1000_101),
|
||||
Self(0b1_1000_100),
|
||||
Self(0b1_1000_011),
|
||||
Self(0b1_1000_010),
|
||||
Self(0b1_1000_001),
|
||||
Self(0b1_1000_000), // -2
|
||||
// -m*2^0
|
||||
Self(0b1_0111_111), // -1.875
|
||||
Self(0b1_0111_110),
|
||||
Self(0b1_0111_101),
|
||||
Self(0b1_0111_100),
|
||||
Self(0b1_0111_011),
|
||||
Self(0b1_0111_010),
|
||||
Self(0b1_0111_001),
|
||||
Self(0b1_0111_000), // -1
|
||||
// -m*2^-1
|
||||
Self(0b1_0110_111), // −0.9375
|
||||
Self(0b1_0110_110),
|
||||
Self(0b1_0110_101),
|
||||
Self(0b1_0110_100),
|
||||
Self(0b1_0110_011),
|
||||
Self(0b1_0110_010),
|
||||
Self(0b1_0110_001),
|
||||
Self(0b1_0110_000), // -0.5
|
||||
// -m*2^-2
|
||||
Self(0b1_0101_111), // −0.46875
|
||||
Self(0b1_0101_110),
|
||||
Self(0b1_0101_101),
|
||||
Self(0b1_0101_100),
|
||||
Self(0b1_0101_011),
|
||||
Self(0b1_0101_010),
|
||||
Self(0b1_0101_001),
|
||||
Self(0b1_0101_000), // -0.25
|
||||
// -m*2^-3
|
||||
Self(0b1_0100_111), // −0.234375
|
||||
Self(0b1_0100_110),
|
||||
Self(0b1_0100_101),
|
||||
Self(0b1_0100_100),
|
||||
Self(0b1_0100_011),
|
||||
Self(0b1_0100_010),
|
||||
Self(0b1_0100_001),
|
||||
Self(0b1_0100_000), // -0.125
|
||||
// -m*2^-4
|
||||
Self(0b1_0011_111), // −0.1171875
|
||||
Self(0b1_0011_110),
|
||||
Self(0b1_0011_101),
|
||||
Self(0b1_0011_100),
|
||||
Self(0b1_0011_011),
|
||||
Self(0b1_0011_010),
|
||||
Self(0b1_0011_001),
|
||||
Self(0b1_0011_000), // −0.0625
|
||||
// -m*2^-5
|
||||
Self(0b1_0010_111), // −0.05859375
|
||||
Self(0b1_0010_110),
|
||||
Self(0b1_0010_101),
|
||||
Self(0b1_0010_100),
|
||||
Self(0b1_0010_011),
|
||||
Self(0b1_0010_010),
|
||||
Self(0b1_0010_001),
|
||||
Self(0b1_0010_000), // −0.03125
|
||||
// -m*2^-6
|
||||
Self(0b1_0001_111), // −0.029296875
|
||||
Self(0b1_0001_110),
|
||||
Self(0b1_0001_101),
|
||||
Self(0b1_0001_100),
|
||||
Self(0b1_0001_011),
|
||||
Self(0b1_0001_010),
|
||||
Self(0b1_0001_001),
|
||||
Self(0b1_0001_000), // −0.015625
|
||||
// -m*2^-7 subnormal numbers
|
||||
Self(0b1_0000_111), // −0.013671875
|
||||
Self(0b1_0000_110),
|
||||
Self(0b1_0000_101),
|
||||
Self(0b1_0000_100),
|
||||
Self(0b1_0000_011),
|
||||
Self(0b1_0000_010),
|
||||
Self(0b1_0000_001), // −0.001953125
|
||||
// Zeroes
|
||||
Self(0b1_0000_000), // -0.0
|
||||
Self(0b0_0000_000), // 0.0
|
||||
// m*2^-7 // subnormal numbers
|
||||
Self(0b0_0000_001),
|
||||
Self(0b0_0000_010),
|
||||
Self(0b0_0000_011),
|
||||
Self(0b0_0000_100),
|
||||
Self(0b0_0000_101),
|
||||
Self(0b0_0000_110),
|
||||
Self(0b0_0000_111), // 0.013671875
|
||||
// m*2^-6
|
||||
Self(0b0_0001_000), // 0.015625
|
||||
Self(0b0_0001_001),
|
||||
Self(0b0_0001_010),
|
||||
Self(0b0_0001_011),
|
||||
Self(0b0_0001_100),
|
||||
Self(0b0_0001_101),
|
||||
Self(0b0_0001_110),
|
||||
Self(0b0_0001_111), // 0.029296875
|
||||
// m*2^-5
|
||||
Self(0b0_0010_000), // 0.03125
|
||||
Self(0b0_0010_001),
|
||||
Self(0b0_0010_010),
|
||||
Self(0b0_0010_011),
|
||||
Self(0b0_0010_100),
|
||||
Self(0b0_0010_101),
|
||||
Self(0b0_0010_110),
|
||||
Self(0b0_0010_111), // 0.05859375
|
||||
// m*2^-4
|
||||
Self(0b0_0011_000), // 0.0625
|
||||
Self(0b0_0011_001),
|
||||
Self(0b0_0011_010),
|
||||
Self(0b0_0011_011),
|
||||
Self(0b0_0011_100),
|
||||
Self(0b0_0011_101),
|
||||
Self(0b0_0011_110),
|
||||
Self(0b0_0011_111), // 0.1171875
|
||||
// m*2^-3
|
||||
Self(0b0_0100_000), // 0.125
|
||||
Self(0b0_0100_001),
|
||||
Self(0b0_0100_010),
|
||||
Self(0b0_0100_011),
|
||||
Self(0b0_0100_100),
|
||||
Self(0b0_0100_101),
|
||||
Self(0b0_0100_110),
|
||||
Self(0b0_0100_111), // 0.234375
|
||||
// m*2^-2
|
||||
Self(0b0_0101_000), // 0.25
|
||||
Self(0b0_0101_001),
|
||||
Self(0b0_0101_010),
|
||||
Self(0b0_0101_011),
|
||||
Self(0b0_0101_100),
|
||||
Self(0b0_0101_101),
|
||||
Self(0b0_0101_110),
|
||||
Self(0b0_0101_111), // 0.46875
|
||||
// m*2^-1
|
||||
Self(0b0_0110_000), // 0.5
|
||||
Self(0b0_0110_001),
|
||||
Self(0b0_0110_010),
|
||||
Self(0b0_0110_011),
|
||||
Self(0b0_0110_100),
|
||||
Self(0b0_0110_101),
|
||||
Self(0b0_0110_110),
|
||||
Self(0b0_0110_111), // 0.9375
|
||||
// m*2^0
|
||||
Self(0b0_0111_000), // 1
|
||||
Self(0b0_0111_001),
|
||||
Self(0b0_0111_010),
|
||||
Self(0b0_0111_011),
|
||||
Self(0b0_0111_100),
|
||||
Self(0b0_0111_101),
|
||||
Self(0b0_0111_110),
|
||||
Self(0b0_0111_111), // 1.875
|
||||
// m*2^1
|
||||
Self(0b0_1000_000), // 2
|
||||
Self(0b0_1000_001),
|
||||
Self(0b0_1000_010),
|
||||
Self(0b0_1000_011),
|
||||
Self(0b0_1000_100),
|
||||
Self(0b0_1000_101),
|
||||
Self(0b0_1000_110),
|
||||
Self(0b0_1000_111), // 3.75
|
||||
// m*2^2
|
||||
Self(0b0_1001_000), // 4
|
||||
Self(0b0_1001_001),
|
||||
Self(0b0_1001_010),
|
||||
Self(0b0_1001_011),
|
||||
Self(0b0_1001_100),
|
||||
Self(0b0_1001_101),
|
||||
Self(0b0_1001_110),
|
||||
Self(0b0_1001_111), // 7.5
|
||||
// m*2^3
|
||||
Self(0b0_1010_000), // 8
|
||||
Self(0b0_1010_001),
|
||||
Self(0b0_1010_010),
|
||||
Self(0b0_1010_011),
|
||||
Self(0b0_1010_100),
|
||||
Self(0b0_1010_101),
|
||||
Self(0b0_1010_110),
|
||||
Self(0b0_1010_111), // 15
|
||||
// m*2^4
|
||||
Self(0b0_1011_000), // 16
|
||||
Self(0b0_1011_001),
|
||||
Self(0b0_1011_010),
|
||||
Self(0b0_1011_011),
|
||||
Self(0b0_1011_100),
|
||||
Self(0b0_1011_101),
|
||||
Self(0b0_1011_110),
|
||||
Self(0b0_1011_111), // 30
|
||||
// m*2^5
|
||||
Self(0b0_1100_000), // 32
|
||||
Self(0b0_1100_001),
|
||||
Self(0b0_1100_010),
|
||||
Self(0b0_1100_011),
|
||||
Self(0b0_1100_100),
|
||||
Self(0b0_1100_101),
|
||||
Self(0b0_1100_110),
|
||||
Self(0b0_1100_111), // 60
|
||||
// m*2^6
|
||||
Self(0b0_1101_000), // 64
|
||||
Self(0b0_1101_001),
|
||||
Self(0b0_1101_010),
|
||||
Self(0b0_1101_011),
|
||||
Self(0b0_1101_100),
|
||||
Self(0b0_1101_101),
|
||||
Self(0b0_1101_110),
|
||||
Self(0b0_1101_111), // 120
|
||||
// m*2^7
|
||||
Self(0b0_1110_000), // 128
|
||||
Self(0b0_1110_001),
|
||||
Self(0b0_1110_010),
|
||||
Self(0b0_1110_011),
|
||||
Self(0b0_1110_100),
|
||||
Self(0b0_1110_101),
|
||||
Self(0b0_1110_110),
|
||||
Self(0b0_1110_111), // 240
|
||||
];
|
||||
}
|
||||
|
||||
impl ops::Add for f8 {
|
||||
type Output = Self;
|
||||
fn add(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for f8 {
|
||||
type Output = Self;
|
||||
fn sub(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl ops::Mul for f8 {
|
||||
type Output = Self;
|
||||
fn mul(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl ops::Div for f8 {
|
||||
type Output = Self;
|
||||
fn div(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Neg for f8 {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
Self(self.0 ^ Self::SIGN_MASK)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Rem for f8 {
|
||||
type Output = Self;
|
||||
fn rem(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::AddAssign for f8 {
|
||||
fn add_assign(&mut self, _rhs: Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::SubAssign for f8 {
|
||||
fn sub_assign(&mut self, _rhs: Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::MulAssign for f8 {
|
||||
fn mul_assign(&mut self, _rhs: Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialEq for f8 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if self.is_nan() || other.is_nan() {
|
||||
false
|
||||
} else if self.abs().to_bits() | other.abs().to_bits() == 0 {
|
||||
true
|
||||
} else {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
}
|
||||
impl cmp::PartialOrd for f8 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
let inf_rep = f8::EXP_MASK;
|
||||
|
||||
let a_abs = self.abs().to_bits();
|
||||
let b_abs = other.abs().to_bits();
|
||||
|
||||
// If either a or b is NaN, they are unordered.
|
||||
if a_abs > inf_rep || b_abs > inf_rep {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If a and b are both zeros, they are equal.
|
||||
if a_abs | b_abs == 0 {
|
||||
return Some(Ordering::Equal);
|
||||
}
|
||||
|
||||
let a_srep = self.to_bits_signed();
|
||||
let b_srep = other.to_bits_signed();
|
||||
let res = a_srep.cmp(&b_srep);
|
||||
|
||||
if a_srep & b_srep >= 0 {
|
||||
// If at least one of a and b is positive, we get the same result comparing
|
||||
// a and b as signed integers as we would with a fp_ting-point compare.
|
||||
Some(res)
|
||||
} else {
|
||||
// Otherwise, both are negative, so we need to flip the sense of the
|
||||
// comparison to get the correct result.
|
||||
Some(res.reverse())
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for f8 {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for f8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Binary::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Binary for f8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let v = self.0;
|
||||
write!(
|
||||
f,
|
||||
"0b{:b}_{:04b}_{:03b}",
|
||||
v >> 7,
|
||||
(v & Self::EXP_MASK) >> Self::SIG_BITS,
|
||||
v & Self::SIG_MASK
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::LowerHex for f8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
#![allow(clippy::unusual_byte_groupings)] // sometimes we group by sign_exp_sig
|
||||
|
||||
mod f8_impl;
|
||||
pub mod gen;
|
||||
#[cfg(feature = "test-multiprecision")]
|
||||
pub mod mpfloat;
|
||||
@@ -5,6 +8,7 @@ pub mod op;
|
||||
mod precision;
|
||||
mod test_traits;
|
||||
|
||||
pub use f8_impl::f8;
|
||||
pub use libm::support::{Float, Int, IntTy};
|
||||
pub use op::{BaseName, Identifier, MathOp, OpCFn, OpFTy, OpRustFn, OpRustRet};
|
||||
pub use precision::{MaybeOverride, SpecialCase, default_ulp};
|
||||
|
||||
Reference in New Issue
Block a user