ieee754 0.2.5

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.2"
//! ```
//!
//! # Examples
//!
//! ```rust
//! use ieee754::Ieee754;
//!
//! // there are 840 single-precision floats between 1.0 and 1.0001
//! // (inclusive).
//! assert_eq!(1_f32.upto(1.0001).count(), 840);
//! ```

#![no_std]
#[cfg(test)] #[macro_use] extern crate std;

use core::mem;
use core::cmp::Ordering;

/// An iterator over floating point numbers, created by `Ieee754::upto`.
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)
    }

    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 }
}

/// Types that are IEEE754 floating point numbers.
pub trait Ieee754: Copy + PartialEq + PartialOrd {
    /// Iterate over each value of `Self` in `[self, lim]`.
    ///
    /// The returned iterator will include subnormal numbers, and will
    /// only include one of `-0.0` and `0.0`.
    ///
    /// # Panics
    ///
    /// Panics if `self > lim`, or if either are NaN.
    ///
    /// # 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);
    /// ```
    fn upto(self, lim: Self) -> Iter<Self>;

    /// A type that represents the raw bits of `Self`.
    type Bits: Bits;
    /// A type large enough to store the true exponent of `Self`.
    type Exponent;
    /// A type large enough to store the raw exponent (i.e. with the bias).
    type RawExponent;
    /// A type large enough to store the significand of `Self`.
    type Significand;

    /// Return the next value after `self`.
    ///
    /// Calling this on NaN or positive infinity will yield nonsense.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    /// let x: f32 = 1.0;
    /// assert_eq!(x.next(), 1.000000119209);
    /// ```
    fn next(self) -> Self;

    /// Return the unit-in-the-last-place ulp of `self`. That is,
    /// `x.abs().next() - x.abs()`, but handling overflow properly.
    ///
    /// Returns `None` if `self` is not finite.
    fn ulp(self) -> Option<Self>;

    /// Return the previous value before `self`.
    ///
    /// Calling this on NaN or negative infinity will yield nonsense.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    /// let x: f32 = 1.0;
    /// assert_eq!(x.prev(), 0.99999995);
    /// ```
    fn prev(self) -> Self;
    /// View `self` as a collection of bits.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    /// let x: f32 = 1.0;
    /// assert_eq!(x.bits(), 0x3f80_0000);
    /// ```
    fn bits(self) -> Self::Bits;
    /// View a collections of bits as a floating point number.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    /// let float: f32 = Ieee754::from_bits(0xbf80_0000);
    /// assert_eq!(float, -1.0);
    /// ```
    fn from_bits(x: Self::Bits) -> Self;
    /// Get the bias of the stored exponent.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(f32::exponent_bias(), 127);
    /// assert_eq!(f64::exponent_bias(), 1023);
    /// ```
    fn exponent_bias() -> 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 or use `decompose` to obtain this
    /// in precomputed form.
    ///
    /// # Examples
    ///
    /// Single precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(1_f32.decompose_raw(), (false, 127, 0));
    /// assert_eq!(1234.567_f32.decompose_raw(), (false, 137, 0x1a5225));
    ///
    /// assert_eq!((-0.525_f32).decompose_raw(), (true, 126, 0x66666));
    ///
    /// assert_eq!(std::f32::INFINITY.decompose_raw(), (false, 255, 0));
    ///
    /// let (sign, expn, signif) = std::f32::NAN.decompose_raw();
    /// assert_eq!((sign, expn), (false, 255));
    /// assert!(signif != 0);
    /// ```
    ///
    /// Double precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(1_f64.decompose_raw(), (false, 1023, 0));
    /// assert_eq!(1234.567_f64.decompose_raw(), (false, 1033, 0x34a449ba5e354));
    ///
    /// assert_eq!((-0.525_f64).decompose_raw(), (true, 1022, 0xcccc_cccc_cccd));
    ///
    /// assert_eq!(std::f64::INFINITY.decompose_raw(), (false, 2047, 0));
    ///
    /// let (sign, expn, signif) = std::f64::NAN.decompose_raw();
    /// assert_eq!((sign, expn), (false, 2047));
    /// assert!(signif != 0);
    /// ```
    fn decompose_raw(self) -> (bool, Self::RawExponent, Self::Significand);

