diff --git a/src/posit/number.rs b/src/posit/number.rs index 612dacc..db89367 100644 --- a/src/posit/number.rs +++ b/src/posit/number.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use num_traits::{One, Zero}; use rug::Integer; @@ -175,6 +177,44 @@ impl Real for Posit { } } +impl PartialEq for Posit { + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other) == Some(Ordering::Equal) + } +} + +impl PartialOrd for Posit { + fn partial_cmp(&self, other: &Self) -> Option { + match (&self.num, &other.num) { + (PositVal::Nar, PositVal::Nar) => Some(Ordering::Equal), + (PositVal::Nar, _) => Some(Ordering::Less), + (_, PositVal::Nar) => Some(Ordering::Greater), + (PositVal::Zero, PositVal::Zero) => Some(Ordering::Equal), + (PositVal::Zero, PositVal::NonZero(s, _, _, _)) => { + if *s { + // 0 > -x + Some(Ordering::Greater) + } else { + // 0 < +X + Some(Ordering::Less) + } + } + (PositVal::NonZero(s, _, _, _), PositVal::Zero) => { + if *s { + // -x < 0 + Some(Ordering::Less) + } else { + // +x > 0 + Some(Ordering::Greater) + } + } + (PositVal::NonZero(_, _, _, _), PositVal::NonZero(_, _, _, _)) => { + RFloat::from(self.clone()).partial_cmp(&RFloat::from(other.clone())) + } + } + } +} + impl From for RFloat { fn from(value: Posit) -> Self { match value.num { diff --git a/src/posit/round.rs b/src/posit/round.rs index 6df8bcf..15801f2 100644 --- a/src/posit/round.rs +++ b/src/posit/round.rs @@ -1,8 +1,6 @@ -use std::cmp::max; - use rug::Integer; -use crate::{util::bitmask, Real, RoundingContext}; +use crate::{rfloat::RFloatContext, util::bitmask, Real, RoundingContext, RoundingMode}; use super::{Posit, PositVal}; @@ -106,7 +104,7 @@ impl PositContext { /// Smallest representable (normalized) exponent pub fn emin(&self) -> isize { // format only contains regime bits - self.rscale() * self.rmax() + self.rscale() * -self.rmax() } /// Largest representable (unnormalized) exponent @@ -115,17 +113,17 @@ impl PositContext { } /// Maximum representable value. - pub fn maxval(&self) -> Posit { + pub fn maxval(&self, sign: bool) -> Posit { Posit { - num: PositVal::NonZero(false, self.rmax(), 0, Integer::from(1)), + num: PositVal::NonZero(sign, self.rmax(), 0, Integer::from(1)), ctx: self.clone(), } } /// Minimum representable value. - pub fn minval(&self) -> Posit { + pub fn minval(&self, sign: bool) -> Posit { Posit { - num: PositVal::NonZero(false, -self.rmax(), 0, Integer::from(1)), + num: PositVal::NonZero(sign, -self.rmax(), 0, Integer::from(1)), ctx: self.clone(), } } @@ -220,10 +218,72 @@ impl PositContext { } } +// Rounding utility functions. +impl PositContext { + fn round_finite(&self, val: &T) -> Posit { + // extract fields + let s = val.sign(); + let e = val.e().unwrap(); + if e >= self.emax() { + // |val| >= MAXVAL + self.maxval(s) + } else if e <= self.emin() { + // |val| <= MINVAL + self.minval(s) + } else { + // within representable range + + // step 1: compute size of the mantissa since it is dynamic, + // it is a function of the size of the regime field + let useed = self.useed(); + let r = e / useed; + let kbits = if r < 0 { -r } else { r + 1 } as usize; + let embits = self.nbits - (kbits + 2); + let mbits = if embits <= self.es { + 0 + } else { + embits - self.es + }; + + // step 2: rounding as an unbounded, fixed-precision floating-point, + // so we need to compute the context parameters: we use + // precision `mbits + 1` using `NearestTiesToEven` + let (p, n) = RFloatContext::new().with_max_p(mbits + 1).round_params(val); + + // step 3: split the significand at binary digit `n` + let split = RFloatContext::round_prepare(val, n); + + // step 4: finalize the rounding + let rounded = RFloatContext::round_finalize(split, p, RoundingMode::NearestTiesToEven); + + // recompute exponent + let e = rounded.e().unwrap(); + let r = e / useed; + let e = e % useed; + + // unnormalized exponent and significand + let c = rounded.c().unwrap(); + let exp = (e + 1) - (c.significant_bits() as isize); + + // compose result + Posit { + num: PositVal::NonZero(s, r, exp, c), + ctx: self.clone(), + } + } + } +} + impl RoundingContext for PositContext { type Rounded = Posit; fn round(&self, val: &T) -> Self::Rounded { - todo!() + if val.is_zero() { + self.zero() + } else if val.is_nar() { + self.nar() + } else { + self.round_finite(val) + } } } diff --git a/tests/posit.rs b/tests/posit.rs index 4f5f51e..0a19a73 100644 --- a/tests/posit.rs +++ b/tests/posit.rs @@ -1,4 +1,4 @@ -use mpmfnum::{posit::*, rfloat::RFloat, Real}; +use mpmfnum::{posit::*, rfloat::RFloat, Real, RoundingContext}; use rug::Integer; fn bits_to_rfloat(ctx: &PositContext, i: usize) -> RFloat { @@ -126,11 +126,11 @@ fn bounds() { let ctx = PositContext::new(2, 8); assert_eq!(ctx.useed(), 16); assert_eq!( - RFloat::from(ctx.maxval()), + RFloat::from(ctx.maxval(false)), RFloat::Real(false, 24, Integer::from(1)) ); assert_eq!( - RFloat::from(ctx.minval()), + RFloat::from(ctx.minval(false)), RFloat::Real(false, -24, Integer::from(1)) ); @@ -138,11 +138,51 @@ fn bounds() { let ctx = PositContext::new(3, 8); assert_eq!(ctx.useed(), 256); assert_eq!( - RFloat::from(ctx.maxval()), + RFloat::from(ctx.maxval(false)), RFloat::Real(false, 48, Integer::from(1)) ); assert_eq!( - RFloat::from(ctx.minval()), + RFloat::from(ctx.minval(false)), RFloat::Real(false, -48, Integer::from(1)) ); } + +#[test] +fn round_small() { + let ctx = PositContext::new(2, 8); + + // rounding NaN + let nan = RFloat::Nan; + let rounded_nan = ctx.round(&nan); + assert!(rounded_nan.is_nar(), "round(NaN) = NaR"); + + // rounding +Inf + let inf = RFloat::Infinite(true); + let rounded_inf = ctx.round(&inf); + assert!(rounded_inf.is_nar(), "round(+Inf) = NaR"); + + // rounding +Inf + let inf = RFloat::Infinite(false); + let rounded_inf = ctx.round(&inf); + assert!(rounded_inf.is_nar(), "round(+Inf) = NaR"); + + // rounding 0 + let zero = RFloat::zero(); + let rounded_zero = ctx.round(&zero); + assert!(rounded_zero.is_zero(), "round(+0) = +0"); + + // rounding MAXVAL + 1 + let maxp1 = RFloat::one() + RFloat::from(ctx.maxval(false)); + let rounded_maxp1 = ctx.round(&maxp1); + assert_eq!(rounded_maxp1, ctx.maxval(false), "round(MAXVAL+1) = MAXVAL"); + + // rounding +1 + let one = RFloat::one(); + let rounded_one = ctx.round(&one); + assert_eq!(one, RFloat::from(rounded_one), "round(+1) = +1"); + + // rounding +1.0625 + let one_1_16 = RFloat::Real(false, -4, Integer::from(17)); + let rounded = ctx.round(&one_1_16); + assert_eq!(one, RFloat::from(rounded), "round(+1.0625) = +1"); +}