Skip to content

Commit

Permalink
posit rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
bksaiki committed Oct 11, 2023
1 parent efc72a9 commit 3191fa0
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 14 deletions.
40 changes: 40 additions & 0 deletions src/posit/number.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp::Ordering;

use num_traits::{One, Zero};
use rug::Integer;

Expand Down Expand Up @@ -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<Ordering> {
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<Posit> for RFloat {
fn from(value: Posit) -> Self {
match value.num {
Expand Down
78 changes: 69 additions & 9 deletions src/posit/round.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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
Expand All @@ -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(),
}
}
Expand Down Expand Up @@ -220,10 +218,72 @@ impl PositContext {
}
}

// Rounding utility functions.
impl PositContext {
fn round_finite<T: Real>(&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<T: Real>(&self, val: &T) -> Self::Rounded {
todo!()
if val.is_zero() {
self.zero()
} else if val.is_nar() {
self.nar()
} else {
self.round_finite(val)
}
}
}
50 changes: 45 additions & 5 deletions tests/posit.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -126,23 +126,63 @@ 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))
);

// posit<3, 8> format
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");
}

0 comments on commit 3191fa0

Please sign in to comment.