use core::{
cmp::Ordering,
fmt::{Debug, Display},
hash::{Hash, Hasher},
ops::{AddAssign, SubAssign},
};
#[cfg(feature = "float_nightly_experimental")]
use super::total_float::{from_ordered_16, from_ordered_128, to_ordered_16, to_ordered_128};
use super::total_float::{from_ordered_32, from_ordered_64, to_ordered_32, to_ordered_64};
use num_traits::ops::wrapping::{WrappingAdd, WrappingSub};
use num_traits::{One, Zero};
mod private {
pub trait Sealed {}
}
#[doc(hidden)]
pub trait FiniteFloat:
private::Sealed + Default + Copy + Clone + Debug + Send + Sync + 'static
{
const MIN: Self;
const MAX: Self;
const MAX_SIZE: Self::SafeLen;
type SafeLen: Send
+ Sync
+ Debug
+ Display
+ Hash
+ Copy
+ PartialEq
+ PartialOrd
+ num_traits::Zero
+ num_traits::One
+ AddAssign
+ SubAssign;
fn hash<H: Hasher>(x: Self, state: &mut H);
fn total_cmp(x: Self, y: Self) -> Ordering;
fn is_finite(x: Self) -> bool;
fn normalize(x: Self) -> Self;
fn after(x: Self) -> Self;
fn before(x: Self) -> Self;
fn is_neg_zero(x: Self) -> bool;
fn prim_safe_len(start: Self, end: Self) -> Self::SafeLen;
fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64;
fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen;
fn inclusive_end_from_start(a: Self, b: Self::SafeLen) -> Self;
fn start_from_inclusive_end(a: Self, b: Self::SafeLen) -> Self;
}
pub(super) trait FiniteFloatImpl:
FiniteFloat + Default + Copy + Clone + Debug + Send + Sync + 'static
{
type Bits: Copy + Eq + Hash + Send + Sync + Debug;
type Ordered: WrappingAdd
+ WrappingSub
+ One
+ PartialEq
+ Copy
+ Send
+ Sync
+ Debug
+ Display
+ PartialOrd;
const MIN: Self;
const MAX: Self;
const MIN_ORDERED: Self::Ordered;
const MAX_ORDERED: Self::Ordered;
const MAX_SIZE: Self::SafeLen;
const NEG_ZERO_BITS: Self::Bits;
const NEG_ZERO_ORDERED: Self::Ordered;
fn to_ordered(x: Self) -> Self::Ordered;
fn from_ordered(x: Self::Ordered) -> Self;
fn to_bits(x: Self) -> Self::Bits;
fn safe_len(start: Self::Ordered, end: Self::Ordered) -> Self::SafeLen;
fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64;
fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen;
fn safe_as_ordered(x: Self::SafeLen) -> Self::Ordered;
fn total_cmp(x: Self, y: Self) -> Ordering;
fn inclusive_end_from_start(a: Self, b: Self::SafeLen) -> Self {
#[cfg(debug_assertions)]
{
let max_len =
<Self as FiniteFloatImpl>::prim_safe_len(a, <Self as FiniteFloatImpl>::MAX);
assert!(
Self::SafeLen::zero() < b && b <= max_len,
"b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
);
}
let start = Self::to_ordered(a);
let mut end = start.wrapping_add(&Self::safe_as_ordered(b));
if Self::crosses_neg_zero(start, end) {
end = end.wrapping_add(&Self::Ordered::one());
}
Self::from_ordered(end)
}
fn start_from_inclusive_end(a: Self, b: Self::SafeLen) -> Self {
#[cfg(debug_assertions)]
{
let max_len =
<Self as FiniteFloatImpl>::prim_safe_len(<Self as FiniteFloatImpl>::MIN, a);
assert!(
Self::SafeLen::zero() < b && b <= max_len,
"b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
);
}
let end = Self::to_ordered(a);
let mut start = end.wrapping_sub(&Self::safe_as_ordered(b));
if Self::crosses_neg_zero(start, end) {
start = start.wrapping_sub(&Self::Ordered::one());
}
Self::from_ordered(start)
}
fn prim_safe_len(start: Self, end: Self) -> Self::SafeLen {
Self::safe_len(Self::to_ordered(start), Self::to_ordered(end))
}
fn is_finite(x: Self) -> bool;
fn normalize(x: Self) -> Self;
fn after(x: Self) -> Self;
fn before(x: Self) -> Self;
fn is_neg_zero(x: Self) -> bool;
#[must_use]
fn crosses_neg_zero(start: Self::Ordered, end: Self::Ordered) -> bool {
(start..=end).contains(&Self::NEG_ZERO_ORDERED)
}
}
macro_rules! impl_finite_ops {
($to_ordered:ident) => {
const MIN: Self = Self::MIN;
const MAX: Self = Self::MAX;
const MIN_ORDERED: Self::Ordered = $to_ordered(Self::MIN);
const MAX_ORDERED: Self::Ordered = $to_ordered(Self::MAX);
const NEG_ZERO_BITS: Self::Bits = Self::to_bits(-0.0);
const NEG_ZERO_ORDERED: Self::Ordered = $to_ordered(-0.0);
fn to_ordered(x: Self) -> Self::Ordered {
$to_ordered(x)
}
fn to_bits(x: Self) -> Self::Bits {
x.to_bits()
}
#[expect(clippy::cast_sign_loss)]
fn safe_len(start: Self::Ordered, end: Self::Ordered) -> Self::SafeLen {
debug_assert!(start <= end, "start ≤ end required");
debug_assert!(start >= Self::MIN_ORDERED, "start >= MIN required");
debug_assert!(end <= Self::MAX_ORDERED, "end <= MAX required");
if Self::crosses_neg_zero(start, end) {
end.wrapping_sub(start) as Self::SafeLen
} else {
end.wrapping_sub(start).wrapping_add(1) as Self::SafeLen
}
}
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::use_self, reason = "f64 is not really Self")]
#[allow(clippy::cast_lossless)]
fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
len as f64
}
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::cast_sign_loss)]
fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
f as Self::SafeLen
}
#[expect(clippy::cast_possible_wrap)]
fn safe_as_ordered(x: Self::SafeLen) -> Self::Ordered {
debug_assert!(!x.is_zero(), "x must not be zero");
(x - 1) as Self::Ordered
}
fn total_cmp(x: Self, y: Self) -> Ordering {
x.total_cmp(&y)
}
fn is_finite(x: Self) -> bool {
x.is_finite()
}
fn normalize(x: Self) -> Self {
if x.to_bits() == Self::NEG_ZERO_BITS {
0.0
} else {
x
}
}
fn after(x: Self) -> Self {
x.next_up()
}
fn before(x: Self) -> Self {
x.next_down()
}
fn is_neg_zero(x: Self) -> bool {
x.to_bits() == Self::NEG_ZERO_BITS
}
};
}
impl private::Sealed for f64 {}
impl private::Sealed for f32 {}
#[cfg(feature = "float_nightly_experimental")]
impl private::Sealed for f16 {}
#[cfg(feature = "float_nightly_experimental")]
impl private::Sealed for f128 {}
macro_rules! impl_finite_capability {
($primitive:ty, $safe_len:ty) => {
impl FiniteFloat for $primitive {
type SafeLen = $safe_len;
const MIN: Self = <Self as FiniteFloatImpl>::MIN;
const MAX: Self = <Self as FiniteFloatImpl>::MAX;
const MAX_SIZE: Self::SafeLen = <Self as FiniteFloatImpl>::MAX_SIZE;
fn hash<H: Hasher>(x: Self, state: &mut H) {
<Self as FiniteFloatImpl>::to_bits(x).hash(state);
}
fn total_cmp(x: Self, y: Self) -> Ordering {
<Self as FiniteFloatImpl>::total_cmp(x, y)
}
fn is_finite(x: Self) -> bool {
<Self as FiniteFloatImpl>::is_finite(x)
}
fn normalize(x: Self) -> Self {
<Self as FiniteFloatImpl>::normalize(x)
}
fn after(x: Self) -> Self {
<Self as FiniteFloatImpl>::after(x)
}
fn before(x: Self) -> Self {
<Self as FiniteFloatImpl>::before(x)
}
fn is_neg_zero(x: Self) -> bool {
<Self as FiniteFloatImpl>::is_neg_zero(x)
}
fn prim_safe_len(start: Self, end: Self) -> Self::SafeLen {
<Self as FiniteFloatImpl>::prim_safe_len(start, end)
}
fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
<Self as FiniteFloatImpl>::safe_len_to_f64_lossy(len)
}
fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
<Self as FiniteFloatImpl>::f64_to_safe_len_lossy(f)
}
fn inclusive_end_from_start(a: Self, b: Self::SafeLen) -> Self {
<Self as FiniteFloatImpl>::inclusive_end_from_start(a, b)
}
fn start_from_inclusive_end(a: Self, b: Self::SafeLen) -> Self {
<Self as FiniteFloatImpl>::start_from_inclusive_end(a, b)
}
}
};
}
impl_finite_capability!(f64, u64);
impl_finite_capability!(f32, u32);
#[cfg(feature = "float_nightly_experimental")]
impl_finite_capability!(f16, u16);
#[cfg(feature = "float_nightly_experimental")]
impl_finite_capability!(f128, u128);
impl FiniteFloatImpl for f64 {
type Bits = u64;
type Ordered = i64;
const MAX_SIZE: Self::SafeLen = 0xFFE0_0000_0000_0000_u64 - 1;
fn from_ordered(bits: Self::Ordered) -> Self {
from_ordered_64(bits)
}
impl_finite_ops!(to_ordered_64);
}
impl FiniteFloatImpl for f32 {
type Bits = u32;
type Ordered = i32;
const MAX_SIZE: Self::SafeLen = 0xFF00_0000_u32 - 1;
impl_finite_ops!(to_ordered_32);
fn from_ordered(bits: Self::Ordered) -> Self {
from_ordered_32(bits)
}
}
#[cfg(feature = "float_nightly_experimental")]
impl FiniteFloatImpl for f16 {
type Bits = u16;
type Ordered = i16;
const MAX_SIZE: Self::SafeLen = 0xF800u16 - 1;
impl_finite_ops!(to_ordered_16);
fn from_ordered(bits: Self::Ordered) -> Self {
from_ordered_16(bits)
}
}
#[cfg(feature = "float_nightly_experimental")]
impl FiniteFloatImpl for f128 {
type Bits = u128;
type Ordered = i128;
const MAX_SIZE: Self::SafeLen = 0xFFFE_0000_0000_0000_0000_0000_0000_0000_u128 - 1;
impl_finite_ops!(to_ordered_128);
fn from_ordered(bits: Self::Ordered) -> Self {
from_ordered_128(bits)
}
}