Files
fips/src/proto/math.rs
T
Johnathan Corgan 2b009196b5 proto: no_std hygiene for the fmt/alloc imports and the filter math
Bring the proto tree closer to a std+alloc-only posture, behavior unchanged on
every decision path:

- Sweep the remaining std::fmt and std::collections imports over to core::fmt
  and alloc::collections across the wire codecs and their tests. Subsystem files
  that shadow the core name with a child core module use the leading-colon
  ::core::fmt form. Imports only.

- Replace the two f64::powi calls (bloom false-positive-rate, FMP backoff timer)
  with a shared core-only square-and-multiply helper in proto/math, bit-identical
  to std::powi (a guard test pins this against every exponent the codecs reach),
  and route the diagnostic estimated-count natural log through libm::log. The FPR
  reject decision and the backoff timer are bit-for-bit unchanged; only the
  debug-only count estimate may differ by at most one ULP.
2026-07-08 19:00:25 +00:00

56 lines
1.9 KiB
Rust

//! no_std-shaped numeric helpers for the protocol cores.
//!
//! `f64::powi` and the transcendental methods live in `std`, not `core`. The
//! helpers here keep the small amount of protocol math the codecs need in a
//! `core`-only shape so the cores stay portable, without changing any result.
/// Raise `base` to a non-negative integer power via square-and-multiply.
///
/// Bit-identical to `f64::powi` for the exponents the codecs use: it mirrors the
/// compiler-rt `__powidf2` multiply order (multiply-then-square, skipping the
/// final square), so — floating-point multiplication being non-associative — it
/// reproduces `powi` exactly rather than a naive left-to-right product. The
/// `powi_bit_identical_to_std` test pins this. Keeping it bit-identical matters:
/// the bloom false-positive-rate feeds a reject comparison and the FMP backoff
/// feeds a `u64` timer, so any drift could change a decision.
pub(crate) fn powi(base: f64, exp: u32) -> f64 {
let mut result = 1.0_f64;
let mut b = base;
let mut e = exp;
loop {
if e & 1 == 1 {
result *= b;
}
e >>= 1;
if e == 0 {
break;
}
b *= b;
}
result
}
#[cfg(test)]
mod tests {
use super::powi;
#[test]
fn powi_bit_identical_to_std() {
// The core square-and-multiply must match f64::powi bit-for-bit across
// representative bases and every exponent the protocol math reaches, so
// the FPR reject decision and the backoff timer are unchanged.
let bases = [
0.0, 1.0, 0.5, 0.5469, 0.5493, 0.5508, 0.9999, 1.5, 2.0, 3.7, 0.1, 1e-3,
];
for &base in &bases {
for exp in 0u32..=64 {
assert_eq!(
powi(base, exp).to_bits(),
base.powi(exp as i32).to_bits(),
"powi mismatch at base={base}, exp={exp}"
);
}
}
}
}