pub mod binary;
mod kernel;
pub mod x87;
#[cfg(test)]
mod tests;
pub use binary::{
B32, B64, Category, Format, add, classify, compare, convert, div, eq, fma, from_signed,
from_unsigned, le, lt, max, min, mul, sqrt, sub, to_signed, to_unsigned,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Round {
#[default]
TiesEven,
TiesAway,
TowardZero,
TowardNegative,
TowardPositive,
}
impl Round {
#[must_use]
pub const fn from_riscv_rm(bits: u32) -> Option<Round> {
match bits {
0 => Some(Round::TiesEven),
1 => Some(Round::TowardZero),
2 => Some(Round::TowardNegative),
3 => Some(Round::TowardPositive),
4 => Some(Round::TiesAway),
_ => None,
}
}
#[must_use]
pub const fn riscv_rm(self) -> u32 {
match self {
Round::TiesEven => 0,
Round::TowardZero => 1,
Round::TowardNegative => 2,
Round::TowardPositive => 3,
Round::TiesAway => 4,
}
}
#[must_use]
pub const fn from_x86_rc(bits: u32) -> Round {
match bits & 3 {
0 => Round::TiesEven,
1 => Round::TowardNegative,
2 => Round::TowardPositive,
_ => Round::TowardZero,
}
}
#[must_use]
pub const fn x86_rc(self) -> u32 {
match self {
Round::TiesEven | Round::TiesAway => 0,
Round::TowardNegative => 1,
Round::TowardPositive => 2,
Round::TowardZero => 3,
}
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Flags(pub u32);
impl Flags {
pub const NONE: Flags = Flags(0);
pub const INVALID: Flags = Flags(1 << 0);
pub const DIV_BY_ZERO: Flags = Flags(1 << 1);
pub const OVERFLOW: Flags = Flags(1 << 2);
pub const UNDERFLOW: Flags = Flags(1 << 3);
pub const INEXACT: Flags = Flags(1 << 4);
pub const DENORMAL: Flags = Flags(1 << 5);
#[must_use]
#[inline]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
#[must_use]
#[inline]
pub const fn contains(self, other: Flags) -> bool {
self.0 & other.0 == other.0
}
#[must_use]
#[inline]
pub const fn union(self, other: Flags) -> Flags {
Flags(self.0 | other.0)
}
#[must_use]
pub const fn to_fcsr(self) -> u32 {
let mut v = 0;
if self.contains(Flags::INEXACT) {
v |= 1 << 0;
}
if self.contains(Flags::UNDERFLOW) {
v |= 1 << 1;
}
if self.contains(Flags::OVERFLOW) {
v |= 1 << 2;
}
if self.contains(Flags::DIV_BY_ZERO) {
v |= 1 << 3;
}
if self.contains(Flags::INVALID) {
v |= 1 << 4;
}
v
}
#[must_use]
pub const fn to_mxcsr(self) -> u32 {
let mut v = 0;
if self.contains(Flags::INVALID) {
v |= 1 << 0;
}
if self.contains(Flags::DENORMAL) {
v |= 1 << 1;
}
if self.contains(Flags::DIV_BY_ZERO) {
v |= 1 << 2;
}
if self.contains(Flags::OVERFLOW) {
v |= 1 << 3;
}
if self.contains(Flags::UNDERFLOW) {
v |= 1 << 4;
}
if self.contains(Flags::INEXACT) {
v |= 1 << 5;
}
v
}
#[must_use]
pub const fn to_x87_status(self) -> u32 {
self.to_mxcsr()
}
#[must_use]
pub const fn to_fpsr(self) -> u32 {
let mut v = 0;
if self.contains(Flags::INVALID) {
v |= 1 << 0;
}
if self.contains(Flags::DIV_BY_ZERO) {
v |= 1 << 1;
}
if self.contains(Flags::OVERFLOW) {
v |= 1 << 2;
}
if self.contains(Flags::UNDERFLOW) {
v |= 1 << 3;
}
if self.contains(Flags::INEXACT) {
v |= 1 << 4;
}
if self.contains(Flags::DENORMAL) {
v |= 1 << 7;
}
v
}
}
impl core::ops::BitOr for Flags {
type Output = Flags;
#[inline]
fn bitor(self, rhs: Flags) -> Flags {
Flags(self.0 | rhs.0)
}
}
impl core::ops::BitOrAssign for Flags {
#[inline]
fn bitor_assign(&mut self, rhs: Flags) {
self.0 |= rhs.0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Propagate {
Default,
FirstNan,
SignalingFirst,
LargerSignificand,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Nan {
pub propagate: Propagate,
pub default_sign: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Tininess {
AfterRounding,
BeforeRounding,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InputSubnormal {
Exact,
Flagged,
Flushed,
FlushedFlagged,
}
impl InputSubnormal {
#[must_use]
#[inline]
pub const fn flushes(self) -> bool {
matches!(
self,
InputSubnormal::Flushed | InputSubnormal::FlushedFlagged
)
}
#[must_use]
#[inline]
pub const fn reports(self) -> bool {
matches!(
self,
InputSubnormal::Flagged | InputSubnormal::FlushedFlagged
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MinMax {
NonNan,
SecondOperand,
PropagateNan,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntOverflow {
SaturateNanMax,
SaturateNanZero,
Indefinite,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Env {
pub round: Round,
pub nan: Nan,
pub tininess: Tininess,
pub subnormal_inputs: InputSubnormal,
pub flush_outputs: bool,
pub min_max: MinMax,
pub int_overflow: IntOverflow,
}
impl Env {
pub const RISCV: Env = Env {
round: Round::TiesEven,
nan: Nan {
propagate: Propagate::Default,
default_sign: false,
},
tininess: Tininess::AfterRounding,
subnormal_inputs: InputSubnormal::Exact,
flush_outputs: false,
min_max: MinMax::NonNan,
int_overflow: IntOverflow::SaturateNanMax,
};
pub const X86_SSE: Env = Env {
round: Round::TiesEven,
nan: Nan {
propagate: Propagate::FirstNan,
default_sign: true,
},
tininess: Tininess::AfterRounding,
subnormal_inputs: InputSubnormal::Flagged,
flush_outputs: false,
min_max: MinMax::SecondOperand,
int_overflow: IntOverflow::Indefinite,
};
pub const X87: Env = Env {
nan: Nan {
propagate: Propagate::LargerSignificand,
default_sign: true,
},
min_max: MinMax::PropagateNan,
..Env::X86_SSE
};
pub const ARM: Env = Env {
round: Round::TiesEven,
nan: Nan {
propagate: Propagate::SignalingFirst,
default_sign: false,
},
tininess: Tininess::BeforeRounding,
subnormal_inputs: InputSubnormal::Exact,
flush_outputs: false,
min_max: MinMax::PropagateNan,
int_overflow: IntOverflow::SaturateNanZero,
};
pub const ARM_DEFAULT_NAN: Env = Env {
nan: Nan {
propagate: Propagate::Default,
default_sign: false,
},
..Env::ARM
};
#[must_use]
#[inline]
pub const fn round(self, round: Round) -> Env {
Env { round, ..self }
}
#[must_use]
#[inline]
pub const fn flush(self, flush: bool) -> Env {
Env {
subnormal_inputs: if flush {
InputSubnormal::FlushedFlagged
} else {
InputSubnormal::Exact
},
flush_outputs: flush,
..self
}
}
#[must_use]
#[inline]
pub const fn daz(self, on: bool) -> Env {
Env {
subnormal_inputs: if on {
InputSubnormal::Flushed
} else {
InputSubnormal::Flagged
},
..self
}
}
#[must_use]
#[inline]
pub const fn ftz(self, on: bool) -> Env {
Env {
flush_outputs: on,
..self
}
}
}
impl Default for Env {
fn default() -> Env {
Env::RISCV
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Spec {
pub precision: u32,
pub emax: i32,
pub min_ulp: i32,
}
impl Spec {
#[must_use]
pub const fn interchange(precision: u32, emax: i32) -> Spec {
Spec {
precision,
emax,
min_ulp: 1 - emax - (precision as i32 - 1),
}
}
#[must_use]
#[inline]
pub const fn emin(self) -> i32 {
1 - self.emax
}
#[must_use]
#[inline]
pub const fn with_precision(self, precision: u32) -> Spec {
Spec { precision, ..self }
}
}