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