Skip to main content

decimal_scaled/identity/
equalities.rs

1// SPDX-FileCopyrightText: 2026 John Moxley
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! `PartialEq` impls between every decimal width and primitive numeric
5//! types. Wires the macro emissions in `src/macros/equalities.rs` to
6//! each concrete width.
7//!
8//! # Semantics — EXACT value equality
9//!
10//! Both surfaces ride the shared 1.3 const comparator
11//! (`Int::cmp_cross_scaled` for integers, `Int::cmp_f64_exact` for
12//! floats); no quotient/remainder bespoke path, no `from_f64`/`to_f64`
13//! round-trip.
14//!
15//! - **Integers** (`i8`-`i128`, `u8`-`u128`, `isize`, `usize`): exact
16//! mathematical equality. A primitive `n` is the scale-0 value `n`, so
17//! `d == n` compares `d`'s storage (scale `SCALE`) against `n` (scale 0)
18//! via the cross-scale comparator. Exact and overflow-free.
19//!
20//! Examples:
21//! - `D38s12::try_from(5).unwrap() == 5_i32` -> `true`
22//! - `5.5` stored in `D38s12` `== 5_i32` -> `false` (`5.5 != 5`)
23//! - a negative `D38s12` `== 0_u32` -> `false`
24//!
25//! - **Floats** (`f32`, `f64`): EXACT value equality. The decimal's
26//! rational value `bits / 10^SCALE` is compared against the float's
27//! exact dyadic value `m · 2^e` by cross-multiplying to integers. `NaN`
28//! and `±inf` always compare unequal.
29//!
30//! This is distinct from the lossy `TryFrom<f64>` / `to_f64`
31//! conversions: `f64` cannot represent `1.1` exactly (the nearest `f64`
32//! is `1.1000000000000000888...`), so `D::from_str("1.1") == 1.1_f64` is
33//! `false`. Callers wanting the *rounded* float should convert with
34//! `from_f64` and compare decimals.
35//!
36//! Each impl provides both directions (`D38<S> == T` and `T == D38<S>`) so
37//! comparisons are symmetric at the call site.
38
39use crate::types::widths::{D18, D38};
40
41// Cross-equality with primitive integer types is emitted by the
42// `decl_eq_all_integers!` macro family in `src/macros/equalities.rs`.
43// The same surface is generated for every decimal width.
44crate::macros::equalities::decl_eq_all_integers!(D38);
45crate::macros::equalities::decl_eq_all_integers!(D18);
46
47#[cfg(any(feature = "d76", feature = "wide"))]
48use crate::types::widths::D76;
49#[cfg(any(feature = "d153", feature = "wide"))]
50use crate::types::widths::D153;
51#[cfg(any(feature = "d307", feature = "wide"))]
52use crate::types::widths::D307;
53
54#[cfg(any(feature = "d76", feature = "wide"))]
55crate::macros::equalities::decl_eq_all_integers!(D76);
56#[cfg(any(feature = "d153", feature = "wide"))]
57crate::macros::equalities::decl_eq_all_integers!(D153);
58#[cfg(any(feature = "d307", feature = "wide"))]
59crate::macros::equalities::decl_eq_all_integers!(D307);
60
61// Float equality requires the f64 bridge (`from_f64` / `to_f64`),
62// which is gated on `std`. Float impls are emitted for every width
63// below.
64#[cfg(feature = "std")]
65crate::macros::equalities::decl_eq_float!(D38, f32);
66#[cfg(feature = "std")]
67crate::macros::equalities::decl_eq_float!(D38, f64);
68#[cfg(feature = "std")]
69crate::macros::equalities::decl_eq_float!(D18, f32);
70#[cfg(feature = "std")]
71crate::macros::equalities::decl_eq_float!(D18, f64);
72
73// Wide tiers share the same exact-equality surface, so the same macro
74// applies unchanged.
75#[cfg(all(feature = "std", any(feature = "d76", feature = "wide")))]
76crate::macros::equalities::decl_eq_float!(D76, f32);
77#[cfg(all(feature = "std", any(feature = "d76", feature = "wide")))]
78crate::macros::equalities::decl_eq_float!(D76, f64);
79#[cfg(all(feature = "std", any(feature = "d153", feature = "wide")))]
80crate::macros::equalities::decl_eq_float!(D153, f32);
81#[cfg(all(feature = "std", any(feature = "d153", feature = "wide")))]
82crate::macros::equalities::decl_eq_float!(D153, f64);
83#[cfg(all(feature = "std", any(feature = "d307", feature = "wide")))]
84crate::macros::equalities::decl_eq_float!(D307, f32);
85#[cfg(all(feature = "std", any(feature = "d307", feature = "wide")))]
86crate::macros::equalities::decl_eq_float!(D307, f64);