Skip to main content

decimal_scaled/types/traits/
consts.rs

1// SPDX-FileCopyrightText: 2026 John Moxley
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! The [`DecimalConstants`] trait — mathematical constants (`pi`, `tau`,
5//! `half_pi`, `quarter_pi`, `golden`, `e`, `deg_per_rad`, `rad_per_deg`,
6//! `log10_2`) available on every decimal width.
7//!
8//! Split out of `types/consts/d38.rs` to sit with its sibling traits
9//! ([`crate::DecimalArithmetic`], [`crate::DecimalConvert`],
10//! [`crate::DecimalTranscendental`]); `Decimal` requires all four.
11//! Callers who only need constants (not arithmetic or transcendentals)
12//! can target this narrower bound:
13//!
14//! ```ignore
15//! use decimal_scaled::DecimalConstants;
16//!
17//! fn circle_area<T: DecimalConstants + Copy + std::ops::Mul<Output = T>>(r: T) -> T {
18//!     T::pi() * r * r
19//! }
20//! ```
21//!
22//! See [`crate::types::traits::decimal`] for the full scope rationale.
23
24/// Well-known mathematical constants available on every decimal width
25/// (`D18` / `D38` / `D76` / `D153` / `D307`).
26///
27/// Import this trait to call `D38s12::pi()`, `D76::<35>::e()`, etc.
28///
29/// All returned values are computed from a raw integer reference at
30/// the tier's maximum storage precision (75 digits for D18/D38 and
31/// D76; 153 for D153; 307 for D307) without passing through `f64`,
32/// then rescaled down to the caller's `SCALE` with half-to-even
33/// rounding. The result is **within 0.5 ULP** of the canonical
34/// decimal expansion at every supported scale on every width.
35///
36/// The one situation where a method does not return a value is when
37/// the constant's magnitude exceeds the type's storage range at the
38/// caller's `SCALE` — e.g. `D38<38>::pi()` would need `3.14 × 10³⁸`,
39/// which exceeds `i128::MAX ≈ 1.7×10³⁸`. The method panics with a
40/// clear "constant out of storage range" message in that case.
41///
42/// # Crossing into f64
43///
44/// `to_f64()` is itself correctly rounded, but it can only round to
45/// the *decimal value the type holds* — not to the underlying ideal
46/// constant. `f64` carries ~15.95 decimal digits of mantissa, so any
47/// constant produced at `SCALE < 15` is intrinsically coarser than
48/// the `f64` grid: `D38<12>::pi().to_f64()` lands ~466 ULPs from
49/// [`std::f64::consts::PI`], because the 12-digit decimal rounds
50/// differently than the closest-`f64` to true π. At `SCALE ≥ 15` the
51/// round-trip is bit-exact for these constants (the decimal value
52/// has enough digits to disambiguate the `f64` grid).
53///
54/// **Practical rule for downstream code that crosses into `f64`** —
55/// CAD bulge-arc tessellation, OpenGL/GLSL, hardware drivers — and
56/// uses the `f64` value to count, bucket, or seed a fixed-iteration
57/// loop: source mathematical constants from [`std::f64::consts`]
58/// directly at the boundary rather than going through
59/// `Decimal::pi().to_f64()`. Otherwise pick a `SCALE` of 15 or more
60/// so the decimal value can round-trip to the canonical `f64`.
61pub trait DecimalConstants: Sized {
62    /// Pi (~3.14159265...). One half-turn in radians.
63    ///
64    /// Source: ISO 80000-2 / OEIS A000796. Rescaled per-tier (see the
65    /// module-level table) to the caller's `SCALE` via the crate-default
66    /// rounding mode.
67    ///
68    /// # Precision
69    ///
70    /// N/A: constant value, no arithmetic performed.
71    fn pi() -> Self;
72
73    /// Tau (~6.28318530...). One full turn in radians.
74    ///
75    /// Defined as `2 * pi`. Rescaled per-tier (see the module-level table) to the caller's `SCALE` via the crate-default rounding mode.
76    ///
77    /// # Precision
78    ///
79    /// N/A: constant value, no arithmetic performed.
80    fn tau() -> Self;
81
82    /// Half-pi (~1.57079632...). One quarter-turn in radians.
83    ///
84    /// Defined as `pi / 2`. Rescaled per-tier (see the module-level table) to the caller's `SCALE` via the crate-default rounding mode.
85    ///
86    /// # Precision
87    ///
88    /// N/A: constant value, no arithmetic performed.
89    fn half_pi() -> Self;
90
91    /// Quarter-pi (~0.78539816...). One eighth-turn in radians.
92    ///
93    /// Defined as `pi / 4`. Rescaled per-tier (see the module-level table) to the caller's `SCALE` via the crate-default rounding mode.
94    ///
95    /// # Precision
96    ///
97    /// N/A: constant value, no arithmetic performed.
98    fn quarter_pi() -> Self;
99
100    /// The golden ratio (~1.61803398...). Dimensionless.
101    ///
102    /// Defined as `(1 + sqrt(5)) / 2`. Source: OEIS A001622. Rescaled
103    /// per-tier (see the module-level table) to the caller's `SCALE`
104    /// via the crate-default rounding mode.
105    ///
106    /// # Precision
107    ///
108    /// N/A: constant value, no arithmetic performed.
109    fn golden() -> Self;
110
111    /// Euler's number (~2.71828182...). Dimensionless.
112    ///
113    /// Source: OEIS A001113. Rescaled per-tier (see the module-level table) to the caller's `SCALE` via the crate-default rounding mode.
114    ///
115    /// # Precision
116    ///
117    /// N/A: constant value, no arithmetic performed.
118    fn e() -> Self;
119
120    /// Degrees per radian (~57.29577951...). The factor `180/π`:
121    /// multiply a radian measure by this to convert it to degrees.
122    ///
123    /// Source: the oracle value of `180/π`. Sourced per-scale from the
124    /// constant table to the caller's `SCALE` via the crate-default
125    /// rounding mode.
126    ///
127    /// # Precision
128    ///
129    /// N/A: constant value, no arithmetic performed.
130    fn deg_per_rad() -> Self;
131
132    /// Radians per degree (~0.01745329...). The factor `π/180`:
133    /// multiply a degree measure by this to convert it to radians.
134    ///
135    /// Source: the oracle value of `π/180`. Sourced per-scale from the
136    /// constant table to the caller's `SCALE` via the crate-default
137    /// rounding mode.
138    ///
139    /// # Precision
140    ///
141    /// N/A: constant value, no arithmetic performed.
142    fn rad_per_deg() -> Self;
143
144    /// Base-10 logarithm of 2 (~0.30103...). The bit-to-digit factor:
145    /// a value's decimal-digit count is about `bit_length * log10(2)`,
146    /// so this scales between binary and decimal magnitudes.
147    ///
148    /// Source: the oracle value of `log(2)/log(10)`. Sourced per-scale
149    /// from the constant table to the caller's `SCALE` via the
150    /// crate-default rounding mode.
151    ///
152    /// # Precision
153    ///
154    /// N/A: constant value, no arithmetic performed.
155    fn log10_2() -> Self;
156
157    // ─── *_with(mode) siblings ───────────────────────────────────
158    //
159    // Each `<const>_with(mode)` rescales the 75-digit reference under
160    // the caller-supplied `RoundingMode`. Useful when the default
161    // mode (half-to-even, or whatever a `rounding-*` Cargo feature
162    // selects) is the wrong direction for the use case — e.g. a CAD
163    // tessellation that needs `pi_with(Floor)` so the down-stream
164    // f64 conversion stays ≤ true π and segment counts can't
165    // over-flow their fixed-size buffers.
166
167    /// `pi()` under the supplied rounding mode.
168    fn pi_with(mode: crate::support::rounding::RoundingMode) -> Self;
169    /// `tau()` under the supplied rounding mode.
170    fn tau_with(mode: crate::support::rounding::RoundingMode) -> Self;
171    /// `half_pi()` under the supplied rounding mode.
172    fn half_pi_with(mode: crate::support::rounding::RoundingMode) -> Self;
173    /// `quarter_pi()` under the supplied rounding mode.
174    fn quarter_pi_with(mode: crate::support::rounding::RoundingMode) -> Self;
175    /// `golden()` under the supplied rounding mode.
176    fn golden_with(mode: crate::support::rounding::RoundingMode) -> Self;
177    /// `e()` under the supplied rounding mode.
178    fn e_with(mode: crate::support::rounding::RoundingMode) -> Self;
179    /// `deg_per_rad()` under the supplied rounding mode.
180    fn deg_per_rad_with(mode: crate::support::rounding::RoundingMode) -> Self;
181    /// `rad_per_deg()` under the supplied rounding mode.
182    fn rad_per_deg_with(mode: crate::support::rounding::RoundingMode) -> Self;
183    /// `log10_2()` under the supplied rounding mode.
184    fn log10_2_with(mode: crate::support::rounding::RoundingMode) -> Self;
185}
186
187