use super::kernel::{self, Class, IntValue, Outcome, Parts, Rounded};
use super::{Env, Flags, IntOverflow, Spec};
pub trait Format: Copy + core::fmt::Debug {
const SIG_BITS: u32;
const EXP_BITS: u32;
const BITS: u32 = Self::SIG_BITS + Self::EXP_BITS + 1;
const PRECISION: u32 = Self::SIG_BITS + 1;
const BIAS: i32 = (1i32 << (Self::EXP_BITS - 1)) - 1;
const EMIN: i32 = 1 - Self::BIAS;
const EMAX: i32 = Self::BIAS;
const MASK: u64 = u64::MAX >> (64 - Self::BITS);
const SIGN: u64 = 1u64 << (Self::BITS - 1);
const SIG_MASK: u64 = (1u64 << Self::SIG_BITS) - 1;
const EXP_FIELD_MAX: u64 = (1u64 << Self::EXP_BITS) - 1;
const QUIET: u64 = 1u64 << (Self::SIG_BITS - 1);
const INF: u64 = Self::EXP_FIELD_MAX << Self::SIG_BITS;
const QUIET_NAN: u64 = Self::INF | Self::QUIET;
const MAX_FINITE: u64 = Self::INF - 1;
const SPEC: Spec = Spec::interchange(Self::PRECISION, Self::EMAX);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct B32;
impl Format for B32 {
const SIG_BITS: u32 = 23;
const EXP_BITS: u32 = 8;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct B64;
impl Format for B64 {
const SIG_BITS: u32 = 52;
const EXP_BITS: u32 = 11;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Category {
SignalingNan,
QuietNan,
NegativeInfinity,
NegativeNormal,
NegativeSubnormal,
NegativeZero,
PositiveZero,
PositiveSubnormal,
PositiveNormal,
PositiveInfinity,
}
impl Category {
#[must_use]
pub const fn riscv_fclass(self) -> u64 {
match self {
Category::NegativeInfinity => 1 << 0,
Category::NegativeNormal => 1 << 1,
Category::NegativeSubnormal => 1 << 2,
Category::NegativeZero => 1 << 3,
Category::PositiveZero => 1 << 4,
Category::PositiveSubnormal => 1 << 5,
Category::PositiveNormal => 1 << 6,
Category::PositiveInfinity => 1 << 7,
Category::SignalingNan => 1 << 8,
Category::QuietNan => 1 << 9,
}
}
}
fn decode<F: Format>(bits: u64) -> Parts {
let bits = bits & F::MASK;
let sign = bits & F::SIGN != 0;
let field = (bits >> F::SIG_BITS) & F::EXP_FIELD_MAX;
let frac = bits & F::SIG_MASK;
if field == F::EXP_FIELD_MAX {
if frac == 0 {
Parts {
sign,
exp: 0,
frac: 0,
class: Class::Inf,
snan: false,
}
} else {
Parts {
sign,
exp: 0,
frac,
class: Class::Nan,
snan: frac & F::QUIET == 0,
}
}
} else if field == 0 {
Parts {
sign,
exp: F::SPEC.min_ulp,
frac,
class: if frac == 0 {
Class::Zero
} else {
Class::Finite
},
snan: false,
}
} else {
Parts {
sign,
exp: field as i32 - F::BIAS - F::SIG_BITS as i32,
frac: frac | (1u64 << F::SIG_BITS),
class: Class::Finite,
snan: false,
}
}
}
fn unpack<F: Format>(bits: u64, env: Env) -> (Parts, Flags) {
let p = decode::<F>(bits);
let subnormal = p.class == Class::Finite && p.frac < (1u64 << F::SIG_BITS);
if !subnormal {
return (p, Flags::NONE);
}
let flags = if env.subnormal_inputs.reports() {
Flags::DENORMAL
} else {
Flags::NONE
};
if env.subnormal_inputs.flushes() {
(Parts::zero(p.sign), flags)
} else {
(p, flags)
}
}
pub(super) fn encode_outcome<F: Format>(out: Outcome, env: Env) -> u64 {
let sign_bit = |sign: bool| if sign { F::SIGN } else { 0 };
match out {
Outcome::DefaultNan => sign_bit(env.nan.default_sign) | F::QUIET_NAN,
Outcome::Nan { sign, payload } => {
sign_bit(sign) | F::INF | F::QUIET | (payload & F::SIG_MASK)
}
Outcome::Num(sign, Rounded::Zero) => sign_bit(sign),
Outcome::Num(sign, Rounded::Inf) => sign_bit(sign) | F::INF,
Outcome::Num(sign, Rounded::Finite { exp, frac }) => {
let msb = 63 - frac.leading_zeros();
let lead = exp + msb as i32;
if lead >= F::EMIN {
let frac = frac << (F::SIG_BITS - msb);
let field = (lead + F::BIAS) as u64;
sign_bit(sign) | (field << F::SIG_BITS) | (frac & F::SIG_MASK)
} else {
sign_bit(sign) | (frac << (exp - F::SPEC.min_ulp))
}
}
}
}
pub fn add<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (out, f) = kernel::add(pa, pb, false, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa | fb)
}
pub fn sub<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (out, f) = kernel::add(pa, pb, true, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa | fb)
}
pub fn mul<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (out, f) = kernel::mul(pa, pb, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa | fb)
}
pub fn div<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (out, f) = kernel::div(pa, pb, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa | fb)
}
pub fn sqrt<F: Format>(a: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (out, f) = kernel::sqrt(pa, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa)
}
pub fn fma<F: Format>(a: u64, b: u64, c: u64, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (pc, fc) = unpack::<F>(c, env);
let (out, f) = kernel::fma(pa, pb, pc, F::SPEC, env);
(encode_outcome::<F>(out, env), f | fa | fb | fc)
}
#[must_use]
pub fn compare<F: Format>(a: u64, b: u64) -> Option<core::cmp::Ordering> {
let (pa, pb) = (decode::<F>(a), decode::<F>(b));
if pa.class == Class::Nan || pb.class == Class::Nan {
return None;
}
Some(kernel::compare(pa, pb))
}
pub fn eq<F: Format>(a: u64, b: u64) -> (bool, Flags) {
let (pa, pb) = (decode::<F>(a), decode::<F>(b));
if pa.class == Class::Nan || pb.class == Class::Nan {
let f = if pa.snan || pb.snan {
Flags::INVALID
} else {
Flags::NONE
};
return (false, f);
}
(
kernel::compare(pa, pb) == core::cmp::Ordering::Equal,
Flags::NONE,
)
}
pub fn lt<F: Format>(a: u64, b: u64) -> (bool, Flags) {
match compare::<F>(a, b) {
None => (false, Flags::INVALID),
Some(ord) => (ord == core::cmp::Ordering::Less, Flags::NONE),
}
}
pub fn le<F: Format>(a: u64, b: u64) -> (bool, Flags) {
match compare::<F>(a, b) {
None => (false, Flags::INVALID),
Some(ord) => (ord != core::cmp::Ordering::Greater, Flags::NONE),
}
}
pub fn min<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
min_max::<F>(a, b, true, env)
}
pub fn max<F: Format>(a: u64, b: u64, env: Env) -> (u64, Flags) {
min_max::<F>(a, b, false, env)
}
fn min_max<F: Format>(a: u64, b: u64, want_min: bool, env: Env) -> (u64, Flags) {
let (pa, fa) = unpack::<F>(a, env);
let (pb, fb) = unpack::<F>(b, env);
let (pick, f) = kernel::min_max(pa, pb, want_min, env);
let bits = match pick {
Ok(i) => {
let (parts, raw) = if i == 0 { (pa, a) } else { (pb, b) };
if parts.class == Class::Zero && raw & F::MASK & !F::SIGN != 0 {
encode_outcome::<F>(Outcome::Num(parts.sign, Rounded::Zero), env)
} else {
raw & F::MASK
}
}
Err(out) => encode_outcome::<F>(out, env),
};
(bits, f | fa | fb)
}
#[must_use]
pub fn classify<F: Format>(a: u64) -> Category {
let p = decode::<F>(a);
match p.class {
Class::Nan => {
if p.snan {
Category::SignalingNan
} else {
Category::QuietNan
}
}
Class::Inf => {
if p.sign {
Category::NegativeInfinity
} else {
Category::PositiveInfinity
}
}
Class::Zero => {
if p.sign {
Category::NegativeZero
} else {
Category::PositiveZero
}
}
Class::Finite => {
let subnormal = p.frac < (1u64 << F::SIG_BITS);
match (p.sign, subnormal) {
(true, false) => Category::NegativeNormal,
(true, true) => Category::NegativeSubnormal,
(false, true) => Category::PositiveSubnormal,
(false, false) => Category::PositiveNormal,
}
}
}
}
pub fn convert<A: Format, B: Format>(bits: u64, env: Env) -> (u64, Flags) {
let (p, fin) = unpack::<A>(bits, env);
let (out, f) = match p.class {
Class::Nan => {
let flags = if p.snan { Flags::INVALID } else { Flags::NONE };
match super::kernel::nan_result(&[p], env) {
Some((Outcome::Nan { sign, payload }, _)) => {
let payload = if B::SIG_BITS >= A::SIG_BITS {
payload << (B::SIG_BITS - A::SIG_BITS)
} else {
payload >> (A::SIG_BITS - B::SIG_BITS)
};
(Outcome::Nan { sign, payload }, flags)
}
_ => (Outcome::DefaultNan, flags),
}
}
Class::Inf => (Outcome::Num(p.sign, Rounded::Inf), Flags::NONE),
Class::Zero => (Outcome::Num(p.sign, Rounded::Zero), Flags::NONE),
Class::Finite => kernel::round_exact(p.sign, p.exp, u128::from(p.frac), B::SPEC, env),
};
(encode_outcome::<B>(out, env), f | fin)
}
fn sign_extend(v: u64, bits: u32) -> i64 {
if bits >= 64 {
v as i64
} else {
((v << (64 - bits)) as i64) >> (64 - bits)
}
}
pub fn to_signed<F: Format>(value: u64, bits: u32, env: Env) -> (i64, Flags) {
let (p, fin) = unpack::<F>(value, env);
let max: u128 = (1u128 << (bits - 1)) - 1;
let min_mag: u128 = 1u128 << (bits - 1);
let most_negative = sign_extend(min_mag as u64, bits);
let most_positive = max as i64;
let out_of_range = |sign: bool| match env.int_overflow {
IntOverflow::Indefinite => most_negative,
_ => {
if sign {
most_negative
} else {
most_positive
}
}
};
match kernel::to_integer(p, env) {
IntValue::Nan => {
let v = match env.int_overflow {
IntOverflow::SaturateNanMax => most_positive,
IntOverflow::SaturateNanZero => 0,
IntOverflow::Indefinite => most_negative,
};
(v, Flags::INVALID | fin)
}
IntValue::Inf(sign) => (out_of_range(sign), Flags::INVALID | fin),
IntValue::Value {
sign,
magnitude,
inexact,
} => {
let fits = if sign {
magnitude <= min_mag
} else {
magnitude <= max
};
if !fits {
return (out_of_range(sign), Flags::INVALID | fin);
}
let v = if sign {
sign_extend((magnitude as u64).wrapping_neg(), bits)
} else {
magnitude as i64
};
let f = if inexact { Flags::INEXACT } else { Flags::NONE };
(v, f | fin)
}
}
}
pub fn to_unsigned<F: Format>(value: u64, bits: u32, env: Env) -> (u64, Flags) {
let (p, fin) = unpack::<F>(value, env);
let max: u128 = if bits >= 64 {
u128::from(u64::MAX)
} else {
(1u128 << bits) - 1
};
match kernel::to_integer(p, env) {
IntValue::Nan => {
let v = match env.int_overflow {
IntOverflow::SaturateNanZero => 0,
_ => max as u64,
};
(v, Flags::INVALID | fin)
}
IntValue::Inf(sign) => (if sign { 0 } else { max as u64 }, Flags::INVALID | fin),
IntValue::Value {
sign,
magnitude,
inexact,
} => {
if sign && magnitude != 0 {
(0, Flags::INVALID | fin)
} else if magnitude > max {
(max as u64, Flags::INVALID | fin)
} else {
let f = if inexact { Flags::INEXACT } else { Flags::NONE };
(magnitude as u64, f | fin)
}
}
}
}
pub fn from_signed<F: Format>(value: i64, bits: u32, env: Env) -> (u64, Flags) {
let v = sign_extend(value as u64, bits);
from_magnitude::<F>(v < 0, u128::from(v.unsigned_abs()), env)
}
pub fn from_unsigned<F: Format>(value: u64, bits: u32, env: Env) -> (u64, Flags) {
let v = if bits >= 64 {
value
} else {
value & ((1u64 << bits) - 1)
};
from_magnitude::<F>(false, u128::from(v), env)
}
fn from_magnitude<F: Format>(sign: bool, magnitude: u128, env: Env) -> (u64, Flags) {
if magnitude == 0 {
return (0, Flags::NONE);
}
let (out, f) = kernel::round_exact(sign, 0, magnitude, F::SPEC, env);
(encode_outcome::<F>(out, env), f)
}