#![no_std]
#[cfg(test)] #[macro_use] extern crate std;
use core::mem;
use core::cmp::Ordering;
pub struct Iter<T: Ieee754> {
from: T,
to: T,
done: bool
}
impl<T: Ieee754> Iterator for Iter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.done { return None }
let x = self.from;
let y = x.next();
if x.bits() == self.to.bits() {
self.done = true;
}
self.from = y;
return Some(x)
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.done {
return (0, Some(0))
}
let high_pos = 8 * mem::size_of::<T>() - 1;
let high_mask = 1 << high_pos;
let from_ = self.from.bits().as_u64();
let (from, from_sign) = (from_ & !high_mask,
from_ & high_mask != 0);
let to_ = self.to.bits().as_u64();
let (to, to_sign) = (to_ & !high_mask,
to_ & high_mask != 0);
let from = if from_sign { -(from as i64) } else { from as i64 };
let to = if to_sign { -(to as i64) } else { to as i64 };
let distance = (to - from + 1) as u64;
if distance <= core::usize::MAX as u64 {
let d = distance as usize;
(d, Some(d))
} else {
(core::usize::MAX, None)
}
}
}
impl<T: Ieee754> DoubleEndedIterator for Iter<T> {
fn next_back(&mut self) -> Option<T> {
if self.done { return None }
let x = self.to;
let y = x.prev();
if x == self.from {
self.done = true;
}
self.to = y;
return Some(x)
}
}
pub trait Bits: Eq + PartialEq + PartialOrd + Ord + Copy {
fn as_u64(self) -> u64;
}
impl Bits for u32 {
fn as_u64(self) -> u64 { self as u64 }
}
impl Bits for u64 {
fn as_u64(self) -> u64 { self }
}
pub trait Ieee754: Copy + PartialEq + PartialOrd {
fn upto(self, lim: Self) -> Iter<Self>;
type Bits: Bits;
type Exponent;
type RawExponent;
type Significand;
fn next(self) -> Self;
fn ulp(self) -> Option<Self>;
fn prev(self) -> Self;
fn bits(self) -> Self::Bits;
fn from_bits(x: Self::Bits) -> Self;
fn exponent_bias() -> Self::Exponent;
fn decompose_raw(self) -> (bool, Self::RawExponent, Self::Significand);
fn recompose_raw(sign: bool, expn: Self::RawExponent, signif: Self::Significand) -> Self;
fn decompose(self) -> (bool, Self::Exponent, Self::Significand);
fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Significand) -> Self;
fn total_cmp(&self, other: &Self) -> Ordering;
}
macro_rules! mask{
($bits: expr; $current: expr => $($other: expr),*) => {
($bits >> (0 $(+ $other)*)) & ((1 << $current) - 1)
}
}
macro_rules! unmask {
($x: expr => $($other: expr),*) => {
$x << (0 $(+ $other)*)
}
}
#[inline]
pub fn abs<F: Ieee754>(x: F) -> F {
let (_, e, s) = x.decompose_raw();
F::recompose_raw(false, e, s)
}
macro_rules! mk_impl {
($f: ident, $bits: ty, $signed_bits: ty,
$expn: ty, $expn_raw: ty, $signif: ty,
$expn_n: expr, $signif_n: expr) => {
impl Ieee754 for $f {
type Bits = $bits;
type Exponent = $expn;
type RawExponent = $expn_raw;
type Significand = $signif;
#[inline]
fn upto(self, lim: Self) -> Iter<Self> {
assert!(self <= lim);
#[inline(always)]
fn canon(x: $f) -> $f { if x == 0.0 { 0.0 } else { x } }
Iter {
from: canon(self),
to: canon(lim),
done: false,
}
}
#[inline]
fn ulp(self) -> Option<Self> {
let (_sign, expn, _signif) = self.decompose_raw();
const MAX_EXPN: $expn_raw = ((1u64 << $expn_n) - 1) as $expn_raw;
match expn {
MAX_EXPN => None,
0 => Some($f::recompose_raw(false, 0, 1)),
_ => {
let ulp_expn = expn.saturating_sub($signif_n);
if ulp_expn == 0 {
Some($f::recompose_raw(false, 0, 1 << (expn - 1)))
} else {
Some($f::recompose_raw(false, ulp_expn, 0))
}
}
}
}
#[inline]
fn next(self) -> Self {
let abs_mask = (!(0 as Self::Bits)) >> 1;
let mut bits = self.bits();
if self == 0.0 {
bits = 1;
} else if self < 0.0 {
bits -= 1;
if bits == !abs_mask {
bits = 0
}
} else {
bits += 1
}
Ieee754::from_bits(bits)
}
#[inline]
fn prev(self) -> Self {
let abs_mask = (!(0 as Self::Bits)) >> 1;
let mut bits = self.bits();
if self < 0.0 {
bits += 1;
} else if bits & abs_mask == 0 {
bits = 1 | !abs_mask;
} else {
bits -= 1;
}
Ieee754::from_bits(bits)
}
#[inline]
fn exponent_bias() -> Self::Exponent {
(1 << ($expn_n - 1)) - 1
}
#[inline]
fn bits(self) -> Self::Bits {
unsafe {mem::transmute(self)}
}
#[inline]
fn from_bits(bits: Self::Bits) -> Self {
unsafe {mem::transmute(bits)}
}
#[inline]
fn decompose_raw(self) -> (bool, Self::RawExponent, Self::Significand) {
let bits = self.bits();
(mask!(bits; 1 => $expn_n, $signif_n) != 0,
mask!(bits; $expn_n => $signif_n) as Self::RawExponent,
mask!(bits; $signif_n => ) as Self::Significand)
}
#[inline]
fn recompose_raw(sign: bool, expn: Self::RawExponent, signif: Self::Significand) -> Self {
Ieee754::from_bits(
unmask!(sign as Self::Bits => $expn_n, $signif_n) |
unmask!(expn as Self::Bits => $signif_n) |
unmask!(signif as Self::Bits => ))
}
#[inline]
fn decompose(self) -> (bool, Self::Exponent, Self::Significand) {
let (sign, expn, signif) = self.decompose_raw();
(sign, expn as Self::Exponent - Self::exponent_bias(),
signif)
}
#[inline]
fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Significand) -> Self {
Self::recompose_raw(sign,
(expn + Self::exponent_bias()) as Self::RawExponent,
signif)
}
#[inline]
fn total_cmp(&self, other: &Self) -> Ordering {
#[inline]
fn cmp_key(x: $f) -> $signed_bits {
let bits = x.bits();
let sign_bit = bits & (1 << ($expn_n + $signif_n));
let mask = ((sign_bit as $signed_bits) >> ($expn_n + $signif_n)) as $bits >> 1;
(bits ^ mask) as $signed_bits
}
cmp_key(*self).cmp(&cmp_key(*other))
}
}
#[cfg(test)]
mod $f {
use std::prelude::v1::*;
use std::$f;
use {Ieee754, abs};
#[test]
fn upto() {
assert_eq!((0.0 as $f).upto(0.0).collect::<Vec<_>>(),
&[0.0]);
assert_eq!($f::recompose(false, 1, 1).upto($f::recompose(false, 1, 10)).count(),
10);
assert_eq!($f::recompose(true, -$f::exponent_bias(), 10)
.upto($f::recompose(false, -$f::exponent_bias(), 10)).count(),
21);
}
#[test]
fn upto_rev() {
assert_eq!(0.0_f32.upto(0.0_f32).rev().collect::<Vec<_>>(),
&[0.0]);
assert_eq!($f::recompose(false, 1, 1)
.upto($f::recompose(false, 1, 10)).rev().count(),
10);
assert_eq!($f::recompose(true, -$f::exponent_bias(), 10)
.upto($f::recompose(false, -$f::exponent_bias(), 10)).rev().count(),
21);
}
#[test]
fn upto_infinities() {
use std::$f as f;
assert_eq!(f::MAX.upto(f::INFINITY).collect::<Vec<_>>(),
&[f::MAX, f::INFINITY]);
assert_eq!(f::NEG_INFINITY.upto(f::MIN).collect::<Vec<_>>(),
&[f::NEG_INFINITY, f::MIN]);
}
#[test]
fn upto_infinities_rev() {
use std::$f as f;
assert_eq!(f::MAX.upto(f::INFINITY).rev().collect::<Vec<_>>(),
&[f::INFINITY, f::MAX]);
assert_eq!(f::NEG_INFINITY.upto(f::MIN).rev().collect::<Vec<_>>(),
&[f::MIN, f::NEG_INFINITY]);
}
#[test]
fn upto_size_hint() {
let mut iter =
$f::recompose(true, -$f::exponent_bias(), 10)
.upto($f::recompose(false, -$f::exponent_bias(), 10));
assert_eq!(iter.size_hint(), (21, Some(21)));
for i in (0..21).rev() {
assert!(iter.next().is_some());
assert_eq!(iter.size_hint(), (i, Some(i)));
}
assert_eq!(iter.next(), None);
assert_eq!(iter.size_hint(), (0, Some(0)))
}
#[test]
fn upto_size_hint_rev() {
let mut iter =
$f::recompose(true, -$f::exponent_bias(), 10)
.upto($f::recompose(false, -$f::exponent_bias(), 10))
.rev();
assert_eq!(iter.size_hint(), (21, Some(21)));
for i in (0..21).rev() {
assert!(iter.next().is_some());
assert_eq!(iter.size_hint(), (i, Some(i)));
}
assert_eq!(iter.next(), None);
assert_eq!(iter.size_hint(), (0, Some(0)))
}
#[test]
fn next_prev_order() {
let cases = [0.0 as $f, -0.0, 1.0, 1.0001, 1e30, -1.0, -1.0001, -1e30];
for &x in &cases {
assert!(x.next() > x);
assert!(x.prev() < x);
}
}
#[test]
fn ulp_smoke() {
let smallest_subnormal = $f::recompose_raw(false, 0, 1);
let smallest_normal = $f::recompose_raw(false, 1, 0);
assert_eq!((0.0 as $f).ulp(), Some(smallest_subnormal));
assert_eq!(smallest_subnormal.ulp(), Some(smallest_subnormal));
assert_eq!($f::recompose_raw(true, 0, 9436).ulp(),
Some(smallest_subnormal));
assert_eq!(smallest_normal.ulp(), Some(smallest_subnormal));
assert_eq!((1.0 as $f).ulp(),
Some($f::recompose(false, -$signif_n, 0)));
assert_eq!((-123.456e30 as $f).ulp(),
Some($f::recompose(false, 106 - $signif_n, 0)));
assert_eq!($f::INFINITY.ulp(), None);
assert_eq!($f::NEG_INFINITY.ulp(), None);
assert_eq!($f::NAN.ulp(), None);
}
#[test]
fn ulp_aggressive() {
fn check_ulp(x: $f, ulp: $f) {
println!(" {:e} {:e}", x, ulp);
assert_eq!(x.ulp(), Some(ulp));
let same_sign_ulp = if x < 0.0 { -ulp } else { ulp };
assert_ne!(x + same_sign_ulp, x, "adding ulp should be different");
if ulp / 2.0 > 0.0 {
if x.decompose().2 & 1 == 0 {
assert_eq!(x + same_sign_ulp / 2.0, x);
} else {
assert_eq!(x + same_sign_ulp / 2.0, x + same_sign_ulp);
}
}
assert_eq!(x + same_sign_ulp / 4.0, x);
}
let smallest_subnormal = $f::recompose_raw(false, 0, 1);
let mut ulp = smallest_subnormal;
check_ulp(0.0, ulp);
let mut pow2 = smallest_subnormal;
for i in 0..200 {
println!("{}", i);
check_ulp(pow2, ulp);
check_ulp(-pow2, ulp);
let (_, e, _) = pow2.decompose_raw();
if e > 0 {
for &signif in &[1,
9436, 1577069,
(1 << $signif_n) - 2, (1 << $signif_n) - 1] {
check_ulp($f::recompose_raw(false, e, signif), ulp);
check_ulp($f::recompose_raw(true, e, signif), ulp);
}
}
pow2 *= 2.0;
if i >= $signif_n {
ulp *= 2.0;
}
}
}
#[test]
fn test_abs() {
assert!(abs($f::NAN).is_nan());
let cases = [0.0 as $f, -1.0, 1.0001,
$f::recompose_raw(false, 0, 123), $f::recompose(true, 0, 123),
$f::NEG_INFINITY, $f::INFINITY];
for x in &cases {
assert_eq!(abs(*x), x.abs());
}
}
#[test]
fn total_cmp() {
let nan_exp = $f::NAN.decompose_raw().1;
let q = 1 << ($signif_n - 1);
let qnan0 = $f::recompose_raw(false, nan_exp, q);
let qnan1 = $f::recompose_raw(false, nan_exp, q | 1);
let qnanlarge = $f::recompose_raw(false, nan_exp, q | (q - 1));
let snan1 = $f::recompose_raw(false, nan_exp, 1);
let snan2 = $f::recompose_raw(false, nan_exp, 2);
let snanlarge = $f::recompose_raw(false, nan_exp, q - 1);
let subnormal = $f::recompose_raw(false, 0, 1);
let include_snan = cfg!(not(target_arch = "x86"));
let mut cases = vec![-qnanlarge, -qnan1, -qnan0];
if include_snan {
cases.extend_from_slice(&[-snanlarge, -snan2, -snan1]);
}
cases.extend_from_slice(&[
$f::NEG_INFINITY,
-1e15, -1.001, -1.0, -0.999, -1e-15, -subnormal,
-0.0, 0.0,
subnormal, 1e-15, 0.999, 1.0, 1.001, 1e15,
$f::INFINITY
]);
if include_snan {
cases.extend_from_slice(&[snan1, snan2, snanlarge]);
}
cases.extend_from_slice(&[qnan0, qnan1, qnanlarge]);
for (ix, &x) in cases.iter().enumerate() {
for (iy, &y) in cases.iter().enumerate() {
let computed = x.total_cmp(&y);
let expected = ix.cmp(&iy);
assert_eq!(
computed, expected,
"{:e} ({}, {:?}) cmp {:e} ({}, {:?}), got: {:?}, expected: {:?}",
x, ix, x.decompose(),
y, iy, y.decompose(),
computed, expected);
}
}
}
}
}
}
mk_impl!(f32, u32, i32, i16, u8, u32, 8, 23);
mk_impl!(f64, u64, i64, i16, u16, u64, 11, 52);