#![cfg_attr(all(test, feature = "unstable"), feature(test))]
#[cfg(all(test, feature = "unstable"))] extern crate test;
use std::mem;
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)
}
}
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 Ieee754: Copy + PartialEq + PartialOrd {
fn upto(self, lim: Self) -> Iter<Self>;
type Bits: Eq + PartialEq + PartialOrd + Ord + Copy;
type Exponent;
type Signif;
fn next(self) -> Self;
fn prev(self) -> Self;
fn bits(self) -> Self::Bits;
fn from_bits(x: Self::Bits) -> Self;
fn exponent_bias(self) -> Self::Exponent;
fn decompose(self) -> (bool, Self::Exponent, Self::Signif);
fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Signif) -> Self;
}
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)*)
}
}
macro_rules! mk_impl {
($f: ident, $bits: ty, $expn: ty, $signif: ty,
$expn_n: expr, $signif_n: expr) => {
impl Ieee754 for $f {
type Bits = $bits;
type Exponent = $expn;
type Signif = $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 next(self) -> Self {
let abs_mask = (!(0 as Self::Bits)) >> 1;
let (sign, _expn, _signif) = self.decompose();
let mut bits = self.bits();
if sign {
bits -= 1;
if bits == !abs_mask {
bits = 0
}
} else {
bits += 1
}
Self::from_bits(bits)
}
#[inline]
fn prev(self) -> Self {
let abs_mask = (!(0 as Self::Bits)) >> 1;
let (sign, _expn, _signif) = self.decompose();
let mut bits = self.bits();
if sign {
bits += 1;
} else if bits & abs_mask == 0 {
bits = 1 | !abs_mask;
} else {
bits -= 1;
}
Self::from_bits(bits)
}
#[inline]
fn exponent_bias(self) -> 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(self) -> (bool, Self::Exponent, Self::Signif) {
let bits = self.bits();
(mask!(bits; 1 => $expn_n, $signif_n) != 0,
mask!(bits; $expn_n => $signif_n) as Self::Exponent,
mask!(bits; $signif_n => ) as Self::Signif)
}
#[inline]
fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Signif) -> Self {
Self::from_bits(
unmask!(sign as Self::Bits => $expn_n, $signif_n) |
unmask!(expn as Self::Bits => $signif_n) |
unmask!(signif as Self::Bits => ))
}
}
#[cfg(test)]
mod $f {
use Ieee754;
#[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, 0, 10).upto($f::recompose(false, 0, 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, 0, 10)
.upto($f::recompose(false, 0, 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]);
}
#[cfg(all(test, feature = "unstable"))]
mod benches {
use test::{Bencher, black_box};
use Ieee754;
#[bench]
fn iter_pos(b: &mut Bencher) {
let (_, expn, _) = $f::decompose(1.0);
let end = $f::recompose(false, expn, 40);
b.iter(|| {
assert_eq!(black_box(1.0 as $f).upto(black_box(end))
.map(black_box).count(),
41);
})
}
#[bench]
fn iter_over_zero(b: &mut Bencher) {
let x = $f::recompose(false, 0, 20);
b.iter(|| {
assert_eq!(black_box(-x).upto(black_box(x)).map(black_box).count(),
41);
})
}
#[bench]
fn baseline(b: &mut Bencher) {
b.iter(|| {
assert_eq!((black_box(0 as $bits)..black_box(41)).map(black_box).count(),
41);
})
}
}
}
}
}
mk_impl!(f32, u32, u8, u32, 8, 23);
mk_impl!(f64, u64, u16, u64, 11, 52);