// Copyright © 2018–2025 Trevor Spiteri // This library is free software: you can redistribute it and/or // modify it under the terms of either // // * the Apache License, Version 2.0 or // * the MIT License // // at your option. // // You should have recieved copies of the Apache License and the MIT // License along with the library. If not, see // and // . #![allow(deprecated)] use crate::float_helper; use crate::int_helper; use crate::int_helper::IntFixed; use crate::types::extra::Unsigned; use crate::{ F128, F128Bits, FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64, FixedU128, }; use core::cmp::Ordering; use core::ops::{Shl, Shr}; use half::{bf16 as half_bf16, f16 as half_f16}; macro_rules! fixed_cmp_int { ($Fixed:ident, $Int:ident) => { impl PartialEq<$Int> for $Fixed { #[inline] fn eq(&self, rhs: &$Int) -> bool { let fixed_rhs = IntFixed(*rhs).fixed(); PartialEq::eq(self, &fixed_rhs) } } impl PartialEq<$Fixed> for $Int { #[inline] fn eq(&self, rhs: &$Fixed) -> bool { let fixed_lhs = IntFixed(*self).fixed(); PartialEq::eq(&fixed_lhs, rhs) } } impl PartialOrd<$Int> for $Fixed { #[inline] fn partial_cmp(&self, rhs: &$Int) -> Option { let fixed_rhs = IntFixed(*rhs).fixed(); PartialOrd::partial_cmp(self, &fixed_rhs) } #[inline] fn lt(&self, rhs: &$Int) -> bool { let fixed_rhs = IntFixed(*rhs).fixed(); PartialOrd::lt(self, &fixed_rhs) } #[inline] fn le(&self, rhs: &$Int) -> bool { let fixed_rhs = IntFixed(*rhs).fixed(); PartialOrd::le(self, &fixed_rhs) } #[inline] fn gt(&self, rhs: &$Int) -> bool { let fixed_rhs = IntFixed(*rhs).fixed(); PartialOrd::gt(self, &fixed_rhs) } #[inline] fn ge(&self, rhs: &$Int) -> bool { let fixed_rhs = IntFixed(*rhs).fixed(); PartialOrd::ge(self, &fixed_rhs) } } impl PartialOrd<$Fixed> for $Int { #[inline] fn partial_cmp(&self, rhs: &$Fixed) -> Option { let fixed_lhs = IntFixed(*self).fixed(); PartialOrd::partial_cmp(&fixed_lhs, rhs) } #[inline] fn lt(&self, rhs: &$Fixed) -> bool { let fixed_lhs = IntFixed(*self).fixed(); PartialOrd::lt(&fixed_lhs, rhs) } #[inline] fn le(&self, rhs: &$Fixed) -> bool { let fixed_lhs = IntFixed(*self).fixed(); PartialOrd::le(&fixed_lhs, rhs) } #[inline] fn gt(&self, rhs: &$Fixed) -> bool { let fixed_lhs = IntFixed(*self).fixed(); PartialOrd::gt(&fixed_lhs, rhs) } #[inline] fn ge(&self, rhs: &$Fixed) -> bool { let fixed_lhs = IntFixed(*self).fixed(); PartialOrd::ge(&fixed_lhs, rhs) } } }; } // Zero must NOT be neg struct Value { neg: bool, abs: U, bits: u32, frac_bits: i32, } #[inline] // lhs_frac >= rhs_frac fn float_rhs_shl(rhs_abs: U, bits: u32, lhs_frac: i32, rhs_frac: i32) -> Option where U: Copy + Eq + TryFrom + Shl + Shr, { debug_assert!(lhs_frac >= rhs_frac); let rhs_shl = lhs_frac.wrapping_sub(rhs_frac) as u32; let Ok(rhs_zero) = U::try_from(0u32) else { unreachable!(); }; if rhs_abs == rhs_zero { Some(rhs_zero) } else if rhs_shl >= bits { None } else { let shifted = rhs_abs << rhs_shl; if (shifted >> rhs_shl) == rhs_abs { Some(shifted) } else { None } } } #[inline] fn float_eq_even(lhs: Value, rhs: Value) -> bool where U: Copy + Eq + TryFrom + Shl + Shr, { if lhs.frac_bits < rhs.frac_bits { return float_eq_even(rhs, lhs); } if lhs.neg != rhs.neg { return false; } // lhs.frac_bits >= rhs.frac_bits match float_rhs_shl(rhs.abs, rhs.bits, lhs.frac_bits, rhs.frac_bits) { None => false, Some(shifted_rhs_abs) => lhs.abs == shifted_rhs_abs, } } #[inline] fn float_eq(lhs: Value, rhs: Value) -> bool where Lhs: Copy + Eq + TryFrom + TryFrom + Shl + Shr, Rhs: Copy + Eq + TryFrom + TryFrom + Shl + Shr, { if lhs.bits >= rhs.bits { let Ok(rhs_abs) = Lhs::try_from(rhs.abs) else { unreachable!(); }; let rhs = Value { neg: rhs.neg, abs: rhs_abs, bits: lhs.bits, frac_bits: rhs.frac_bits, }; float_eq_even(lhs, rhs) } else { let Ok(lhs_abs) = Rhs::try_from(lhs.abs) else { unreachable!(); }; let lhs = Value { neg: lhs.neg, abs: lhs_abs, bits: rhs.bits, frac_bits: lhs.frac_bits, }; float_eq_even(lhs, rhs) } } #[inline] fn float_cmp_even(lhs: Value, rhs: Value) -> Ordering where U: Copy + Ord + TryFrom + Shl + Shr, { if lhs.frac_bits < rhs.frac_bits { return float_cmp_even(rhs, lhs).reverse(); } if !lhs.neg && rhs.neg { return Ordering::Greater; } if lhs.neg && !rhs.neg { return Ordering::Less; } match float_rhs_shl(rhs.abs, rhs.bits, lhs.frac_bits, rhs.frac_bits) { None => { // rhs is so large it doesn't fit if lhs.neg { // both lhs and rhs are negative, and rhs is even more negative Ordering::Greater } else { Ordering::Less } } Some(shifted_rhs_abs) => { if lhs.neg { // both lhs are negative, so reverse order shifted_rhs_abs.cmp(&lhs.abs) } else { lhs.abs.cmp(&shifted_rhs_abs) } } } } #[inline] fn float_cmp(lhs: Value, rhs: Value) -> Ordering where Lhs: Copy + Ord + TryFrom + TryFrom + Shl + Shr, Rhs: Copy + Ord + TryFrom + TryFrom + Shl + Shr, { if lhs.bits >= rhs.bits { let Ok(rhs_abs) = Lhs::try_from(rhs.abs) else { unreachable!(); }; let rhs = Value { neg: rhs.neg, abs: rhs_abs, bits: lhs.bits, frac_bits: rhs.frac_bits, }; float_cmp_even(lhs, rhs) } else { let Ok(lhs_abs) = Rhs::try_from(lhs.abs) else { unreachable!(); }; let lhs = Value { neg: lhs.neg, abs: lhs_abs, bits: rhs.bits, frac_bits: lhs.frac_bits, }; float_cmp_even(lhs, rhs) } } macro_rules! fixed_cmp_float { ($Fix:ident($Inner:ident), $Float:ident, $FloatBits:ident) => { impl PartialEq<$Float> for $Fix { #[inline] fn eq(&self, rhs: &$Float) -> bool { use float_helper::$Float::Kind; let (lhs_neg, lhs_abs) = int_helper::$Inner::neg_abs(self.to_bits()); let lhs = Value { neg: lhs_neg, abs: lhs_abs, bits: $Inner::BITS, frac_bits: Frac::to_i32(), }; let Kind::Finite { neg: rhs_neg, abs: rhs_abs, frac_bits: rhs_frac, } = float_helper::$Float::kind(*rhs) else { return false; }; let rhs = Value { neg: rhs_neg, abs: rhs_abs, bits: $FloatBits::BITS, frac_bits: rhs_frac, }; float_eq(lhs, rhs) } } impl PartialEq<$Fix> for $Float { #[inline] fn eq(&self, rhs: &$Fix) -> bool { rhs.eq(self) } } impl PartialOrd<$Float> for $Fix { #[inline] fn partial_cmp(&self, rhs: &$Float) -> Option { use float_helper::$Float::Kind; let (lhs_neg, lhs_abs) = int_helper::$Inner::neg_abs(self.to_bits()); let lhs = Value { neg: lhs_neg, abs: lhs_abs, bits: $Inner::BITS, frac_bits: Frac::to_i32(), }; let (rhs_neg, rhs_abs, rhs_frac) = match float_helper::$Float::kind(*rhs) { Kind::Finite { neg, abs, frac_bits, } => (neg, abs, frac_bits), Kind::Infinite { neg } => { return if neg { Some(Ordering::Greater) } else { Some(Ordering::Less) }; } Kind::NaN => return None, }; let rhs = Value { neg: rhs_neg, abs: rhs_abs, bits: $FloatBits::BITS, frac_bits: rhs_frac, }; Some(float_cmp(lhs, rhs)) } } impl PartialOrd<$Fix> for $Float { #[inline] fn partial_cmp(&self, rhs: &$Fix) -> Option { rhs.partial_cmp(self).map(Ordering::reverse) } } }; } macro_rules! fixed_cmp_all { ($Fix:ident($LeEqU:ident, $Inner:ident)) => { fixed_cmp_int! { $Fix, i8 } fixed_cmp_int! { $Fix, i16 } fixed_cmp_int! { $Fix, i32 } fixed_cmp_int! { $Fix, i64 } fixed_cmp_int! { $Fix, i128 } fixed_cmp_int! { $Fix, isize } fixed_cmp_int! { $Fix, u8 } fixed_cmp_int! { $Fix, u16 } fixed_cmp_int! { $Fix, u32 } fixed_cmp_int! { $Fix, u64 } fixed_cmp_int! { $Fix, u128 } fixed_cmp_int! { $Fix, usize } #[cfg(feature = "nightly-float")] fixed_cmp_float! { $Fix($Inner), f16, u16 } fixed_cmp_float! { $Fix($Inner), half_f16, u16 } fixed_cmp_float! { $Fix($Inner), half_bf16, u16 } fixed_cmp_float! { $Fix($Inner), f32, u32 } fixed_cmp_float! { $Fix($Inner), f64, u64 } #[cfg(feature = "nightly-float")] fixed_cmp_float! { $Fix($Inner), f128, u128 } fixed_cmp_float! { $Fix($Inner), F128, u128 } fixed_cmp_float! { $Fix($Inner), F128Bits, u128 } }; } fixed_cmp_all! { FixedI8(LeEqU8, i8) } fixed_cmp_all! { FixedI16(LeEqU16, i16) } fixed_cmp_all! { FixedI32(LeEqU32, i32) } fixed_cmp_all! { FixedI64(LeEqU64, i64) } fixed_cmp_all! { FixedI128(LeEqU128, i128) } fixed_cmp_all! { FixedU8(LeEqU8, u8) } fixed_cmp_all! { FixedU16(LeEqU16, u16) } fixed_cmp_all! { FixedU32(LeEqU32, u32) } fixed_cmp_all! { FixedU64(LeEqU64, u64) } fixed_cmp_all! { FixedU128(LeEqU128, u128) } #[cfg(test)] mod tests { use crate::*; use core::cmp::Ordering; #[test] fn cmp_signed() { use core::cmp::Ordering::*; let neg1_16 = FixedI32::::NEG_ONE; let neg1_20 = FixedI32::::NEG_ONE; let mut a = neg1_16; let mut b = neg1_20; // a = ffff.0000 = -1, b = fff.00000 = -1 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert_eq!(a, -1i8); assert_eq!(b, -1i128); a >>= 16; b >>= 16; // a = ffff.ffff = -2^-16, b = fff.ffff0 = -2^-16 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert!(a < 0.0); assert_eq!(a.partial_cmp(&f32::INFINITY), Some(Less)); assert!(a < f32::INFINITY); assert!(a != f32::INFINITY); assert_eq!(a.partial_cmp(&f32::NEG_INFINITY), Some(Greater)); assert!(a > f32::NEG_INFINITY); assert_eq!(a, -(-16f32).exp2()); assert!(a <= -(-16f32).exp2()); assert!(a >= -(-16f32).exp2()); assert!(a < (-16f32).exp2()); assert_ne!(a, -0.75 * (-16f32).exp2()); assert!(a < -0.75 * (-16f32).exp2()); assert!(a <= -0.75 * (-16f32).exp2()); assert!(a > -1.25 * (-16f32).exp2()); assert!(a >= -1.25 * (-16f32).exp2()); a >>= 1; b >>= 1; // a = ffff.ffff = -2^-16, b = fff.ffff8 = -2^-17 assert!(a.ne(&b) && b.ne(&a)); assert_eq!(a.partial_cmp(&b), Some(Less)); assert_eq!(b.partial_cmp(&a), Some(Greater)); a = neg1_16 << 11; b = neg1_20 << 11; // a = f800.0000 = -2^11, b = 800.00000 = -2^11 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert_eq!(a, -1i16 << 11); assert_eq!(b, -1i64 << 11); a <<= 1; b <<= 1; // a = f000.0000 = -2^-12, b = 000.00000 = 0 assert!(a.ne(&b) && b.ne(&a)); assert_eq!(a.partial_cmp(&b), Some(Less)); assert_eq!(b.partial_cmp(&a), Some(Greater)); assert!(a < 1u8); assert_eq!(b, 0); } #[test] fn cmp_unsigned() { use core::cmp::Ordering::*; let one_16 = FixedU32::::ONE; let one_20 = FixedU32::::ONE; let mut a = one_16; let mut b = one_20; // a = 0001.0000 = 1, b = 001.00000 = 1 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert_eq!(a, 1u8); assert_eq!(b, 1i128); a >>= 16; b >>= 16; // a = 0000.0001 = 2^-16, b = 000.00010 = 2^-16 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert!(a > 0.0); assert_eq!(a.partial_cmp(&f32::INFINITY), Some(Less)); assert!(a < f32::INFINITY); assert!(a != f32::INFINITY); assert_eq!(a.partial_cmp(&f32::NEG_INFINITY), Some(Greater)); assert!(a > f32::NEG_INFINITY); assert_eq!(a, (-16f64).exp2()); assert!(a <= (-16f64).exp2()); assert!(a >= (-16f64).exp2()); assert!(a > -(-16f64).exp2()); assert_ne!(a, 0.75 * (-16f64).exp2()); assert!(a > 0.75 * (-16f64).exp2()); assert!(a >= 0.75 * (-16f64).exp2()); assert!(a < 1.25 * (-16f64).exp2()); assert!(a <= 1.25 * (-16f64).exp2()); a >>= 1; b >>= 1; // a = 0000.0000 = 0, b = 000.00008 = 2^-17 assert!(a.ne(&b) && b.ne(&a)); assert_eq!(a.partial_cmp(&b), Some(Less)); assert_eq!(b.partial_cmp(&a), Some(Greater)); a = one_16 << 11; b = one_20 << 11; // a = 0800.0000 = 2^11, b = 800.00000 = 2^11 assert!(a.eq(&b) && b.eq(&a)); assert_eq!(a.partial_cmp(&b), Some(Equal)); assert_eq!(b.partial_cmp(&a), Some(Equal)); assert_eq!(a, 1i16 << 11); assert_eq!(b, 1u64 << 11); a <<= 1; b <<= 1; // a = 1000.0000 = 2^12, b = 000.00000 = 0 assert!(a.ne(&b) && b.ne(&a)); assert_eq!(a.partial_cmp(&b), Some(Greater)); assert_eq!(b.partial_cmp(&a), Some(Less)); assert!(a > -1i8); assert_eq!(a, 1i32 << 12); assert_eq!(b, 0); } #[test] fn cmp_i0() { use crate::types::*; assert_eq!(I0F32::checked_from_num(0.5), None); for &float in &[ -0.5, -0.5 + f32::EPSILON, -0.25, -f32::EPSILON, 0.0, f32::EPSILON, 0.25, 0.5 - f32::EPSILON, ] { let fixed = I0F32::from_num(float); let half = U0F32::from_num(0.5); assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}"); assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}"); assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}"); assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}"); assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}"); assert_eq!( fixed.partial_cmp(&half), float.partial_cmp(&0.5), "{fixed}.partial_cmp(&{half})" ); assert_eq!(half < fixed, fixed > half); assert_eq!(half <= fixed, fixed >= half); assert_eq!(half == fixed, fixed == half); assert_eq!(half >= fixed, fixed <= half); assert_eq!(half > fixed, fixed < half); assert_eq!( half.partial_cmp(&fixed), fixed.partial_cmp(&half).map(Ordering::reverse) ); let half = I1F31::from_num(0.5); assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}"); assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}"); assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}"); assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}"); assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}"); assert_eq!( fixed.partial_cmp(&half), float.partial_cmp(&0.5), "{fixed}.partial_cmp(&{half})" ); assert_eq!(half < fixed, fixed > half); assert_eq!(half <= fixed, fixed >= half); assert_eq!(half == fixed, fixed == half); assert_eq!(half >= fixed, fixed <= half); assert_eq!(half > fixed, fixed < half); assert_eq!( half.partial_cmp(&fixed), fixed.partial_cmp(&half).map(Ordering::reverse) ); let half = 0.5f32; assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}"); assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}"); assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}"); assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}"); assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}"); assert_eq!( fixed.partial_cmp(&half), float.partial_cmp(&0.5), "{fixed}.partial_cmp(&{half})" ); assert_eq!(half < fixed, fixed > half); assert_eq!(half <= fixed, fixed >= half); assert_eq!(half == fixed, fixed == half); assert_eq!(half >= fixed, fixed <= half); assert_eq!(half > fixed, fixed < half); assert_eq!( half.partial_cmp(&fixed), fixed.partial_cmp(&half).map(Ordering::reverse) ); let m1 = I32F0::from_num(-1.0); assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}"); assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}"); assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}"); assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}"); assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}"); assert_eq!( fixed.partial_cmp(&m1), float.partial_cmp(&-1.0), "{fixed}.partial_cmp(&{m1})" ); assert_eq!(m1 < fixed, fixed > m1); assert_eq!(m1 <= fixed, fixed >= m1); assert_eq!(m1 == fixed, fixed == m1); assert_eq!(m1 >= fixed, fixed <= m1); assert_eq!(m1 > fixed, fixed < m1); assert_eq!( m1.partial_cmp(&fixed), fixed.partial_cmp(&m1).map(Ordering::reverse) ); let m1 = I1F31::from_num(-1.0); assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}"); assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}"); assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}"); assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}"); assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}"); assert_eq!( fixed.partial_cmp(&m1), float.partial_cmp(&-1.0), "{fixed}.partial_cmp(&{m1})" ); assert_eq!(m1 < fixed, fixed > m1); assert_eq!(m1 <= fixed, fixed >= m1); assert_eq!(m1 == fixed, fixed == m1); assert_eq!(m1 >= fixed, fixed <= m1); assert_eq!(m1 > fixed, fixed < m1); assert_eq!( m1.partial_cmp(&fixed), fixed.partial_cmp(&m1).map(Ordering::reverse) ); let m1 = -1.0f32; assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}"); assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}"); assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}"); assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}"); assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}"); assert_eq!( fixed.partial_cmp(&m1), float.partial_cmp(&-1.0), "{fixed}.partial_cmp(&{m1})" ); assert_eq!(m1 < fixed, fixed > m1); assert_eq!(m1 <= fixed, fixed >= m1); assert_eq!(m1 == fixed, fixed == m1); assert_eq!(m1 >= fixed, fixed <= m1); assert_eq!(m1 > fixed, fixed < m1); assert_eq!( m1.partial_cmp(&fixed), fixed.partial_cmp(&m1).map(Ordering::reverse) ); let mhalf = I1F31::from_num(-0.5); assert_eq!(fixed < mhalf, float < -0.5, "{fixed} < {mhalf}"); assert_eq!(fixed <= mhalf, float <= -0.5, "{fixed} <= {mhalf}"); assert_eq!(fixed == mhalf, float == -0.5, "{fixed} == {mhalf}"); assert_eq!(fixed >= mhalf, float >= -0.5, "{fixed} >= {mhalf}"); assert_eq!(fixed > mhalf, float > -0.5, "{fixed} > {mhalf}"); assert_eq!( fixed.partial_cmp(&mhalf), float.partial_cmp(&-0.5), "{fixed}.partial_cmp(&{mhalf})" ); assert_eq!(mhalf < fixed, fixed > mhalf); assert_eq!(mhalf <= fixed, fixed >= mhalf); assert_eq!(mhalf == fixed, fixed == mhalf); assert_eq!(mhalf >= fixed, fixed <= mhalf); assert_eq!(mhalf > fixed, fixed < mhalf); assert_eq!( mhalf.partial_cmp(&fixed), fixed.partial_cmp(&mhalf).map(Ordering::reverse) ); let mhalf = -0.5f32; assert_eq!(fixed < mhalf, float < -0.5, "{fixed} < {mhalf}"); assert_eq!(fixed <= mhalf, float <= -0.5, "{fixed} <= {mhalf}"); assert_eq!(fixed == mhalf, float == -0.5, "{fixed} == {mhalf}"); assert_eq!(fixed >= mhalf, float >= -0.5, "{fixed} >= {mhalf}"); assert_eq!(fixed > mhalf, float > -0.5, "{fixed} > {mhalf}"); assert_eq!( fixed.partial_cmp(&mhalf), float.partial_cmp(&-0.5), "{fixed}.partial_cmp(&{mhalf})" ); assert_eq!(mhalf < fixed, fixed > mhalf); assert_eq!(mhalf <= fixed, fixed >= mhalf); assert_eq!(mhalf == fixed, fixed == mhalf); assert_eq!(mhalf >= fixed, fixed <= mhalf); assert_eq!(mhalf > fixed, fixed < mhalf); assert_eq!( mhalf.partial_cmp(&fixed), fixed.partial_cmp(&mhalf).map(Ordering::reverse) ); } } }