use crate::core_type::D38;
use crate::d_w128_kernels::Fixed;
use crate::wide_int::Int256;
const SCALE_REF: u32 = 75;
include!(concat!(env!("OUT_DIR"), "/wide_consts.rs"));
const PI_RAW: Int256 = match Int256::from_str_radix(PI_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: PI_D76_S75 not parseable"),
};
const TAU_RAW: Int256 = match Int256::from_str_radix(TAU_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: TAU_D76_S75 not parseable"),
};
const HALF_PI_RAW: Int256 = match Int256::from_str_radix(HALF_PI_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: HALF_PI_D76_S75 not parseable"),
};
const QUARTER_PI_RAW: Int256 = match Int256::from_str_radix(QUARTER_PI_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: QUARTER_PI_D76_S75 not parseable"),
};
const E_RAW: Int256 = match Int256::from_str_radix(E_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: E_D76_S75 not parseable"),
};
const GOLDEN_RAW: Int256 = match Int256::from_str_radix(GOLDEN_D76_S75, 10) {
Ok(v) => v,
Err(_) => panic!("consts: GOLDEN_D76_S75 not parseable"),
};
fn rescale_75_to_target<const TARGET: u32>(raw: Int256, name: &'static str) -> i128 {
let limbs = raw.0; let f = Fixed { negative: false, mag: limbs };
match f.round_to_i128(SCALE_REF, TARGET) {
Some(v) => v,
None => panic!(
"D38 constant out of storage range: {name} cannot fit i128 at SCALE = {TARGET} \
(storage range is ±i128::MAX / 10^SCALE)",
name = name,
TARGET = TARGET,
),
}
}
pub trait DecimalConsts: Sized {
fn pi() -> Self;
fn tau() -> Self;
fn half_pi() -> Self;
fn quarter_pi() -> Self;
fn golden() -> Self;
fn e() -> Self;
}
pub(crate) fn pi_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(PI_RAW, "pi")
}
pub(crate) fn tau_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(TAU_RAW, "tau")
}
pub(crate) fn half_pi_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(HALF_PI_RAW, "half_pi")
}
pub(crate) fn quarter_pi_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(QUARTER_PI_RAW, "quarter_pi")
}
pub(crate) fn golden_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(GOLDEN_RAW, "golden")
}
pub(crate) fn e_at_target<const TARGET: u32>() -> i128 {
rescale_75_to_target::<TARGET>(E_RAW, "e")
}
crate::macros::consts::decl_decimal_consts!(D38, i128);
impl<const SCALE: u32> D38<SCALE> {
pub const EPSILON: Self = Self(1);
pub const MIN_POSITIVE: Self = Self(1);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core_type::D38s12;
#[test]
fn pi_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::pi().to_bits(), 3_141_592_653_590_i128);
}
#[test]
fn tau_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::tau().to_bits(), 6_283_185_307_180_i128);
}
#[test]
fn half_pi_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::half_pi().to_bits(), 1_570_796_326_795_i128);
}
#[test]
fn quarter_pi_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::quarter_pi().to_bits(), 785_398_163_397_i128);
}
#[test]
fn e_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::e().to_bits(), 2_718_281_828_459_i128);
}
#[test]
fn golden_is_bit_exact_at_scale_12() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
assert_eq!(D38s12::golden().to_bits(), 1_618_033_988_750_i128);
}
#[test]
fn pi_close_to_f64_pi() {
let diff = (D38s12::pi().to_f64() - core::f64::consts::PI).abs();
assert!(diff < 1e-11, "pi diverges from f64 PI by {diff}");
}
#[test]
fn tau_close_to_f64_tau() {
let diff = (D38s12::tau().to_f64() - core::f64::consts::TAU).abs();
assert!(diff < 1e-11, "tau diverges from f64 TAU by {diff}");
}
#[test]
fn half_pi_close_to_f64_frac_pi_2() {
let diff =
(D38s12::half_pi().to_f64() - core::f64::consts::FRAC_PI_2).abs();
assert!(diff < 1e-11, "half_pi diverges from f64 FRAC_PI_2 by {diff}");
}
#[test]
fn quarter_pi_close_to_f64_frac_pi_4() {
let diff =
(D38s12::quarter_pi().to_f64() - core::f64::consts::FRAC_PI_4).abs();
assert!(
diff < 1e-11,
"quarter_pi diverges from f64 FRAC_PI_4 by {diff}"
);
}
#[test]
fn e_close_to_f64_e() {
let diff = (D38s12::e().to_f64() - core::f64::consts::E).abs();
assert!(diff < 1e-11, "e diverges from f64 E by {diff}");
}
#[cfg(feature = "std")]
#[test]
fn golden_close_to_closed_form() {
let expected = (1.0_f64 + 5.0_f64.sqrt()) / 2.0;
let diff = (D38s12::golden().to_f64() - expected).abs();
assert!(diff < 1e-11, "golden diverges from closed-form by {diff}");
}
#[test]
fn epsilon_is_one_ulp() {
assert_eq!(D38s12::EPSILON.to_bits(), 1_i128);
assert!(D38s12::EPSILON > D38s12::ZERO);
}
#[test]
fn min_positive_is_one_ulp() {
assert_eq!(D38s12::MIN_POSITIVE.to_bits(), 1_i128);
assert_eq!(D38s12::MIN_POSITIVE, D38s12::EPSILON);
}
#[test]
fn epsilon_at_scale_6_is_one_ulp() {
type D6 = D38<6>;
assert_eq!(D6::EPSILON.to_bits(), 1_i128);
assert_eq!(D6::MIN_POSITIVE.to_bits(), 1_i128);
}
#[test]
fn pi_at_scale_6_is_bit_exact() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
type D6 = D38<6>;
assert_eq!(D6::pi().to_bits(), 3_141_593_i128);
}
#[test]
fn pi_at_scale_0_is_three() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
type D0 = D38<0>;
assert_eq!(D0::pi().to_bits(), 3_i128);
}
#[test]
fn pi_at_scale_37_matches_canonical_37_digit_rounding() {
type D37 = D38<37>;
let expected: i128 = 31_415_926_535_897_932_384_626_433_832_795_028_842;
assert_eq!(D37::pi().to_bits(), expected);
}
#[test]
#[should_panic(expected = "out of storage range")]
fn pi_at_scale_38_panics_storage_range() {
let _ = D38::<38>::pi();
}
#[test]
fn half_pi_and_quarter_pi_at_scale_38_are_correctly_rounded() {
let expected_half_pi: i128 = 157_079_632_679_489_661_923_132_169_163_975_144_210;
let got = D38::<38>::half_pi().to_bits();
let diff = (got - expected_half_pi).abs();
assert!(diff <= 1, "half_pi: got {got}, expected {expected_half_pi}, diff {diff} > 1 LSB");
let expected_quarter_pi: i128 = 78_539_816_339_744_830_961_566_084_581_987_572_105;
let got = D38::<38>::quarter_pi().to_bits();
let diff = (got - expected_quarter_pi).abs();
assert!(diff <= 1, "quarter_pi: got {got}, expected {expected_quarter_pi}, diff {diff} > 1 LSB");
}
#[test]
fn neg_pi_round_trip() {
if !crate::rounding::DEFAULT_IS_HALF_TO_EVEN { return; }
let pi = D38s12::pi();
let neg_pi = -pi;
assert_eq!(neg_pi.to_bits(), -3_141_592_653_590_i128);
}
}