pub(crate) const U_N: f64 = f64::MIN_POSITIVE;
pub(crate) const U_S: f64 = 5e-324;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Window {
q_min: i32,
e_max: i32,
}
impl Window {
const fn for_degree(degree: i32) -> Self {
Self {
q_min: -900 / degree,
e_max: 960 / degree - 2,
}
}
pub(crate) const ORIENT2D: Self = Self::for_degree(2);
pub(crate) const ORIENT3D: Self = Self::for_degree(3);
pub(crate) const INCIRCLE: Self = Self::for_degree(4);
pub(crate) const INSPHERE: Self = Self::for_degree(5);
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) enum Prepared {
AsIs,
Scaled(f64),
OutOfWindow,
}
fn bit_span(x: f64) -> (i32, i32) {
debug_assert!(
x != 0.0 && x.is_finite(),
"bit_span needs a non-zero finite value"
);
let bits = x.to_bits();
let biased = ((bits >> 52) & 0x7FF) as i32;
let field = bits & 0x000F_FFFF_FFFF_FFFF;
if biased > 0 {
let significand = field | (1_u64 << 52);
#[allow(clippy::cast_possible_wrap)]
let lowest = significand.trailing_zeros() as i32;
(biased - 1023, biased - 1075 + lowest)
} else {
#[allow(clippy::cast_possible_wrap)]
let leading = field.ilog2() as i32;
#[allow(clippy::cast_possible_wrap)]
let lowest = field.trailing_zeros() as i32;
(leading - 1074, lowest - 1074)
}
}
fn power_of_two(exponent: i32) -> f64 {
debug_assert!(
(-1022..=1023).contains(&exponent),
"the scale factor must be a normal power of two"
);
#[allow(clippy::cast_sign_loss)]
let biased = (exponent + 1023) as u64;
f64::from_bits(biased << 52)
}
pub(crate) fn prepare(coords: &[f64], window: Window) -> Prepared {
let mut highest_leading = i32::MIN;
let mut lowest_set = i32::MAX;
for &coordinate in coords {
if coordinate != 0.0 {
let (leading, lowest) = bit_span(coordinate);
if leading > highest_leading {
highest_leading = leading;
}
if lowest < lowest_set {
lowest_set = lowest;
}
}
}
if highest_leading == i32::MIN {
return Prepared::AsIs;
}
if lowest_set >= window.q_min && highest_leading <= window.e_max {
return Prepared::AsIs;
}
let up = window.q_min - lowest_set;
let down = window.e_max - highest_leading;
if up > down {
return Prepared::OutOfWindow;
}
let shift = if up > 0 { up } else { down };
debug_assert!(shift != 0, "an in-window input returns AsIs above");
Prepared::Scaled(power_of_two(shift))
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)]
use super::*;
#[test]
fn bit_span_reads_both_ends_of_a_normal_value() {
assert_eq!(bit_span(1.0), (0, 0));
assert_eq!(bit_span(0.75), (-1, -2));
assert_eq!(bit_span(3.0), (1, 0));
assert_eq!(bit_span(-3.0), (1, 0));
assert_eq!(bit_span(1.0 + f64::EPSILON), (0, -52));
assert_eq!(bit_span(f64::MAX), (1023, 971));
assert_eq!(bit_span(f64::MIN_POSITIVE), (-1022, -1022));
}
#[test]
fn bit_span_reads_both_ends_of_a_subnormal() {
assert_eq!(bit_span(f64::from_bits(1)), (-1074, -1074));
assert_eq!(bit_span(f64::from_bits(3)), (-1073, -1074));
let largest = f64::from_bits(0x000F_FFFF_FFFF_FFFF);
assert_eq!(bit_span(largest), (-1023, -1074));
assert_eq!(bit_span(f64::from_bits(1 << 20)), (-1054, -1054));
}
#[test]
fn the_four_windows_have_their_tabulated_limits() {
for (degree, window, q_min, e_max) in [
(2, Window::ORIENT2D, -450, 478),
(3, Window::ORIENT3D, -300, 318),
(4, Window::INCIRCLE, -225, 238),
(5, Window::INSPHERE, -180, 190),
] {
assert_eq!(window.q_min, q_min, "degree {degree}");
assert_eq!(window.e_max, e_max, "degree {degree}");
assert_eq!(degree * window.q_min, -900, "degree {degree}");
assert_eq!(degree * (window.e_max + 2), 960, "degree {degree}");
}
}
#[test]
fn in_window_input_is_used_as_is() {
let window = Window::ORIENT2D;
assert_eq!(
prepare(
&[1_400_000.25, 900_000.5, 1_400_017.75, 899_990.125],
window
),
Prepared::AsIs
);
assert_eq!(prepare(&[0.0, 0.0, 0.0, 0.0], window), Prepared::AsIs);
assert_eq!(prepare(&[], window), Prepared::AsIs);
let low = power_of_two(-450);
let high = power_of_two(478);
assert_eq!(prepare(&[low, high, 0.0], window), Prepared::AsIs);
}
#[test]
fn out_of_range_input_is_rescaled_without_loss() {
let window = Window::INCIRCLE;
for exponent in [-1074_i32, -1000, -700, -300, 300, 700, 1000, 1023] {
let coordinate = if exponent < -1022 {
f64::from_bits(1_u64 << (exponent + 1074).unsigned_abs())
} else {
power_of_two(exponent)
};
let coords = [coordinate, coordinate * 0.5, 0.0];
match prepare(&coords, window) {
Prepared::Scaled(factor) => {
assert_eq!(
factor.to_bits() & 0x000F_FFFF_FFFF_FFFF,
0,
"the factor must be a power of two: {factor:e}"
);
for &c in &coords {
let scaled = c * factor;
if c != 0.0 {
let (leading, lowest) = bit_span(scaled);
assert!(lowest >= window.q_min, "exponent {exponent}");
assert!(leading <= window.e_max, "exponent {exponent}");
assert_eq!((scaled / factor).to_bits(), c.to_bits());
}
}
let moved: [f64; 3] =
[coords[0] * factor, coords[1] * factor, coords[2] * factor];
assert_eq!(prepare(&moved, window), Prepared::AsIs);
}
other => panic!("exponent {exponent} gave {other:?}"),
}
}
}
#[test]
fn input_wider_than_the_window_is_out_of_window() {
let wide = [f64::from_bits(1), power_of_two(900)];
assert_eq!(prepare(&wide, Window::INSPHERE), Prepared::OutOfWindow);
assert_eq!(prepare(&wide, Window::ORIENT2D), Prepared::OutOfWindow);
let just_too_wide = [f64::from_bits(1) * power_of_two(145), 1.0];
assert_eq!(bit_span(just_too_wide[0]).1, -929);
assert_eq!(
prepare(&just_too_wide, Window::ORIENT2D),
Prepared::OutOfWindow
);
let exactly_wide = [f64::from_bits(1) * power_of_two(146), 1.0];
assert_eq!(bit_span(exactly_wide[0]).1, -928);
assert!(matches!(
prepare(&exactly_wide, Window::ORIENT2D),
Prepared::Scaled(_)
));
}
#[test]
fn the_scan_places_input_by_its_lowest_set_bit() {
let window = Window::ORIENT2D;
let value = (1.0 + f64::EPSILON) * power_of_two(-400);
assert_eq!(bit_span(value), (-400, -452));
match prepare(&[value], window) {
Prepared::Scaled(factor) => {
assert_eq!(factor, 4.0, "two bits up");
assert_eq!(bit_span(value * factor), (-398, -450));
}
other => panic!("expected a rescale, got {other:?}"),
}
}
}