    /// 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, or use `recompose` to feed in the
    /// unbiased exponent.
    ///
    /// # Examples
    ///
    /// Single precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(f32::recompose_raw(false, 127, 0), 1.0);
    /// assert_eq!(f32::recompose_raw(false, 137, 0x1a5225), 1234.567);
    /// assert_eq!(f32::recompose_raw(true, 126, 0x66666), -0.525);
    ///
    /// assert_eq!(f32::recompose_raw(false, 255, 0), std::f32::INFINITY);
    ///
    /// assert!(f32::recompose_raw(false, 255, 1).is_nan());
    /// ```
    ///
    /// Double precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(f64::recompose_raw(false, 1023, 0), 1.0);
    /// assert_eq!(f64::recompose_raw(false, 1033, 0x34a449ba5e354), 1234.567);
    /// assert_eq!(f64::recompose_raw(true, 1022, 0xcccc_cccc_cccd), -0.525);
    ///
    /// assert_eq!(f64::recompose_raw(false, 2047, 0), std::f64::INFINITY);
    ///
    /// assert!(f64::recompose_raw(false, 2047, 1).is_nan());
    /// ```
    fn recompose_raw(sign: bool, expn: Self::RawExponent, signif: Self::Significand) -> Self;

    /// Break `self` into the three constituent parts of an IEEE754 float.
    ///
    /// The exponent returned is the true exponent, after accounting
    /// for the bias it is stored with. The significand does not
    /// include the implicit highest bit (if it exists), e.g. the
    /// 24-bit for single precision.
    ///
    /// # Examples
    ///
    /// Single precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(1_f32.decompose(), (false, 0, 0));
    /// assert_eq!(1234.567_f32.decompose(), (false, 10, 0x1a5225));
    ///
    /// assert_eq!((-0.525_f32).decompose(), (true, -1, 0x66666));
    ///
    /// assert_eq!(std::f32::INFINITY.decompose(), (false, 128, 0));
    ///
    /// let (sign, expn, signif) = std::f32::NAN.decompose();
    /// assert_eq!((sign, expn), (false, 128));
    /// assert!(signif != 0);
    /// ```
    ///
    /// Double precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(1_f64.decompose(), (false, 0, 0));
    /// assert_eq!(1234.567_f64.decompose(), (false, 10, 0x34a449ba5e354));
    ///
    /// assert_eq!((-0.525_f64).decompose(), (true, -1, 0xcccc_cccc_cccd));
    ///
    /// assert_eq!(std::f64::INFINITY.decompose(), (false, 1024, 0));
    ///
    /// let (sign, expn, signif) = std::f64::NAN.decompose();
    /// assert_eq!((sign, expn), (false, 1024));
    /// assert!(signif != 0);
    /// ```
    fn decompose(self) -> (bool, Self::Exponent, Self::Significand);

    /// Create a `Self` out of the three constituent parts of an IEEE754 float.
    ///
    /// The exponent should be true exponent, not accounting for any
    /// bias. The significand should not include the implicit highest
    /// bit (if it exists), e.g. the 24-th bit for signle precision.
    ///
    /// # Examples
    ///
    /// Single precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(f32::recompose(false, 0, 0), 1.0);
    /// assert_eq!(f32::recompose(false, 10, 0x1a5225), 1234.567);
    /// assert_eq!(f32::recompose(true, -1, 0x66666), -0.525);
    ///
    /// assert_eq!(f32::recompose(false, 128, 0), std::f32::INFINITY);
    ///
    /// assert!(f32::recompose(false, 128, 1).is_nan());
    /// ```
    ///
    /// Double precision:
    ///
    /// ```rust
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(f64::recompose(false, 0, 0), 1.0);
    /// assert_eq!(f64::recompose(false, 10, 0x34a449ba5e354), 1234.567);
    /// assert_eq!(f64::recompose(true, -1, 0xcccc_cccc_cccd), -0.525);
    ///
    /// assert_eq!(f64::recompose(false, 1024, 0), std::f64::INFINITY);
    ///
    /// assert!(f64::recompose(false, 1024, 1).is_nan());
    /// ```
    fn recompose(sign: bool, expn: Self::Exponent, signif: Self::Significand) -> Self;

