ieee754 0.1.1

Low-level manipulations of IEEE754 floating-point numbers.
Documentation
//! Low-level manipulations of IEEE754 floating-point numbers.
//!
//! # Installation
//!
//! Add this to your Cargo.toml
//!
//! ```toml
//! [dependencies]
//! ieee754 = "0.1"
//! ```
//!
//! # Examples
//!
//! ```rust
//! use ieee754::Ieee754;
//!
//! // there are 840 single-precision floats in between 1.0 and 1.0001
//! // (inclusive).
//! assert_eq!(1_f32.upto(1.0001).count(), 840);
//! ```

#![cfg_attr(all(test, feature = "unstable"), feature(test))]
#[cfg(all(test, feature = "unstable"))] extern crate test;

use std::mem;

/// An iterator over floating point numbers.
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();
        // we've canonicalised negative zero to positive zero, and
        // we're guaranteed that neither is NaN, so comparing bitwise
        // is valid (and 20% faster for the `all` example).
        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)
    }
}

/// Types that are IEEE754 floating point numbers.
pub trait Ieee754: Copy + PartialEq + PartialOrd {
    /// Iterate over each value of `T` in `[self, lim]`.
    ///
    /// # Panics
    ///
    /// Panics if `self > lim`, or if either are NaN.
    ///
    /// # Example
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// // there are 840 single-precision floats in between 1.0 and 1.0001
    /// // (inclusive).
    /// assert_eq!(1_f32.upto(1.0001).count(), 840);
    /// ```
    fn upto(self, lim: Self) -> Iter<Self>;

    /// A type that represents the raw bits of `Self`.
    type Bits: Eq + PartialEq + PartialOrd + Ord + Copy;
    /// A type large enough to store the exponent of `Self`.
    type Exponent;
    /// A type large enough to store the significand of `Self`.
    type Signif;

    /// Return the next value after `self`.
    ///
    /// Calling this on NaN will yield nonsense.
    fn next(self) -> Self;

    /// Return the previous value before `self`.
    ///
    /// Calling this on NaN will yield nonsense.
    fn prev(self) -> Self;
    /// View `self` as a collection of bits.
    fn bits(self) -> Self::Bits;
    /// View a collections of bits as a floating point number.
    fn from_bits(x: Self::Bits) -> Self;
    /// Get the bias of the stored exponent.
    fn exponent_bias(self) -> Self::Exponent;
    /// Break `self` into the three constituent parts of an IEEE754 float.
    ///
    /// The exponent returned is the raw bits, use `exponent_bias` to
    /// compute the offset required.
    fn decompose(self) -> (bool, Self::Exponent, Self::Signif);
    /// Create a `Self` out of the three constituent parts of an IEEE754 float.
    ///
    /// The exponent should be the raw bits, use `exponent_bias` to
    /// compute the offset required.
    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);
                // map -0.0 to 0.0, i.e. ensure that any zero is
                // stored in a canonical manner. This is necessary to
                // use bit-hacks for the comparison in next.
                #[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 {
                        // normalise -0.0 to +0.0
                        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);