use std::cmp::Ordering;
use std::ops::{BitAnd, BitOr};
use num_traits::Zero;
use rug::Integer;
use crate::ieee754::IEEE754Context;
use crate::rfloat::RFloat;
use crate::util::bitmask;
use crate::Real;
#[derive(Clone, Debug, Default)]
pub struct Exceptions {
pub invalid: bool,
pub divzero: bool,
pub overflow: bool,
pub underflow_pre: bool,
pub underflow_post: bool,
pub inexact: bool,
pub carry: bool,
pub denorm: bool,
pub tiny_pre: bool,
pub tiny_post: bool,
}
impl Exceptions {
pub fn new() -> Self {
Self {
invalid: false,
divzero: false,
overflow: false,
underflow_pre: false,
underflow_post: false,
inexact: false,
carry: false,
denorm: false,
tiny_pre: false,
tiny_post: false,
}
}
}
#[derive(Clone, Debug)]
pub enum IEEE754Val {
PosZero,
NegZero,
Subnormal(bool, Integer),
Normal(bool, isize, Integer),
PosInfinity,
NegInfinity,
Nan(bool, bool, Integer),
}
#[derive(Clone, Debug)]
pub struct IEEE754 {
pub(crate) num: IEEE754Val,
pub(crate) flags: Exceptions,
pub(crate) ctx: IEEE754Context,
}
impl IEEE754 {
pub fn flags(&self) -> &Exceptions {
&self.flags
}
pub fn ctx(&self) -> &IEEE754Context {
&self.ctx
}
pub fn is_subnormal(&self) -> bool {
matches!(self.num, IEEE754Val::Subnormal(_, _))
}
pub fn is_normal(&self) -> bool {
matches!(self.num, IEEE754Val::Normal(_, _, _))
}
pub fn is_nan(&self) -> bool {
matches!(self.num, IEEE754Val::Nan(_, _, _))
}
pub fn nan_quiet(&self) -> Option<bool> {
match &self.num {
IEEE754Val::Nan(_, q, _) => Some(*q),
_ => None,
}
}
pub fn nan_payload(&self) -> Option<Integer> {
match &self.num {
IEEE754Val::Nan(_, _, payload) => Some(payload.clone()),
_ => None,
}
}
pub fn into_bits(self) -> Integer {
let nbits = self.ctx.nbits();
let (s, unsigned) = match &self.num {
IEEE754Val::PosZero => (false, Integer::zero()),
IEEE754Val::NegZero => (true, Integer::zero()),
IEEE754Val::Subnormal(s, c) => (*s, c.clone()),
IEEE754Val::Normal(s, exp, c) => {
let m = self.ctx().max_m();
let efield = Integer::from((exp + m as isize) + self.ctx().emax()) << m;
let mfield = c.clone().bitand(bitmask(m));
(*s, mfield.bitor(efield))
}
IEEE754Val::PosInfinity => {
let m = self.ctx().max_m();
let efield = bitmask(self.ctx.es()) << m;
(false, efield)
}
IEEE754Val::NegInfinity => {
let m = self.ctx().max_m();
let efield = bitmask(self.ctx.es()) << m;
(true, efield)
}
IEEE754Val::Nan(s, q, payload) => {
let m = self.ctx().max_m() as isize;
let efield = bitmask(self.ctx.es()) << m;
let qfield = if *q {
Integer::from(1) << (m - 1)
} else {
Integer::zero()
};
(*s, payload.clone().bitor(qfield).bitor(efield))
}
};
if s {
let sfield = Integer::from(1) << (nbits - 1);
unsigned.bitor(sfield)
} else {
unsigned
}
}
}
impl Real for IEEE754 {
fn radix() -> usize {
2
}
fn sign(&self) -> Option<bool> {
match &self.num {
IEEE754Val::PosZero => Some(false),
IEEE754Val::NegZero => Some(true),
IEEE754Val::Subnormal(s, _) => Some(*s),
IEEE754Val::Normal(s, _, _) => Some(*s),
IEEE754Val::PosInfinity => Some(false),
IEEE754Val::NegInfinity => Some(true),
IEEE754Val::Nan(s, _, _) => Some(*s),
}
}
fn exp(&self) -> Option<isize> {
match &self.num {
IEEE754Val::Subnormal(_, _) => Some(self.ctx().expmin()),
IEEE754Val::Normal(_, exp, _) => Some(*exp),
_ => None,
}
}
fn e(&self) -> Option<isize> {
match &self.num {
IEEE754Val::Subnormal(_, c) => {
let n = self.ctx().expmin() - 1;
Some(n + (c.significant_bits() as isize))
}
IEEE754Val::Normal(_, exp, c) => Some((*exp - 1) + (c.significant_bits() as isize)),
_ => None,
}
}
fn n(&self) -> Option<isize> {
match &self.num {
IEEE754Val::Subnormal(_, _) => Some(self.ctx().expmin() - 1),
IEEE754Val::Normal(_, exp, _) => Some(exp - 1),
_ => None,
}
}
fn c(&self) -> Option<Integer> {
match &self.num {
IEEE754Val::Subnormal(_, c) => Some(c.clone()),
IEEE754Val::Normal(_, _, c) => Some(c.clone()),
_ => None,
}
}
fn m(&self) -> Option<Integer> {
self.c().map(|c| if self.sign().unwrap() { -c } else { c })
}
fn prec(&self) -> Option<usize> {
match &self.num {
IEEE754Val::Subnormal(_, c) => Some(c.significant_bits() as usize),
IEEE754Val::Normal(_, _, c) => Some(c.significant_bits() as usize),
_ => None,
}
}
fn is_nar(&self) -> bool {
matches!(
&self.num,
IEEE754Val::PosInfinity | IEEE754Val::NegInfinity | IEEE754Val::Nan(_, _, _)
)
}
fn is_finite(&self) -> bool {
matches!(
&self.num,
IEEE754Val::PosZero
| IEEE754Val::NegZero
| IEEE754Val::Subnormal(_, _)
| IEEE754Val::Normal(_, _, _)
)
}
fn is_infinite(&self) -> bool {
matches!(&self.num, IEEE754Val::PosInfinity | IEEE754Val::NegInfinity)
}
fn is_zero(&self) -> bool {
matches!(&self.num, IEEE754Val::PosZero | IEEE754Val::NegZero)
}
fn is_negative(&self) -> Option<bool> {
match &self.num {
IEEE754Val::PosZero | IEEE754Val::NegZero => None,
IEEE754Val::Subnormal(s, _) => Some(*s),
IEEE754Val::Normal(s, _, _) => Some(*s),
IEEE754Val::PosInfinity => Some(false),
IEEE754Val::NegInfinity => Some(true),
IEEE754Val::Nan(_, _, _) => None,
}
}
fn is_numerical(&self) -> bool {
!matches!(&self.num, IEEE754Val::Nan(_, _, _))
}
}
impl From<IEEE754> for RFloat {
fn from(val: IEEE754) -> Self {
match val.num {
IEEE754Val::PosZero | IEEE754Val::NegZero => RFloat::zero(),
IEEE754Val::Subnormal(s, c) => RFloat::Real(s, val.ctx.expmin(), c),
IEEE754Val::Normal(s, exp, c) => RFloat::Real(s, exp, c),
IEEE754Val::PosInfinity => RFloat::PosInfinity,
IEEE754Val::NegInfinity => RFloat::NegInfinity,
IEEE754Val::Nan(_, _, _) => RFloat::Nan,
}
}
}
impl From<IEEE754> for rug::Float {
fn from(val: IEEE754) -> Self {
let s = val.sign().unwrap();
let f = rug::Float::from(RFloat::from(val));
if f.is_zero() && s {
-f
} else {
f
}
}
}
impl PartialOrd for IEEE754 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
RFloat::from(self.clone()).partial_cmp(&RFloat::from(other.clone()))
}
}
impl PartialEq for IEEE754 {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}