    /// Compare `x` and `y` using the IEEE-754 `totalOrder` predicate
    /// (Section 5.10).
    ///
    /// This orders NaNs before or after all non-NaN floats, depending
    /// on the sign bit. Using -qNaN to represent a quiet NaN with
    /// negative sign bit and similarly for a signalling NaN (sNaN),
    /// the order is:
    ///
    /// ```txt
    /// -qNaN < -sNaN < -∞ < -12.34 < -0.0 < +0.0 < +12.34 < +∞ < +sNaN < +qNaN
    /// ```
    ///
    /// (NaNs are ordered according to their payload.)
    ///
    /// # Examples
    ///
    /// Single precision:
    ///
    /// ```rust
    /// use std::cmp::Ordering;
    /// use std::f32;
    ///
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(0_f32.total_cmp(&0_f32), Ordering::Equal);
    /// assert_eq!(0_f32.total_cmp(&-0_f32), Ordering::Greater);
    /// assert_eq!(0_f32.total_cmp(&1_f32), Ordering::Less);
    /// assert_eq!(1e10_f32.total_cmp(&f32::NEG_INFINITY), Ordering::Greater);
    /// assert_eq!(f32::NAN.total_cmp(&0_f32), Ordering::Greater);
    /// assert_eq!(f32::NAN.total_cmp(&f32::INFINITY), Ordering::Greater);
    /// assert_eq!((-f32::NAN).total_cmp(&f32::NEG_INFINITY), Ordering::Less);
    /// ```
    ///
    /// Double precision:
    ///
    /// ```rust
    /// use std::cmp::Ordering;
    /// use std::f64;
    ///
    /// use ieee754::Ieee754;
    ///
    /// assert_eq!(0_f64.total_cmp(&0_f64), Ordering::Equal);
    /// assert_eq!(0_f64.total_cmp(&-0_f64), Ordering::Greater);
    /// assert_eq!(0_f64.total_cmp(&1_f64), Ordering::Less);
    /// assert_eq!(1e10_f64.total_cmp(&f64::NEG_INFINITY), Ordering::Greater);
    /// assert_eq!(f64::NAN.total_cmp(&0_f64), Ordering::Greater);
    /// assert_eq!(f64::NAN.total_cmp(&f64::INFINITY), Ordering::Greater);
    /// assert_eq!((-f64::NAN).total_cmp(&f64::NEG_INFINITY), Ordering::Less);
    /// ```
    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)*)
    }
}

/// Return the absolute value of `x`.
///
/// This provides a no_std/core-only version of the built-in `abs` in
/// `std`, until
/// [#50145](https://github.com/rust-lang/rust/issues/50145) is
/// addressed.
///
/// ```rust
/// use std::{f32, f64};
///
/// assert_eq!(ieee754::abs(0_f32), 0.0);
/// assert_eq!(ieee754::abs(0_f64), 0.0);
///
/// assert_eq!(ieee754::abs(12.34_f32), 12.34);
/// assert_eq!(ieee754::abs(-12.34_f64), 12.34);
///
/// assert!(ieee754::abs(f32::NAN).is_nan());
/// assert_eq!(ieee754::abs(f64::NEG_INFINITY), f64::INFINITY);
/// ```
#[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);
                // 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 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 {
                        // normalise -0.0 to +0.0
                        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));
                    // with signed-magnitude we need to be moving away
                    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 {
                        // floats break ties like this by rounding to
                        // even (in the default mode), so adding half
                        // a ulp may be a new value depending on the
                        // significand.
                        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);
                        }
                    }
                    // no ties to worry about
                    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,
                                         // random numbers
                                         9436, 1577069,
                                         // last two for this exponent
                                         (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,
                             // denormals
                             $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);

                // it's a total order, so we can literally write
                // options in order, and compare them all, using their
                // indices as ground-truth. NB. the snans seem to
                // get canonicalized to qnan on some versions of i686
                // Linux (using `cross` on Travis CI), so we can't
                // include them.
                let include_snan = cfg!(not(target_arch = "x86"));
                // -qNaN
                let mut cases = vec![-qnanlarge, -qnan1, -qnan0];
                // -sNaN
                if include_snan {
                    cases.extend_from_slice(&[-snanlarge, -snan2, -snan1]);
                }
                // Numbers (note -0, +0)
                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
                ]);
                // +sNaN
                if include_snan {
                    cases.extend_from_slice(&[snan1, snan2, snanlarge]);
                }
                // +qNaN
                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);