#[doc(hidden)]
#[macro_export]
macro_rules! decl_pow10_cached {
(with_const_table, $max_scale:literal) => {
pub(crate) const POW10_TABLE_MAX_W: u32 = ($max_scale as u32) + GUARD;
pub(crate) static POW10_TABLE: [W; (POW10_TABLE_MAX_W + 1) as usize] = {
let mut table = [<W>::from_u128(0); (POW10_TABLE_MAX_W + 1) as usize];
let ten = <W>::from_u128(10);
table[0] = <W>::from_u128(1);
let mut i: usize = 1;
let len = (POW10_TABLE_MAX_W + 1) as usize;
while i < len {
table[i] = table[i - 1].wrapping_mul(ten);
i += 1;
}
table
};
#[inline]
pub(crate) fn pow10_cached(w: u32) -> W {
if w <= POW10_TABLE_MAX_W {
return unsafe { *POW10_TABLE.get_unchecked(w as usize) };
}
cached(&POW10_CACHE_GET, w, pow10)
}
};
(no_const_table, $max_scale:literal) => {
#[inline]
pub(crate) fn pow10_cached(w: u32) -> W {
cached(&POW10_CACHE_GET, w, pow10)
}
};
}
pub(crate) use decl_pow10_cached;
macro_rules! decl_wide_transcendental {
($Type:ident, $Storage:ty, $Work:ty, $core:ident, $max_scale:literal) => {
$crate::macros::wide_transcendental::decl_wide_transcendental!(
$Type, $Storage, $Work, $core, $max_scale, with_const_table
);
};
($Type:ident, $Storage:ty, $Work:ty, $core:ident, $max_scale:literal, $table_mode:ident) => {
pub(crate) mod $core {
#![allow(unused)]
pub(crate) type W = $Work;
pub(crate) const GUARD: u32 = 30;
#[inline]
pub(crate) const fn guard_agm(scale: u32) -> u32 {
if scale > GUARD - 4 {
scale - GUARD + 4
} else {
4
}
}
pub(crate) fn exp_agm_k_lift_from_w(v_w_at_scale: W, scale: u32) -> u32 {
let av = abs(v_w_at_scale);
let bl_v = bit_length(av);
let bl_one_s = bit_length(pow10_cached(scale));
if bl_v <= bl_one_s {
return 5;
}
let log2_int_part = bl_v - bl_one_s + 1;
let int_part_upper = if log2_int_part >= 128 {
u128::MAX
} else {
1u128 << log2_int_part
};
let k_lift_u128 = int_part_upper.saturating_mul(4343) / 10000 + 5;
if k_lift_u128 > u32::MAX as u128 {
u32::MAX
} else {
k_lift_u128 as u32
}
}
const SERIES_CAP: u128 = 20_000;
#[inline]
pub(crate) fn lit(n: u128) -> W {
$crate::wide_int::wide_cast(n)
}
#[inline]
pub(crate) fn zero() -> W {
lit(0)
}
#[inline]
fn abs(v: W) -> W {
if v < lit(0) { -v } else { v }
}
#[inline]
pub(crate) fn pow10(n: u32) -> W {
lit(10).pow(n)
}
$crate::macros::wide_transcendental::decl_pow10_cached!($table_mode, $max_scale);
#[inline]
pub(crate) fn one(w: u32) -> W {
pow10_cached(w)
}
#[inline]
fn round_div(n: W, d: W) -> W {
let (q, r) = n.div_rem(d);
if r == lit(0) {
return q;
}
let ar = abs(r);
let comp = abs(d) - ar;
let cmp_r = ar.cmp(&comp);
let q_is_odd = q.bit(0);
let result_positive = (n < lit(0)) == (d < lit(0));
if $crate::support::rounding::should_bump(
$crate::support::rounding::RoundingMode::HalfToEven,
cmp_r,
q_is_odd,
result_positive,
) {
if result_positive { q + lit(1) } else { q - lit(1) }
} else {
q
}
}
#[inline]
fn round_div_pow10(n: W, w: u32) -> W {
if w == 0 {
return n;
}
if w <= 38 {
return $crate::algos::mg_divide::div_wide_pow10_with::<W, { <W as $crate::wide_int::WideInt>::U128_LIMBS }>(
n,
w,
$crate::support::rounding::RoundingMode::HalfToEven,
);
}
$crate::algos::newton_reciprocal::dispatch_wide_pow10_with::<W, { <W as $crate::wide_int::WideInt>::U128_LIMBS }>(
n,
w,
$crate::support::rounding::RoundingMode::HalfToEven,
)
}
#[inline]
pub(crate) fn mul(a: W, b: W, w: u32) -> W {
round_div_pow10(a * b, w)
}
#[inline]
pub(crate) fn mul_cached(a: W, b: W, pow10_w: W) -> W {
round_div(a * b, pow10_w)
}
#[inline]
pub(crate) fn div(a: W, b: W, w: u32) -> W {
round_div(a * pow10_cached(w), b)
}
#[inline]
pub(crate) fn div_cached(a: W, b: W, pow10_w: W) -> W {
round_div(a * pow10_w, b)
}
#[inline]
fn mul_u(a: W, n: u128) -> W {
if n <= u64::MAX as u128 {
a.checked_mul_u64(n as u64)
} else {
a * lit(n)
}
}
pub(crate) fn bit_length(v: W) -> u32 {
W::BITS - abs(v).leading_zeros()
}
pub(crate) fn sqrt_fixed(v: W, w: u32) -> W {
let av = abs(v);
debug_assert!(
bit_length(av) + (w as u32) * 4 < W::BITS,
"sqrt_fixed: |v| * 10^w overflows the working width"
);
let n = av * pow10_cached(w);
#[cfg(feature = "std")]
{
if bit_length(n) < 1000 && n > zero() {
let seed_f64 = n.as_f64().sqrt();
let seed = W::from_f64(seed_f64);
let x0 = if seed <= zero() { lit(1) } else { seed };
let mut x = (x0 + n / x0) >> 1;
loop {
let y = (x + n / x) >> 1;
if y >= x {
return x;
}
x = y;
}
}
}
n.isqrt()
}
pub(crate) fn to_work(raw: $Storage) -> W {
$crate::wide_int::wide_cast::<$Storage, W>(raw) * pow10_cached(GUARD)
}
pub(crate) fn to_work_w(raw: $Storage, working_digits: u32) -> W {
$crate::wide_int::wide_cast::<$Storage, W>(raw) * pow10_cached(working_digits)
}
pub(crate) fn round_to_storage(v: W, w: u32, target: u32) -> $Storage {
round_to_storage_with(v, w, target, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
pub(crate) fn round_to_storage_with(
v: W,
w: u32,
target: u32,
mode: $crate::support::rounding::RoundingMode,
) -> $Storage {
let shift = w - target;
let rounded = if shift == 0 {
v
} else if shift <= 38 {
$crate::algos::mg_divide::div_wide_pow10_with::<W, { <W as $crate::wide_int::WideInt>::U128_LIMBS }>(v, shift, mode)
} else {
$crate::algos::newton_reciprocal::dispatch_wide_pow10_with::<W, { <W as $crate::wide_int::WideInt>::U128_LIMBS }>(v, shift, mode)
};
let max_w = $crate::wide_int::wide_cast::<$Storage, W>(<$Storage>::MAX);
let min_w = $crate::wide_int::wide_cast::<$Storage, W>(<$Storage>::MIN);
if rounded > max_w || rounded < min_w {
panic!(concat!(
stringify!($Type),
" strict transcendental: result out of range"
));
}
$crate::wide_int::wide_cast::<W, $Storage>(rounded)
}
pub(crate) fn round_to_nearest_int(v: W, w: u32) -> i128 {
let divisor = pow10_cached(w);
let (q, r) = v.div_rem(divisor);
let half = divisor >> 1;
let qi = if abs(r) >= half {
if v < lit(0) { q - lit(1) } else { q + lit(1) }
} else {
q
};
$crate::wide_int::wide_cast::<W, i128>(qi)
}
#[inline]
fn scale_by_k(c: W, k: i128) -> W {
if k >= 0 {
mul_u(c, k as u128)
} else {
-mul_u(c, k.unsigned_abs())
}
}
pub(crate) fn ln2(w: u32) -> W {
cached(&LN2_CACHE_GET, w, ln2_compute)
}
fn ln2_compute(w: u32) -> W {
let t = one(w) / lit(3);
let t2 = mul(t, t, w);
let mut sum = t;
let mut term = t;
let mut j: u128 = 1;
loop {
term = mul(term, t2, w);
let contrib = term / lit(2 * j + 1);
if contrib == zero() {
break;
}
sum = sum + contrib;
j += 1;
if j > SERIES_CAP {
break;
}
}
sum + sum
}
pub(crate) fn ln_fixed(v_w: W, w: u32) -> W {
let one_w = one(w);
let two_w = one_w + one_w;
let pow10_w = one_w;
let mut k: i32 = bit_length(v_w) as i32 - bit_length(one_w) as i32;
let mut m_w = loop {
let m = if k >= 0 {
v_w >> (k as u32)
} else {
v_w << ((-k) as u32)
};
if m >= two_w {
k += 1;
} else if m < one_w {
k -= 1;
} else {
break m;
}
};
let p_bits = w.saturating_mul(3).saturating_add(1);
let mut sqrt_l: u32 = 0;
{
let mut n: u32 = 0;
while (n + 1) * (n + 1) <= p_bits {
n += 1;
}
sqrt_l = n / 4;
}
let mut i = 0;
while i < sqrt_l {
m_w = sqrt_fixed(m_w, w);
i += 1;
}
let t = div_cached(m_w - one_w, m_w + one_w, pow10_w);
let t2 = mul_cached(t, t, pow10_w);
let mut sum = t;
let mut term = t;
let mut j: u128 = 1;
loop {
term = mul_cached(term, t2, pow10_w);
let contrib = term / lit(2 * j + 1);
if contrib == zero() {
break;
}
sum = sum + contrib;
j += 1;
if j > SERIES_CAP {
break;
}
}
let ln_m = sum << (sqrt_l + 1);
scale_by_k(ln2(w), k as i128) + ln_m
}
pub(crate) fn ln10(w: u32) -> W {
cached(&LN10_CACHE_GET, w, ln10_compute)
}
fn ln10_compute(w: u32) -> W {
ln_fixed(one(w) * lit(10), w)
}
pub(crate) fn ln_fixed_agm(v_w: W, w: u32) -> W {
let one_w = one(w);
let p_bits = ((w as i32) * 332 + 99) / 100;
let bl_v = bit_length(v_w) as i32;
let bl_one = bit_length(one_w) as i32;
let safety = 2 + ((p_bits.max(1) as u32).ilog2() / 2) as i32;
let mut m: i32 = (p_bits / 2) + safety + bl_one - bl_v;
if m < 2 {
m = 2;
}
let cap = (W::BITS as i32) - bl_v - 2;
if cap > 0 && m > cap {
m = cap;
}
debug_assert!(
m > 0,
"ln_fixed_agm: working-int width too small for this scale"
);
let s_w = v_w << (m as u32);
let y_w = div(lit(4) * one_w, s_w, w);
let mut a = one_w;
let mut b = y_w;
let iter_cap = 80u32;
let pow10_w_agm = pow10_cached(w);
for _ in 0..iter_cap {
let next_a = (a + b) >> 1;
let next_b = sqrt_fixed(mul_cached(a, b, pow10_w_agm), w);
let d = if next_a >= next_b { next_a - next_b } else { next_b - next_a };
a = next_a;
b = next_b;
if d <= lit(2) {
break;
}
}
let pi_w = pi(w);
let agm_part = div(pi_w, a + a, w);
agm_part - scale_by_k(ln2(w), m as i128)
}
pub(crate) fn exp_fixed_agm(v_w: W, w: u32) -> W {
let one_w = one(w);
let l2 = ln2(w);
let k = round_to_nearest_int(div(v_w, l2, w), w);
let s = v_w - scale_by_k(l2, k);
let s2 = mul(s, s, w);
let mut x = one_w + s + (s2 >> 1);
if x <= lit(0) {
x = one_w;
}
let iter_cap = 80u32;
for _ in 0..iter_cap {
let ln_x = ln_fixed_agm(x, w);
let delta = s - ln_x;
if abs(delta) <= lit(2) {
x = mul(x, one_w + delta, w);
break;
}
x = mul(x, one_w + delta, w);
}
if k >= 0 {
let shift = k as u32;
if bit_length(x) + shift >= W::BITS {
panic!(concat!(
stringify!($Type),
"::exp: result overflows the representable range"
));
}
x << shift
} else {
let neg_k = (-k) as u128;
if neg_k >= bit_length(x) as u128 {
return zero();
}
x >> (neg_k as u32)
}
}
pub(crate) fn exp_fixed(v_w: W, w: u32) -> W {
#[cfg(feature = "perf-trace")]
let _exp_span = $crate::tracing::info_span!(concat!(
stringify!($Type), "::exp_fixed"
)).entered();
#[cfg(feature = "perf-trace")]
let _reduce_span = $crate::tracing::info_span!("range_reduce").entered();
let one_w_pre = one(w);
let l2_pre = ln2(w);
let pow10_w_pre = one_w_pre;
let k = round_to_nearest_int(div_cached(v_w, l2_pre, pow10_w_pre), w);
let abs_k_u128 = if k < 0 { -k } else { k } as u128;
let extra: u32 = if abs_k_u128 == 0 {
0
} else {
let digits = (abs_k_u128 * 30103 + 99_999) / 100_000;
let capped = digits.min((<W>::BITS / 4) as u128) as u32;
capped + 12 + (capped >> 2)
};
let w_ext = w + extra;
let v_ext = if extra == 0 { v_w } else { v_w * pow10(extra) };
let one_w = one(w_ext);
let l2 = ln2(w_ext);
let pow10_w = one_w;
let s = v_ext - scale_by_k(l2, k);
let p_bits = w_ext.saturating_mul(3).saturating_add(1);
let mut n: u32 = 1;
while (n + 1) * (n + 1) <= p_bits {
n += 1;
}
let s_red = s >> n;
#[cfg(feature = "perf-trace")]
drop(_reduce_span);
#[cfg(feature = "perf-trace")]
let _taylor_span = $crate::tracing::info_span!("taylor_series").entered();
let mut sum = one_w + s_red;
let mut term = s_red;
let mut iter: u128 = 2;
loop {
term = mul_cached(term, s_red, pow10_w) / lit(iter);
if term == zero() {
break;
}
sum = sum + term;
iter += 1;
if iter > SERIES_CAP {
break;
}
}
#[cfg(feature = "perf-trace")]
drop(_taylor_span);
#[cfg(feature = "perf-trace")]
let _sqr_span = $crate::tracing::info_span!("postfix_squarings").entered();
let mut squared = sum;
let mut i = 0;
while i < n {
squared = mul_cached(squared, squared, pow10_w);
i += 1;
}
let sum = squared;
#[cfg(feature = "perf-trace")]
drop(_sqr_span);
#[cfg(feature = "perf-trace")]
let _reasm_span = $crate::tracing::info_span!("reassemble").entered();
let scaled_at_w_ext = if k >= 0 {
let shift = k as u32;
if bit_length(sum) + shift >= W::BITS {
panic!(concat!(
stringify!($Type),
"::exp: result overflows the representable range"
));
}
sum << shift
} else {
let neg_k = -k as u128;
if neg_k >= bit_length(sum) as u128 {
return zero();
}
sum >> (neg_k as u32)
};
if extra == 0 {
scaled_at_w_ext
} else {
round_div_pow10(scaled_at_w_ext, extra)
}
}
pub(crate) fn atan_taylor(x: W, w: u32) -> W {
let pow10_w = pow10_cached(w);
let x2 = mul_cached(x, x, pow10_w);
let mut sum = x;
let mut term = x;
let mut k: u128 = 1;
loop {
term = mul_cached(term, x2, pow10_w);
let contrib = term / lit(2 * k + 1);
if contrib == zero() {
break;
}
if k % 2 == 1 {
sum = sum - contrib;
} else {
sum = sum + contrib;
}
k += 1;
if k > SERIES_CAP {
break;
}
}
sum
}
pub(crate) fn pi(w: u32) -> W {
cached(&PI_CACHE_GET, w, pi_compute)
}
fn pi_compute(w: u32) -> W {
let a = atan_taylor(one(w) / lit(5), w);
let b = atan_taylor(one(w) / lit(239), w);
mul_u(a, 16) - mul_u(b, 4)
}
#[cfg(feature = "std")]
fn cached<F>(slot_get: &dyn Fn() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>>, w: u32, compute: F) -> W
where
F: FnOnce(u32) -> W,
{
let slot = slot_get();
let hit = slot.with(|c| {
let cache = c.borrow();
for &(cw, cv) in cache.iter() {
if cw == w {
return ::core::option::Option::Some(cv);
}
}
::core::option::Option::None
});
if let ::core::option::Option::Some(v) = hit {
return v;
}
let v = compute(w);
slot.with(|c| {
c.borrow_mut().push((w, v));
});
v
}
#[cfg(not(feature = "std"))]
fn cached<F>(_slot_get: &(), w: u32, compute: F) -> W
where
F: FnOnce(u32) -> W,
{
compute(w)
}
#[cfg(feature = "std")]
fn pi_cache_get() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> {
::std::thread_local! {
static SLOT: ::core::cell::RefCell<alloc::vec::Vec<(u32, W)>> = const {
::core::cell::RefCell::new(alloc::vec::Vec::new())
};
}
&SLOT
}
#[cfg(feature = "std")]
fn ln2_cache_get() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> {
::std::thread_local! {
static SLOT: ::core::cell::RefCell<alloc::vec::Vec<(u32, W)>> = const {
::core::cell::RefCell::new(alloc::vec::Vec::new())
};
}
&SLOT
}
#[cfg(feature = "std")]
fn ln10_cache_get() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> {
::std::thread_local! {
static SLOT: ::core::cell::RefCell<alloc::vec::Vec<(u32, W)>> = const {
::core::cell::RefCell::new(alloc::vec::Vec::new())
};
}
&SLOT
}
#[cfg(feature = "std")]
fn pow10_cache_get() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> {
::std::thread_local! {
static SLOT: ::core::cell::RefCell<alloc::vec::Vec<(u32, W)>> = const {
::core::cell::RefCell::new(alloc::vec::Vec::new())
};
}
&SLOT
}
#[cfg(feature = "std")]
const PI_CACHE_GET: fn() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> = pi_cache_get;
#[cfg(feature = "std")]
const LN2_CACHE_GET: fn() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> = ln2_cache_get;
#[cfg(feature = "std")]
const LN10_CACHE_GET: fn() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> = ln10_cache_get;
#[cfg(feature = "std")]
const POW10_CACHE_GET: fn() -> &'static ::std::thread::LocalKey<::core::cell::RefCell<alloc::vec::Vec<(u32, W)>>> = pow10_cache_get;
#[cfg(not(feature = "std"))]
const PI_CACHE_GET: () = ();
#[cfg(not(feature = "std"))]
const LN2_CACHE_GET: () = ();
#[cfg(not(feature = "std"))]
const LN10_CACHE_GET: () = ();
#[cfg(not(feature = "std"))]
const POW10_CACHE_GET: () = ();
pub(crate) fn half_pi(w: u32) -> W {
pi(w) >> 1
}
fn sin_taylor(r: W, w: u32) -> W {
let pow10_w = pow10_cached(w);
let r2 = mul_cached(r, r, pow10_w);
let mut sum = r;
let mut term = r;
let mut k: u128 = 1;
loop {
term = mul_cached(term, r2, pow10_w) / lit((2 * k) * (2 * k + 1));
if term == zero() {
break;
}
if k % 2 == 1 {
sum = sum - term;
} else {
sum = sum + term;
}
k += 1;
if k > SERIES_CAP {
break;
}
}
sum
}
fn cos_taylor(r: W, w: u32) -> W {
let pow10_w = pow10_cached(w);
let r2 = mul_cached(r, r, pow10_w);
let one_w = one(w);
let mut sum = one_w;
let mut term = one_w;
let mut k: u128 = 1;
loop {
term = mul_cached(term, r2, pow10_w)
/ lit((2 * k - 1) * (2 * k));
if term == zero() {
break;
}
if k % 2 == 1 {
sum = sum - term;
} else {
sum = sum + term;
}
k += 1;
if k > SERIES_CAP {
break;
}
}
sum
}
pub(crate) fn sin_fixed(v_w: W, w: u32) -> W {
let pi_w = pi(w);
let tau = pi_w + pi_w;
let hp = pi_w >> 1;
let qp = hp >> 1; let q = round_to_nearest_int(div(v_w, tau, w), w);
let r = v_w - scale_by_k(tau, q);
let neg = r < zero();
let abs_r = if neg { -r } else { r };
let reduced = if abs_r >= hp { pi_w - abs_r } else { abs_r };
let s = if reduced > qp {
cos_taylor(hp - reduced, w)
} else {
sin_taylor(reduced, w)
};
if neg { -s } else { s }
}
pub(crate) fn sin_cos_fixed(v_w: W, w: u32) -> (W, W) {
let pi_w = pi(w);
let tau = pi_w + pi_w;
let hp = pi_w >> 1;
let qp = hp >> 1;
let q = round_to_nearest_int(div(v_w, tau, w), w);
let r = v_w - scale_by_k(tau, q);
let sin_neg = r < zero();
let abs_r = if sin_neg { -r } else { r };
let cos_neg = abs_r > hp; let reduced = if cos_neg { pi_w - abs_r } else { abs_r };
let s_abs = if reduced > qp {
cos_taylor(hp - reduced, w)
} else {
sin_taylor(reduced, w)
};
let one_w = one(w);
let s2 = mul(s_abs, s_abs, w);
let cos_abs = sqrt_fixed(one_w - s2, w);
let sin_result = if sin_neg { -s_abs } else { s_abs };
let cos_result = if cos_neg { -cos_abs } else { cos_abs };
(sin_result, cos_result)
}
pub(crate) fn cos_fixed(v_w: W, w: u32) -> W {
sin_fixed(half_pi(w) - v_w, w)
}
pub(crate) fn atan_fixed(v_w: W, w: u32) -> W {
let one_w = one(w);
let sign = v_w < zero();
let mut x = if sign { -v_w } else { v_w };
let mut add_half_pi = false;
if x > one_w {
x = div(one_w, x, w);
add_half_pi = true;
}
let halvings: u32 = if w < 60 {
5 } else if w < 110 {
6 } else {
7 };
let pow10_w = pow10_cached(w);
for _ in 0..halvings {
let x2 = mul_cached(x, x, pow10_w);
let denom = one_w + sqrt_fixed(one_w + x2, w);
x = div_cached(x, denom, pow10_w);
}
let mut result = atan_taylor(x, w) << halvings;
if add_half_pi {
result = half_pi(w) - result;
}
if sign { -result } else { result }
}
}
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
#[must_use]
pub fn ln_strict(self) -> Self {
<Self as $crate::policy::ln::LnPolicy>::ln_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn ln_strict_agm(self) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::ln_agm: argument must be positive"));
}
let w_prime = SCALE + $core::GUARD + $core::guard_agm(SCALE);
let r = $core::ln_fixed_agm(
$core::to_work_w(raw, $core::GUARD + $core::guard_agm(SCALE)),
w_prime,
);
Self::from_bits($core::round_to_storage(r, w_prime, SCALE))
}
#[inline]
#[must_use]
pub fn exp_strict_agm(self) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let raw_w = $core::to_work_w(raw, 0);
let k_lift = $core::exp_agm_k_lift_from_w(raw_w, SCALE);
let lift = $core::GUARD + $core::guard_agm(SCALE) + k_lift;
let w_prime = SCALE + lift;
let r = $core::exp_fixed_agm(
$core::to_work_w(raw, lift),
w_prime,
);
Self::from_bits($core::round_to_storage(r, w_prime, SCALE))
}
#[inline]
#[must_use]
pub fn log_strict(self, base: Self) -> Self {
let raw = self.to_bits();
let braw = base.to_bits();
let z = $crate::macros::wide_roots::wide_lit!($Storage, "0");
if raw <= z {
panic!(concat!(stringify!($Type), "::log: argument must be positive"));
}
if braw <= z {
panic!(concat!(stringify!($Type), "::log: base must be positive"));
}
let w = SCALE + $core::GUARD;
let ln_b = $core::ln_fixed($core::to_work(braw), w);
if ln_b == $core::zero() {
panic!(concat!(stringify!($Type), "::log: base must not equal 1"));
}
let r = $core::div($core::ln_fixed($core::to_work(raw), w), ln_b, w);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn log2_strict(self) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log2: argument must be positive"));
}
let w = SCALE + $core::GUARD;
let r = $core::div($core::ln_fixed($core::to_work(raw), w), $core::ln2(w), w);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn log10_strict(self) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log10: argument must be positive"));
}
let w = SCALE + $core::GUARD;
let r = $core::div($core::ln_fixed($core::to_work(raw), w), $core::ln10(w), w);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn exp_strict(self) -> Self {
<Self as $crate::policy::exp::ExpPolicy>::exp_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn exp2_strict(self) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let w = SCALE + $core::GUARD;
let arg = $core::mul($core::to_work(raw), $core::ln2(w), w);
let r = $core::exp_fixed(arg, w);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn powf_strict(self, exp: Self) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
if let ::core::option::Option::Some(n) = Self::powf_exp_as_small_int(exp) {
return self.powi(n);
}
let w = SCALE + $core::GUARD;
let ln_x = $core::ln_fixed($core::to_work(raw), w);
let y = $core::to_work(exp.to_bits());
let r = $core::exp_fixed($core::mul(y, ln_x, w), w);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
const INT_POWF_FAST_PATH_THRESHOLD: i32 = 64;
#[inline]
fn powf_exp_as_small_int(exp: Self) -> ::core::option::Option<i32> {
let raw = exp.to_bits();
let mult = Self::multiplier();
let zero = $crate::macros::wide_roots::wide_lit!($Storage, "0");
if raw % mult != zero {
return ::core::option::Option::None;
}
let q = raw / mult;
let lo = $crate::macros::wide_roots::wide_lit!(
$Storage,
"-64"
);
let hi = $crate::macros::wide_roots::wide_lit!(
$Storage,
"64"
);
if q < lo || q > hi {
return ::core::option::Option::None;
}
let q_i128: i128 = $crate::wide_int::wide_cast::<$Storage, i128>(q);
::core::option::Option::Some(q_i128 as i32)
}
#[inline]
#[must_use]
pub fn sin_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::sin_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn cos_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::cos_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn sin_cos_strict(self) -> (Self, Self) {
let w = SCALE + $core::GUARD;
let (s, c) = $core::sin_cos_fixed($core::to_work(self.to_bits()), w);
(
Self::from_bits($core::round_to_storage(s, w, SCALE)),
Self::from_bits($core::round_to_storage(c, w, SCALE)),
)
}
#[inline]
#[must_use]
pub fn tan_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::tan_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn atan_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::atan_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn asin_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::asin_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn acos_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::acos_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn atan2_strict(self, other: Self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::atan2_impl(
self,
other,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn sinh_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::sinh_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn cosh_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::cosh_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn tanh_strict(self) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::tanh_impl(
self,
$crate::support::rounding::DEFAULT_ROUNDING_MODE,
)
}
#[inline]
#[must_use]
pub fn sinh_cosh_strict(self) -> (Self, Self) {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let sinh = (ex - enx) >> 1;
let cosh = (ex + enx) >> 1;
(
Self::from_bits($core::round_to_storage(sinh, w, SCALE)),
Self::from_bits($core::round_to_storage(cosh, w, SCALE)),
)
}
#[inline]
#[must_use]
pub fn asinh_strict(self) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(raw);
let ax = if v < $core::zero() { -v } else { v };
let inner = if ax >= one_w {
let inv = $core::div(one_w, ax, w);
let root = $core::sqrt_fixed(one_w + $core::mul(inv, inv, w), w);
$core::ln_fixed(ax, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(ax, ax, w) + one_w, w);
$core::ln_fixed(ax + root, w)
};
let signed = if raw < $crate::macros::wide_roots::wide_lit!($Storage, "0") {
-inner
} else {
inner
};
Self::from_bits($core::round_to_storage(signed, w, SCALE))
}
#[inline]
#[must_use]
pub fn acosh_strict(self) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
if v < one_w {
panic!(concat!(stringify!($Type), "::acosh: argument must be >= 1"));
}
let two_w = one_w + one_w;
let inner = if v >= two_w {
let inv = $core::div(one_w, v, w);
let root = $core::sqrt_fixed(one_w - $core::mul(inv, inv, w), w);
$core::ln_fixed(v, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(v, v, w) - one_w, w);
$core::ln_fixed(v + root, w)
};
Self::from_bits($core::round_to_storage(inner, w, SCALE))
}
#[inline]
#[must_use]
pub fn atanh_strict(self) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
let ax = if v < $core::zero() { -v } else { v };
if ax >= one_w {
panic!(concat!(stringify!($Type), "::atanh: argument out of domain (-1, 1)"));
}
let ratio = $core::div(one_w + v, one_w - v, w);
let r = $core::ln_fixed(ratio, w) >> 1;
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn to_degrees_strict(self) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
debug_assert!(
$core::bit_length(v) + 8 < <$Work>::BITS,
concat!(stringify!($Type),
"::to_degrees: |self| * 180 overflows the working integer")
);
let r = $core::div(
v * $crate::macros::wide_roots::wide_lit!($Work, "180"),
$core::pi(w),
w,
);
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn to_radians_strict(self) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let r = $core::mul(v, $core::pi(w), w)
/ $crate::macros::wide_roots::wide_lit!($Work, "180");
Self::from_bits($core::round_to_storage(r, w, SCALE))
}
#[inline]
#[must_use]
pub fn ln_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::ln::LnPolicy>::ln_impl(self, mode)
}
#[inline]
#[must_use]
pub fn ln_strict_agm_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::ln_agm: argument must be positive"));
}
let w_prime = SCALE + $core::GUARD + $core::guard_agm(SCALE);
let r = $core::ln_fixed_agm(
$core::to_work_w(raw, $core::GUARD + $core::guard_agm(SCALE)),
w_prime,
);
Self::from_bits($core::round_to_storage_with(r, w_prime, SCALE, mode))
}
#[inline]
#[must_use]
pub fn exp_strict_agm_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let raw_w = $core::to_work_w(raw, 0);
let k_lift = $core::exp_agm_k_lift_from_w(raw_w, SCALE);
let lift = $core::GUARD + $core::guard_agm(SCALE) + k_lift;
let w_prime = SCALE + lift;
let r = $core::exp_fixed_agm(
$core::to_work_w(raw, lift),
w_prime,
);
Self::from_bits($core::round_to_storage_with(r, w_prime, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log_strict_with(self, base: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
let braw = base.to_bits();
let z = $crate::macros::wide_roots::wide_lit!($Storage, "0");
if raw <= z {
panic!(concat!(stringify!($Type), "::log: argument must be positive"));
}
if braw <= z {
panic!(concat!(stringify!($Type), "::log: base must be positive"));
}
let w = SCALE + $core::GUARD;
let ln_b = $core::ln_fixed($core::to_work(braw), w);
if ln_b == $core::zero() {
panic!(concat!(stringify!($Type), "::log: base must not equal 1"));
}
let r = $core::div($core::ln_fixed($core::to_work(raw), w), ln_b, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log2_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log2: argument must be positive"));
}
let w = SCALE + $core::GUARD;
let r = $core::div($core::ln_fixed($core::to_work(raw), w), $core::ln2(w), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log10_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log10: argument must be positive"));
}
let w = SCALE + $core::GUARD;
let r = $core::div($core::ln_fixed($core::to_work(raw), w), $core::ln10(w), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn exp_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::exp::ExpPolicy>::exp_impl(self, mode)
}
#[inline]
#[must_use]
pub fn exp2_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let w = SCALE + $core::GUARD;
let arg = $core::mul($core::to_work(raw), $core::ln2(w), w);
let r = $core::exp_fixed(arg, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn powf_strict_with(self, exp: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
if let ::core::option::Option::Some(n) = Self::powf_exp_as_small_int(exp) {
return self.powi(n);
}
let w = SCALE + $core::GUARD;
let ln_x = $core::ln_fixed($core::to_work(raw), w);
let y = $core::to_work(exp.to_bits());
let r = $core::exp_fixed($core::mul(y, ln_x, w), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sin_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::sin_impl(self, mode)
}
#[inline]
#[must_use]
pub fn cos_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::cos_impl(self, mode)
}
#[inline]
#[must_use]
pub fn tan_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::tan_impl(self, mode)
}
#[inline]
#[must_use]
pub fn atan_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
<Self as $crate::policy::trig::TrigPolicy>::atan_impl(self, mode)
}
#[inline]
#[must_use]
pub fn asin_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
let abs_v = if v < $core::zero() { -v } else { v };
if abs_v > one_w {
panic!(concat!(stringify!($Type), "::asin: argument out of domain [-1, 1]"));
}
let half_w = one_w >> 1;
let r = if abs_v == one_w {
let hp = $core::half_pi(w);
if v < $core::zero() { -hp } else { hp }
} else if abs_v <= half_w {
let denom = $core::sqrt_fixed(one_w - $core::mul(v, v, w), w);
$core::atan_fixed($core::div(v, denom, w), w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = $core::sqrt_fixed(inner, w);
let inner_denom = $core::sqrt_fixed(
one_w - $core::mul(inner_sqrt, inner_sqrt, w),
w,
);
let inner_asin = $core::atan_fixed(
$core::div(inner_sqrt, inner_denom, w),
w,
);
let result_abs = $core::half_pi(w) - inner_asin - inner_asin;
if v < $core::zero() { -result_abs } else { result_abs }
};
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn acos_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
let abs_v = if v < $core::zero() { -v } else { v };
if abs_v > one_w {
panic!(concat!(stringify!($Type), "::acos: argument out of domain [-1, 1]"));
}
let half_w = one_w >> 1;
let asin_w = if abs_v == one_w {
let hp = $core::half_pi(w);
if v < $core::zero() { -hp } else { hp }
} else if abs_v <= half_w {
let denom = $core::sqrt_fixed(one_w - $core::mul(v, v, w), w);
$core::atan_fixed($core::div(v, denom, w), w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = $core::sqrt_fixed(inner, w);
let inner_denom = $core::sqrt_fixed(
one_w - $core::mul(inner_sqrt, inner_sqrt, w),
w,
);
let inner_asin = $core::atan_fixed(
$core::div(inner_sqrt, inner_denom, w),
w,
);
let result_abs = $core::half_pi(w) - inner_asin - inner_asin;
if v < $core::zero() { -result_abs } else { result_abs }
};
let r = $core::half_pi(w) - asin_w;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn atan2_strict_with(self, other: Self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let z = $crate::macros::wide_roots::wide_lit!($Storage, "0");
let yraw = self.to_bits();
let xraw = other.to_bits();
let r = if xraw == z {
if yraw > z {
$core::half_pi(w)
} else if yraw < z {
-$core::half_pi(w)
} else {
$core::zero()
}
} else {
let y = $core::to_work(yraw);
let x = $core::to_work(xraw);
let zero_w = $core::zero();
let abs_y = if y < zero_w { -y } else { y };
let abs_x = if x < zero_w { -x } else { x };
let base = if abs_x >= abs_y {
$core::atan_fixed($core::div(y, x, w), w)
} else {
let inv = $core::atan_fixed($core::div(x, y, w), w);
let hp = $core::half_pi(w);
let same_sign = (y < zero_w) == (x < zero_w);
if same_sign { hp - inv } else { -hp - inv }
};
if xraw > z {
base
} else if yraw >= z {
base + $core::pi(w)
} else {
base - $core::pi(w)
}
};
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sinh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = (ex - enx) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn cosh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = (ex + enx) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn tanh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = $core::div(ex - enx, ex + enx, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn asinh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(raw);
let ax = if v < $core::zero() { -v } else { v };
let inner = if ax >= one_w {
let inv = $core::div(one_w, ax, w);
let root = $core::sqrt_fixed(one_w + $core::mul(inv, inv, w), w);
$core::ln_fixed(ax, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(ax, ax, w) + one_w, w);
$core::ln_fixed(ax + root, w)
};
let signed = if raw < $crate::macros::wide_roots::wide_lit!($Storage, "0") {
-inner
} else {
inner
};
Self::from_bits($core::round_to_storage_with(signed, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn acosh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
if v < one_w {
panic!(concat!(stringify!($Type), "::acosh: argument must be >= 1"));
}
let two_w = one_w + one_w;
let inner = if v >= two_w {
let inv = $core::div(one_w, v, w);
let root = $core::sqrt_fixed(one_w - $core::mul(inv, inv, w), w);
$core::ln_fixed(v, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(v, v, w) - one_w, w);
$core::ln_fixed(v + root, w)
};
Self::from_bits($core::round_to_storage_with(inner, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn atanh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let one_w = $core::one(w);
let v = $core::to_work(self.to_bits());
let ax = if v < $core::zero() { -v } else { v };
if ax >= one_w {
panic!(concat!(stringify!($Type), "::atanh: argument out of domain (-1, 1)"));
}
let ratio = $core::div(one_w + v, one_w - v, w);
let r = $core::ln_fixed(ratio, w) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn to_degrees_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
debug_assert!(
$core::bit_length(v) + 8 < <$Work>::BITS,
concat!(stringify!($Type),
"::to_degrees: |self| * 180 overflows the working integer")
);
let r = $core::div(
v * $crate::macros::wide_roots::wide_lit!($Work, "180"),
$core::pi(w),
w,
);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn to_radians_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> Self {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let r = $core::mul(v, $core::pi(w), w)
/ $crate::macros::wide_roots::wide_lit!($Work, "180");
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sin_cos_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> (Self, Self) {
let w = SCALE + $core::GUARD;
let (s, c) = $core::sin_cos_fixed($core::to_work(self.to_bits()), w);
(
Self::from_bits($core::round_to_storage_with(s, w, SCALE, mode)),
Self::from_bits($core::round_to_storage_with(c, w, SCALE, mode)),
)
}
#[inline]
#[must_use]
pub fn sinh_cosh_strict_with(self, mode: $crate::support::rounding::RoundingMode) -> (Self, Self) {
let w = SCALE + $core::GUARD;
let v = $core::to_work(self.to_bits());
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let sinh = (ex - enx) >> 1;
let cosh = (ex + enx) >> 1;
(
Self::from_bits($core::round_to_storage_with(sinh, w, SCALE, mode)),
Self::from_bits($core::round_to_storage_with(cosh, w, SCALE, mode)),
)
}
#[inline]
#[must_use]
pub fn ln_approx(self, working_digits: u32) -> Self {
self.ln_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn ln_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.ln_strict_with(mode);
}
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::ln: argument must be positive"));
}
let w = SCALE + working_digits;
let r = $core::ln_fixed($core::to_work_w(raw, working_digits), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log_approx(self, base: Self, working_digits: u32) -> Self {
self.log_approx_with(base, working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn log_approx_with(
self,
base: Self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.log_strict_with(base, mode);
}
let raw = self.to_bits();
let braw = base.to_bits();
let z = $crate::macros::wide_roots::wide_lit!($Storage, "0");
if raw <= z {
panic!(concat!(stringify!($Type), "::log: argument must be positive"));
}
if braw <= z {
panic!(concat!(stringify!($Type), "::log: base must be positive"));
}
let w = SCALE + working_digits;
let ln_b = $core::ln_fixed($core::to_work_w(braw, working_digits), w);
if ln_b == $core::zero() {
panic!(concat!(stringify!($Type), "::log: base must not equal 1"));
}
let r = $core::div(
$core::ln_fixed($core::to_work_w(raw, working_digits), w),
ln_b,
w,
);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log2_approx(self, working_digits: u32) -> Self {
self.log2_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn log2_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.log2_strict_with(mode);
}
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log2: argument must be positive"));
}
let w = SCALE + working_digits;
let r = $core::div(
$core::ln_fixed($core::to_work_w(raw, working_digits), w),
$core::ln2(w),
w,
);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn log10_approx(self, working_digits: u32) -> Self {
self.log10_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn log10_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.log10_strict_with(mode);
}
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
panic!(concat!(stringify!($Type), "::log10: argument must be positive"));
}
let w = SCALE + working_digits;
let r = $core::div(
$core::ln_fixed($core::to_work_w(raw, working_digits), w),
$core::ln10(w),
w,
);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn exp_approx(self, working_digits: u32) -> Self {
self.exp_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn exp_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.exp_strict_with(mode);
}
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let w = SCALE + working_digits;
let r = $core::exp_fixed($core::to_work_w(raw, working_digits), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn exp2_approx(self, working_digits: u32) -> Self {
self.exp2_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn exp2_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.exp2_strict_with(mode);
}
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ONE;
}
let w = SCALE + working_digits;
let arg = $core::mul($core::to_work_w(raw, working_digits), $core::ln2(w), w);
let r = $core::exp_fixed(arg, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn powf_approx(self, exp: Self, working_digits: u32) -> Self {
self.powf_approx_with(exp, working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn powf_approx_with(
self,
exp: Self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.powf_strict_with(exp, mode);
}
let raw = self.to_bits();
if raw <= $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
let w = SCALE + working_digits;
let ln_x = $core::ln_fixed($core::to_work_w(raw, working_digits), w);
let y = $core::to_work_w(exp.to_bits(), working_digits);
let r = $core::exp_fixed($core::mul(y, ln_x, w), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sin_approx(self, working_digits: u32) -> Self {
self.sin_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn sin_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.sin_strict_with(mode);
}
let w = SCALE + working_digits;
let r = $core::sin_fixed($core::to_work_w(self.to_bits(), working_digits), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn cos_approx(self, working_digits: u32) -> Self {
self.cos_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn cos_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.cos_strict_with(mode);
}
let w = SCALE + working_digits;
let arg = $core::to_work_w(self.to_bits(), working_digits) + $core::half_pi(w);
let r = $core::sin_fixed(arg, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sin_cos_approx(self, working_digits: u32) -> (Self, Self) {
self.sin_cos_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn sin_cos_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> (Self, Self) {
if working_digits == $core::GUARD {
return self.sin_cos_strict_with(mode);
}
let w = SCALE + working_digits;
let (s, c) = $core::sin_cos_fixed(
$core::to_work_w(self.to_bits(), working_digits),
w,
);
(
Self::from_bits($core::round_to_storage_with(s, w, SCALE, mode)),
Self::from_bits($core::round_to_storage_with(c, w, SCALE, mode)),
)
}
#[inline]
#[must_use]
pub fn tan_approx(self, working_digits: u32) -> Self {
self.tan_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn tan_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.tan_strict_with(mode);
}
let w = SCALE + working_digits;
let (sin_w, cos_w) = $core::sin_cos_fixed(
$core::to_work_w(self.to_bits(), working_digits),
w,
);
if cos_w == $core::zero() {
panic!(concat!(
stringify!($Type),
"::tan: cosine is zero (argument is an odd multiple of pi/2)"
));
}
let r = $core::div(sin_w, cos_w, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn atan_approx(self, working_digits: u32) -> Self {
self.atan_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn atan_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.atan_strict_with(mode);
}
let w = SCALE + working_digits;
let r = $core::atan_fixed($core::to_work_w(self.to_bits(), working_digits), w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn asin_approx(self, working_digits: u32) -> Self {
self.asin_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn asin_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.asin_strict_with(mode);
}
let w = SCALE + working_digits;
let one_w = $core::one(w);
let v = $core::to_work_w(self.to_bits(), working_digits);
let abs_v = if v < $core::zero() { -v } else { v };
if abs_v > one_w {
panic!(concat!(stringify!($Type), "::asin: argument out of domain [-1, 1]"));
}
let half_w = one_w >> 1;
let r = if abs_v == one_w {
let hp = $core::half_pi(w);
if v < $core::zero() { -hp } else { hp }
} else if abs_v <= half_w {
let denom = $core::sqrt_fixed(one_w - $core::mul(v, v, w), w);
$core::atan_fixed($core::div(v, denom, w), w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = $core::sqrt_fixed(inner, w);
let inner_denom = $core::sqrt_fixed(
one_w - $core::mul(inner_sqrt, inner_sqrt, w),
w,
);
let inner_asin = $core::atan_fixed(
$core::div(inner_sqrt, inner_denom, w),
w,
);
let result_abs = $core::half_pi(w) - inner_asin - inner_asin;
if v < $core::zero() { -result_abs } else { result_abs }
};
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn acos_approx(self, working_digits: u32) -> Self {
self.acos_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn acos_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.acos_strict_with(mode);
}
let w = SCALE + working_digits;
let one_w = $core::one(w);
let v = $core::to_work_w(self.to_bits(), working_digits);
let abs_v = if v < $core::zero() { -v } else { v };
if abs_v > one_w {
panic!(concat!(stringify!($Type), "::acos: argument out of domain [-1, 1]"));
}
let half_w = one_w >> 1;
let asin_w = if abs_v == one_w {
let hp = $core::half_pi(w);
if v < $core::zero() { -hp } else { hp }
} else if abs_v <= half_w {
let denom = $core::sqrt_fixed(one_w - $core::mul(v, v, w), w);
$core::atan_fixed($core::div(v, denom, w), w)
} else {
let inner = (one_w - abs_v) >> 1;
let inner_sqrt = $core::sqrt_fixed(inner, w);
let inner_denom = $core::sqrt_fixed(
one_w - $core::mul(inner_sqrt, inner_sqrt, w),
w,
);
let inner_asin = $core::atan_fixed(
$core::div(inner_sqrt, inner_denom, w),
w,
);
let result_abs = $core::half_pi(w) - inner_asin - inner_asin;
if v < $core::zero() { -result_abs } else { result_abs }
};
let r = $core::half_pi(w) - asin_w;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn atan2_approx(self, other: Self, working_digits: u32) -> Self {
self.atan2_approx_with(other, working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn atan2_approx_with(
self,
other: Self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.atan2_strict_with(other, mode);
}
let w = SCALE + working_digits;
let z = $crate::macros::wide_roots::wide_lit!($Storage, "0");
let yraw = self.to_bits();
let xraw = other.to_bits();
let r = if xraw == z {
if yraw > z {
$core::half_pi(w)
} else if yraw < z {
-$core::half_pi(w)
} else {
$core::zero()
}
} else {
let y = $core::to_work_w(yraw, working_digits);
let x = $core::to_work_w(xraw, working_digits);
let base = $core::atan_fixed($core::div(y, x, w), w);
if xraw > z {
base
} else if yraw >= z {
base + $core::pi(w)
} else {
base - $core::pi(w)
}
};
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sinh_approx(self, working_digits: u32) -> Self {
self.sinh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn sinh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.sinh_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = (ex - enx) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn cosh_approx(self, working_digits: u32) -> Self {
self.cosh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn cosh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.cosh_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = (ex + enx) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn tanh_approx(self, working_digits: u32) -> Self {
self.tanh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn tanh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.tanh_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let r = $core::div(ex - enx, ex + enx, w);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn sinh_cosh_approx(self, working_digits: u32) -> (Self, Self) {
self.sinh_cosh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn sinh_cosh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> (Self, Self) {
if working_digits == $core::GUARD {
return self.sinh_cosh_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
let ex = $core::exp_fixed(v, w);
let enx = $core::div($core::one(w), ex, w);
let sinh = (ex - enx) >> 1;
let cosh = (ex + enx) >> 1;
(
Self::from_bits($core::round_to_storage_with(sinh, w, SCALE, mode)),
Self::from_bits($core::round_to_storage_with(cosh, w, SCALE, mode)),
)
}
#[inline]
#[must_use]
pub fn asinh_approx(self, working_digits: u32) -> Self {
self.asinh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn asinh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.asinh_strict_with(mode);
}
let raw = self.to_bits();
if raw == $crate::macros::wide_roots::wide_lit!($Storage, "0") {
return Self::ZERO;
}
let w = SCALE + working_digits;
let one_w = $core::one(w);
let v = $core::to_work_w(raw, working_digits);
let ax = if v < $core::zero() { -v } else { v };
let inner = if ax >= one_w {
let inv = $core::div(one_w, ax, w);
let root = $core::sqrt_fixed(one_w + $core::mul(inv, inv, w), w);
$core::ln_fixed(ax, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(ax, ax, w) + one_w, w);
$core::ln_fixed(ax + root, w)
};
let signed = if raw < $crate::macros::wide_roots::wide_lit!($Storage, "0") {
-inner
} else {
inner
};
Self::from_bits($core::round_to_storage_with(signed, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn acosh_approx(self, working_digits: u32) -> Self {
self.acosh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn acosh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.acosh_strict_with(mode);
}
let w = SCALE + working_digits;
let one_w = $core::one(w);
let v = $core::to_work_w(self.to_bits(), working_digits);
if v < one_w {
panic!(concat!(stringify!($Type), "::acosh: argument must be >= 1"));
}
let two_w = one_w + one_w;
let inner = if v >= two_w {
let inv = $core::div(one_w, v, w);
let root = $core::sqrt_fixed(one_w - $core::mul(inv, inv, w), w);
$core::ln_fixed(v, w) + $core::ln_fixed(one_w + root, w)
} else {
let root = $core::sqrt_fixed($core::mul(v, v, w) - one_w, w);
$core::ln_fixed(v + root, w)
};
Self::from_bits($core::round_to_storage_with(inner, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn atanh_approx(self, working_digits: u32) -> Self {
self.atanh_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn atanh_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.atanh_strict_with(mode);
}
let w = SCALE + working_digits;
let one_w = $core::one(w);
let v = $core::to_work_w(self.to_bits(), working_digits);
let ax = if v < $core::zero() { -v } else { v };
if ax >= one_w {
panic!(concat!(stringify!($Type), "::atanh: argument out of domain (-1, 1)"));
}
let ratio = $core::div(one_w + v, one_w - v, w);
let r = $core::ln_fixed(ratio, w) >> 1;
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn to_degrees_approx(self, working_digits: u32) -> Self {
self.to_degrees_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn to_degrees_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.to_degrees_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
debug_assert!(
$core::bit_length(v) + 8 < <$Work>::BITS,
concat!(stringify!($Type),
"::to_degrees: |self| * 180 overflows the working integer")
);
let r = $core::div(
v * $crate::macros::wide_roots::wide_lit!($Work, "180"),
$core::pi(w),
w,
);
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
#[inline]
#[must_use]
pub fn to_radians_approx(self, working_digits: u32) -> Self {
self.to_radians_approx_with(working_digits, $crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn to_radians_approx_with(
self,
working_digits: u32,
mode: $crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == $core::GUARD {
return self.to_radians_strict_with(mode);
}
let w = SCALE + working_digits;
let v = $core::to_work_w(self.to_bits(), working_digits);
let r = $core::mul(v, $core::pi(w), w)
/ $crate::macros::wide_roots::wide_lit!($Work, "180");
Self::from_bits($core::round_to_storage_with(r, w, SCALE, mode))
}
}
#[cfg(all(feature = "strict", not(feature = "fast")))]
impl<const SCALE: u32> $Type<SCALE> {
#[inline]
#[must_use]
pub fn ln(self) -> Self {
self.ln_strict()
}
#[inline]
#[must_use]
pub fn log(self, base: Self) -> Self {
self.log_strict(base)
}
#[inline]
#[must_use]
pub fn log2(self) -> Self {
self.log2_strict()
}
#[inline]
#[must_use]
pub fn log10(self) -> Self {
self.log10_strict()
}
#[inline]
#[must_use]
pub fn exp(self) -> Self {
self.exp_strict()
}
#[inline]
#[must_use]
pub fn exp2(self) -> Self {
self.exp2_strict()
}
#[inline]
#[must_use]
pub fn powf(self, exp: Self) -> Self {
self.powf_strict(exp)
}
#[inline]
#[must_use]
pub fn sin(self) -> Self {
self.sin_strict()
}
#[inline]
#[must_use]
pub fn cos(self) -> Self {
self.cos_strict()
}
#[inline]
#[must_use]
pub fn tan(self) -> Self {
self.tan_strict()
}
#[inline]
#[must_use]
pub fn asin(self) -> Self {
self.asin_strict()
}
#[inline]
#[must_use]
pub fn acos(self) -> Self {
self.acos_strict()
}
#[inline]
#[must_use]
pub fn atan(self) -> Self {
self.atan_strict()
}
#[inline]
#[must_use]
pub fn atan2(self, other: Self) -> Self {
self.atan2_strict(other)
}
#[inline]
#[must_use]
pub fn sinh(self) -> Self {
self.sinh_strict()
}
#[inline]
#[must_use]
pub fn cosh(self) -> Self {
self.cosh_strict()
}
#[inline]
#[must_use]
pub fn tanh(self) -> Self {
self.tanh_strict()
}
#[inline]
#[must_use]
pub fn asinh(self) -> Self {
self.asinh_strict()
}
#[inline]
#[must_use]
pub fn acosh(self) -> Self {
self.acosh_strict()
}
#[inline]
#[must_use]
pub fn atanh(self) -> Self {
self.atanh_strict()
}
#[inline]
#[must_use]
pub fn to_degrees(self) -> Self {
self.to_degrees_strict()
}
#[inline]
#[must_use]
pub fn to_radians(self) -> Self {
self.to_radians_strict()
}
}
};
}
pub(crate) use decl_wide_transcendental;
#[cfg(all(test, not(feature = "fast")))]
mod tests {
use crate::{D38, D76, D153, D307};
#[test]
fn wide_transcendentals_match_d38() {
let positives = [1i64, 250_000, 500_000, 1_000_000, 2_718_282, 7_500_000];
let unit_range = [-900_000i64, -250_000, 1, 250_000, 900_000];
let all = [-3_000_000i64, -500_000, 1, 500_000, 1_500_000, 4_000_000];
fn agree(label: &str, ctx: i64, wide: i128, d38: i128) {
assert!(
(wide - d38).abs() <= 2,
"{label} mismatch at {ctx}: wide {wide} vs d38 {d38}"
);
}
for raw in positives {
let n = D38::<6>::from_bits(raw as i128);
let w = D76::<6>::from_bits(crate::wide_int::wide_cast::<i128, crate::wide_int::I256>(raw as i128));
agree("ln", raw, w.ln_strict().to_bits().resize::<i128>(), n.ln_strict().to_bits());
agree("log2", raw, w.log2_strict().to_bits().resize::<i128>(), n.log2_strict().to_bits());
agree("log10", raw, w.log10_strict().to_bits().resize::<i128>(), n.log10_strict().to_bits());
}
for raw in all {
let n = D38::<6>::from_bits(raw as i128);
let w = D76::<6>::from_bits(crate::wide_int::wide_cast::<i128, crate::wide_int::I256>(raw as i128));
agree("exp", raw, w.exp_strict().to_bits().resize::<i128>(), n.exp_strict().to_bits());
agree("sin", raw, w.sin_strict().to_bits().resize::<i128>(), n.sin_strict().to_bits());
agree("cos", raw, w.cos_strict().to_bits().resize::<i128>(), n.cos_strict().to_bits());
agree("atan", raw, w.atan_strict().to_bits().resize::<i128>(), n.atan_strict().to_bits());
agree("sinh", raw, w.sinh_strict().to_bits().resize::<i128>(), n.sinh_strict().to_bits());
agree("cosh", raw, w.cosh_strict().to_bits().resize::<i128>(), n.cosh_strict().to_bits());
agree("tanh", raw, w.tanh_strict().to_bits().resize::<i128>(), n.tanh_strict().to_bits());
}
for raw in unit_range {
let n = D38::<6>::from_bits(raw as i128);
let w = D76::<6>::from_bits(crate::wide_int::wide_cast::<i128, crate::wide_int::I256>(raw as i128));
agree("asin", raw, w.asin_strict().to_bits().resize::<i128>(), n.asin_strict().to_bits());
agree("acos", raw, w.acos_strict().to_bits().resize::<i128>(), n.acos_strict().to_bits());
agree("atanh", raw, w.atanh_strict().to_bits().resize::<i128>(), n.atanh_strict().to_bits());
}
}
#[test]
fn wide_transcendental_identities() {
assert_eq!(D76::<6>::ONE.ln_strict(), D76::<6>::ZERO);
assert_eq!(D76::<6>::ZERO.exp_strict(), D76::<6>::ONE);
assert_eq!(D76::<6>::ZERO.sin_strict(), D76::<6>::ZERO);
assert_eq!(D76::<6>::ZERO.sinh_strict(), D76::<6>::ZERO);
assert_eq!(D76::<6>::ZERO.atan_strict(), D76::<6>::ZERO);
assert_eq!(D153::<6>::ONE.ln_strict(), D153::<6>::ZERO);
assert_eq!(D153::<6>::ZERO.exp_strict(), D153::<6>::ONE);
assert_eq!(D153::<6>::ZERO.cos_strict(), D153::<6>::ONE);
assert_eq!(D307::<6>::ONE.ln_strict(), D307::<6>::ZERO);
assert_eq!(D307::<6>::ZERO.exp_strict(), D307::<6>::ONE);
assert_eq!(D307::<6>::ZERO.cosh_strict(), D307::<6>::ONE);
}
#[test]
fn wide_agm_matches_taylor_at_storage_scale() {
let positives = [1i64, 250_000, 500_000, 1_000_000, 2_718_282, 7_500_000];
let all = [-3_000_000i64, -500_000, 1, 500_000, 1_500_000, 4_000_000];
fn agree(label: &str, ctx: i64, agm: i128, taylor: i128) {
assert!(
(agm - taylor).abs() <= 2,
"{label} AGM-vs-Taylor mismatch at {ctx}: agm {agm} vs taylor {taylor}"
);
}
for raw in positives {
let w = D76::<6>::from_bits(
crate::wide_int::wide_cast::<i128, crate::wide_int::I256>(raw as i128),
);
agree(
"ln",
raw,
w.ln_strict_agm().to_bits().resize::<i128>(),
w.ln_strict().to_bits().resize::<i128>(),
);
}
for raw in all {
let w = D76::<6>::from_bits(
crate::wide_int::wide_cast::<i128, crate::wide_int::I256>(raw as i128),
);
agree(
"exp",
raw,
w.exp_strict_agm().to_bits().resize::<i128>(),
w.exp_strict().to_bits().resize::<i128>(),
);
}
}
#[test]
fn wide_agm_identity_points() {
assert_eq!(D76::<6>::ONE.ln_strict_agm(), D76::<6>::ZERO);
assert_eq!(D76::<6>::ZERO.exp_strict_agm(), D76::<6>::ONE);
assert_eq!(D153::<6>::ONE.ln_strict_agm(), D153::<6>::ZERO);
assert_eq!(D153::<6>::ZERO.exp_strict_agm(), D153::<6>::ONE);
assert_eq!(D307::<6>::ONE.ln_strict_agm(), D307::<6>::ZERO);
assert_eq!(D307::<6>::ZERO.exp_strict_agm(), D307::<6>::ONE);
}
#[test]
fn wide_strict_with_honours_mode() {
use crate::support::rounding::RoundingMode;
let n = D76::<6>::ONE;
let hte = n.exp_strict_with(RoundingMode::HalfToEven);
let trunc = n.exp_strict_with(RoundingMode::Trunc);
assert!(
hte.to_bits().resize::<i128>() - trunc.to_bits().resize::<i128>() == 1
|| hte.to_bits().resize::<i128>() - trunc.to_bits().resize::<i128>() == 0,
"exp(1) HTE vs Trunc: hte={}, trunc={}",
hte,
trunc,
);
if !(cfg!(feature = "rounding-half-away-from-zero")
|| cfg!(feature = "rounding-half-toward-zero")
|| cfg!(feature = "rounding-trunc")
|| cfg!(feature = "rounding-floor")
|| cfg!(feature = "rounding-ceiling"))
{
assert_eq!(hte, n.exp_strict());
}
}
#[test]
fn wide_agm_moderate_scale_round_trip() {
let x = D76::<20>::from_int(3);
let back = x.ln_strict_agm().exp_strict_agm();
let delta = (back.to_bits().resize::<i128>() - x.to_bits().resize::<i128>()).abs();
assert!(delta <= 8, "AGM exp(ln(3)) at D76<20> delta {delta}");
let y = D153::<20>::from_int(2);
let back = y.exp_strict_agm().ln_strict_agm();
let delta = (back.to_bits().resize::<i128>() - y.to_bits().resize::<i128>()).abs();
assert!(delta <= 8, "AGM ln(exp(2)) at D153<20> delta {delta}");
}
#[test]
fn wide_only_scale_round_trips() {
let x = D76::<50>::from_int(3);
let back = x.ln_strict().exp_strict();
let delta = (back.to_bits().resize::<i128>() - x.to_bits().resize::<i128>()).abs();
assert!(delta <= 8, "exp(ln(3)) at D76<50> delta {delta}");
let y = D307::<150>::from_int(2);
let back = y.exp_strict().ln_strict();
let delta = (back.to_bits().resize::<i128>() - y.to_bits().resize::<i128>()).abs();
assert!(delta <= 8, "ln(exp(2)) at D307<150> delta {delta}");
}
}