use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::WIDTH_MINUS_1;
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, floor_and_ceiling};
use alloc::vec;
use core::cmp::Ordering::{self, Equal, Greater, Less};
use core::cmp::max;
use core::mem::swap;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
CeilingLogBase2, Exp, ExpAssign, FloorRoot, FloorSqrt, IsPowerOf2, NegAssign, Parity, PowerOf2,
ShrRoundAssign, Sign, Square, SquareAssign, WrappingAddAssign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{
Infinity as InfinityTrait, NaN as NaNTrait, One, Zero as ZeroTrait,
};
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom, WrappingFrom};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{
self, Ceiling, Down, Exact, Floor, Nearest, Up,
};
use malachite_nz::integer::Integer;
use malachite_nz::natural::Natural;
use malachite_nz::natural::arithmetic::float_extras::float_can_round;
use malachite_nz::platform::{Limb, SignedLimb};
use malachite_q::Rational;
fn mpz_normalize(z: Integer, q: i64) -> (Integer, i64) {
let k = z.significant_bits();
if q < 0 || k > u64::exact_from(q) {
let shift = i64::exact_from(k) - q;
(z >> shift, shift)
} else {
(z, 0)
}
}
fn mpz_normalize2(z: Integer, expz: i64, target: i64) -> (Integer, i64) {
(z >> (target - expz), target)
}
fn get_z_2exp(x: Float) -> (Integer, i64) {
if let Finite {
sign,
exponent,
significand,
..
} = x.0
{
let bits = significand.significant_bits();
let m = Integer::from_sign_and_abs(sign, significand);
(m, i64::from(exponent) - i64::exact_from(bits))
} else {
unreachable!()
}
}
fn exp2_aux(r: Float, q: u64) -> (Integer, i64, u64) {
let qi = i64::exact_from(q);
let mut expt: i64 = 0;
let exps: i64 = 1 - qi; let mut t = Integer::ONE;
let mut s = Integer::power_of_2(q - 1);
let (mut rr, mut expr) = get_z_2exp(r); let mut l: u64 = 0;
loop {
l += 1;
t *= &rr;
expt += expr;
let sbit = i64::exact_from(s.significant_bits());
let tbit = i64::exact_from(t.significant_bits());
let dif = exps + sbit - expt - tbit;
let (t2, sh) = mpz_normalize(t, qi - dif);
t = t2;
expt += sh;
if l > 1 {
if l.is_power_of_2() {
t >>= l.ceiling_log_base_2();
} else {
t /= Integer::from(l);
}
debug_assert_eq!(expt, exps);
}
if t == 0 {
break;
}
s += &t; let tbit = i64::exact_from(t.significant_bits());
let (rr2, sh) = mpz_normalize(rr, tbit);
rr = rr2;
expr += sh;
}
(s, exps, 3 * l * (l + 1))
}
const EXP_2_THRESHOLD: u64 = 100;
fn exp2_aux2(r: Float, q: u64) -> (Integer, i64, u64) {
let qi = i64::exact_from(q);
let one_minus_q = 1 - qi;
let expr0 = i64::from(r.get_exponent().unwrap());
debug_assert!(expr0 < 0);
let l_est = q / u64::exact_from(-expr0);
let m = max(2, usize::exact_from(l_est.floor_sqrt()));
let mut r_pows = vec![Integer::ZERO; m + 1];
let mut exp_r_pows = vec![0i64; m + 1];
let exps = one_minus_q; let mut s = Integer::ZERO;
let (r1, e1) = get_z_2exp(r); let r1 = mpz_normalize2(r1, e1, one_minus_q).0;
r_pows[1] = r1;
exp_r_pows[1] = one_minus_q;
let qm1 = q - 1;
r_pows[2] = (&r_pows[1]).square() >> qm1;
exp_r_pows[2] = one_minus_q;
for i in 3..=m {
let t = if i.odd() {
&r_pows[i - 1] * &r_pows[1]
} else {
(&r_pows[i >> 1]).square()
};
r_pows[i] = t >> qm1;
exp_r_pows[i] = one_minus_q;
}
r_pows[0] = Integer::power_of_2(q - 1); exp_r_pows[0] = one_minus_q;
let mut rr = Integer::ONE;
let mut expr: i64 = 0; let mut l: u64 = 0;
let mut ql = q; loop {
let one_minus_ql = 1 - i64::exact_from(ql);
if l != 0 {
for (r_pow, exp_r_pow) in r_pows[..m].iter_mut().zip(exp_r_pows[..m].iter_mut()) {
let z = core::mem::replace(r_pow, Integer::ZERO);
(*r_pow, *exp_r_pow) = mpz_normalize2(z, *exp_r_pow, one_minus_ql);
}
}
let (mut t, mut expt) =
mpz_normalize2(r_pows[m - 1].clone(), exp_r_pows[m - 1], one_minus_ql);
for i in (0..m - 1).rev() {
t /= Integer::from(l + i as u64 + 1); t += &r_pows[i];
}
t *= &rr;
expt += expr;
let (t, et) = mpz_normalize2(t, expt, exps);
debug_assert_eq!(et, exps);
s += &t; let mut t = &rr * &r_pows[m]; expr += exp_r_pows[m];
let mut tmp = Integer::ONE;
for i in 1..=m {
tmp *= Integer::from(l + i as u64);
}
t /= tmp; l += m as u64;
if t == 0 {
break;
}
let (rr2, sh) = mpz_normalize(t, i64::exact_from(ql));
rr = rr2;
expr += sh;
let rrbit = if rr == 0 {
1
} else {
i64::exact_from(rr.significant_bits())
};
let sbit = i64::exact_from(s.significant_bits());
ql = (qi - exps - sbit + expr + rrbit) as u64;
if (expr as u64).wrapping_add(rrbit as u64) <= q.wrapping_neg() {
break;
}
}
(s, exps, l * (l + 4))
}
const EXP_THRESHOLD: u64 = 25000;
fn extract(p: &Float, i: u64) -> Integer {
if let Finite {
sign, significand, ..
} = &p.0
{
let limbs = significand.as_limbs_asc();
let size_p = limbs.len();
let two_i = usize::power_of_2(i);
let two_i_2 = if i == 0 { 1 } else { two_i >> 1 };
let mut y = vec![0 as Limb; two_i_2];
if size_p < two_i {
if size_p >= two_i_2 {
let count = size_p - two_i_2;
y[two_i - size_p..][..count].copy_from_slice(&limbs[..count]);
} else {
fail_on_untested_path("extract, window entirely below the mantissa");
}
} else {
y.copy_from_slice(&limbs[size_p - two_i..][..two_i_2]);
}
Integer::from_sign_and_abs(*sign, Natural::from_owned_limbs_asc(y))
} else {
unreachable!()
}
}
fn exp_rational(p: Integer, mut r: i64, m: usize, prec: u64) -> Float {
let nz = p.trailing_zeros().unwrap();
let p = p >> nz;
r -= i64::exact_from(nz);
let scratch_len = m + 1;
let mut scratch = vec![Integer::ZERO; 3 * scratch_len];
split_into_chunks_mut!(scratch, scratch_len, [q, s], ptoj); let mut scratch = vec![0u64; scratch_len << 1];
let (mult, log2_nb_terms) = scratch.split_at_mut(scratch_len);
ptoj[0] = p;
for k in 1..m {
ptoj[k] = (&ptoj[k - 1]).square();
}
q[0] = Integer::ONE;
s[0] = Integer::ONE;
let mut k = 0usize;
let mut prec_i_have: u64 = 0;
let n_terms = u64::power_of_2(u64::exact_from(m));
let mut i = 1u64;
while prec_i_have < prec && i < n_terms {
k += 1;
log2_nb_terms[k] = 0; q[k] = Integer::from(i + 1);
s[k] = Integer::from(i + 1);
let mut j = i + 1; let mut l = 0u32;
while j.even() {
s[k] *= &ptoj[l as usize];
let mut t = &s[k - 1] * &q[k];
t <<= r << l;
t += &s[k];
s[k - 1] = t;
let (q_lo, q_hi) = q.split_at_mut(k);
*q_lo.last_mut().unwrap() *= &q_hi[0];
log2_nb_terms[k - 1] += 1;
prec_i_have = q[k].significant_bits();
let prec_ptoj = ptoj[l as usize].significant_bits();
mult[k - 1].wrapping_add_assign(
prec_i_have
.wrapping_add(u64::wrapping_from(r << l))
.wrapping_sub(prec_ptoj)
.wrapping_sub(1),
);
prec_i_have = mult[k - 1];
mult[k] = mult[k - 1];
l += 1;
j >>= 1;
k -= 1;
}
i += 1;
}
let mut h = 0u64; while k > 0 {
let jj = log2_nb_terms[k - 1] as usize;
s[k] *= &ptoj[jj];
let mut t = &s[k - 1] * &q[k];
h += u64::power_of_2(log2_nb_terms[k]);
t <<= r * i64::exact_from(h);
t += &s[k];
s[k - 1] = t;
let (q_lo, q_hi) = q.split_at_mut(k);
*q_lo.last_mut().unwrap() *= &q_hi[0];
k -= 1;
}
let mut s0 = core::mem::replace(&mut s[0], Integer::ZERO);
let mut q0 = core::mem::replace(&mut q[0], Integer::ZERO);
let mut diff = i64::exact_from(s0.significant_bits()) - (i64::exact_from(prec) << 1);
let mut expo = diff;
s0 >>= diff; diff = i64::exact_from(q0.significant_bits()) - i64::exact_from(prec);
expo -= diff;
q0 >>= diff;
s0 /= q0; Float::from_rational_prec_round(
Rational::from(s0) << (expo - r * (i64::exact_from(i) - 1)),
prec,
Floor,
)
.0
}
pub(crate) fn exp_3(x: &Float, precy: u64, rm: RoundingMode) -> (Float, Ordering) {
const SHIFT: u64 = Limb::WIDTH >> 1;
let prec_x = x
.get_prec()
.unwrap()
.ceiling_log_base_2()
.saturating_sub(Limb::LOG_WIDTH);
let mut ttt = i64::from(x.get_exponent().unwrap());
let mut x_copy = x.clone();
let shift_x = if ttt > 0 {
let s = u64::exact_from(ttt);
x_copy = x >> s;
ttt = i64::from(x_copy.get_exponent().unwrap());
s
} else {
0
};
debug_assert!(ttt <= 0);
let mut realprec = precy + (prec_x + precy).ceiling_log_base_2();
let mut prec = realprec + SHIFT + 2 + shift_x;
let mut increment = Limb::WIDTH;
loop {
let k = prec.ceiling_log_base_2().saturating_sub(Limb::LOG_WIDTH);
let mut twopoweri = Limb::WIDTH;
let uk = extract(&x_copy, 0);
debug_assert_ne!(uk, 0);
let mut tmp = exp_rational(
uk,
i64::exact_from(SHIFT + twopoweri) - ttt,
usize::exact_from(k + 1),
prec,
);
for _ in 0..SHIFT {
tmp.square_prec_round_assign(prec, Floor);
}
twopoweri <<= 1;
let iter = k.min(prec_x);
for i in 1..=iter {
let uk = extract(&x_copy, i);
if uk != 0 {
let t = exp_rational(
uk,
i64::exact_from(twopoweri) - ttt,
usize::exact_from(k - i + 1),
prec,
);
tmp.mul_prec_round_assign(t, prec, Floor);
}
twopoweri <<= 1;
}
let (val, scaled) = if shift_x > 0 {
for _ in 0..shift_x - 1 {
tmp.square_prec_round_assign(prec, Floor);
}
let mut t = tmp.square_prec_round_ref(prec, Floor).0;
if t.is_infinite() {
fail_on_untested_path("exp_3, overflow above normal_ref's bound_emax");
return exp_overflow(precy, rm);
}
let mut scaled = false;
if matches!(t.0, Zero { .. }) {
tmp <<= 1;
t = tmp.square_prec_round_ref(prec, Floor).0;
if matches!(t.0, Zero { .. }) {
return exp_underflow(precy, if rm == Nearest { Down } else { rm });
}
scaled = true;
}
(t, scaled)
} else {
(tmp, false)
};
if float_can_round(val.significand_ref().unwrap(), realprec, precy, rm) {
let mut y = val;
let mut inexact = y.set_prec_round(precy, rm);
if scaled && y.is_normal() {
let ey = i64::from(y.get_exponent().unwrap());
let inex2 = y.shr_round_assign(2, rm);
if inex2 != Equal {
if rm == Nearest
&& inexact == Less
&& matches!(y.0, Zero { .. })
&& ey == i64::from(Float::MIN_EXPONENT) + 1
{
(y, inexact) = (Float::min_positive_value_prec(precy), Greater);
} else {
inexact = inex2;
}
}
}
return (y, inexact);
}
realprec += increment;
increment = realprec >> 1;
prec = realprec + SHIFT + 2 + shift_x;
}
}
pub(crate) fn exp_2(x: &Float, precy: u64, rm: RoundingMode) -> (Float, Ordering) {
let expx = i64::from(x.get_exponent().unwrap());
let mut n: i64 = if expx <= -2 {
0
} else {
let log2_est = Float::ln_2_prec_round(WIDTH_MINUS_1, Down).0;
let r_est = x.div_prec_ref_val(log2_est, WIDTH_MINUS_1).0;
i64::rounding_from(r_est, Nearest).0
};
let error_r: u64 = if n == 0 {
0
} else {
(n.unsigned_abs() + 1).significant_bits()
};
let k_param = if precy < EXP_2_THRESHOLD {
precy.div_ceil(2).floor_sqrt() + 3
} else {
(precy << 2).floor_root(3)
};
let l = (precy - 1) / k_param + 1;
let mut err = k_param + ((l << 1) + 18).ceiling_log_base_2();
let mut q = precy + err + k_param + 10;
if expx > 0 {
q += u64::exact_from(expx);
}
let mut increment = Limb::WIDTH;
loop {
let working = q + error_r;
let s = Float::ln_2_prec_round(working, if n >= 0 { Down } else { Up }).0;
let mut r = s
.mul_prec_round_ref_val(
Float::from(n.unsigned_abs()),
working,
if n >= 0 { Down } else { Up },
)
.0;
if n < 0 {
r.neg_assign();
}
r = x.sub_prec_round_ref_val(r, working, Up).0;
while r.is_normal() && r.is_sign_negative() {
n -= 1;
r.add_prec_round_assign_ref(&s, working, Up);
}
if r.is_normal() {
if error_r > 0 {
r.set_prec_round(q, Up);
}
r >>= k_param;
let (mut ss, mut exps, l_err) = if precy < EXP_2_THRESHOLD {
exp2_aux(r, q)
} else {
exp2_aux2(r, q)
};
for _ in 0..k_param {
ss.square_assign();
exps <<= 1;
let (ss2, sh) = mpz_normalize(ss, i64::exact_from(q));
ss = ss2;
exps += sh;
}
let s = Float::from_integer_prec(ss, working).0 << exps;
err = k_param + l_err.ceiling_log_base_2() + 2;
if float_can_round(s.significand_ref().unwrap(), q - err, precy, rm) {
return s.shl_prec_round(n, precy, rm);
}
}
q += increment;
increment = q >> 1;
}
}
pub(crate) fn exp_overflow(precy: u64, rm: RoundingMode) -> (Float, Ordering) {
match rm {
Nearest | Up | Ceiling => (Float::INFINITY, Greater),
Down | Floor => (Float::max_finite_value_with_prec(precy), Less),
Exact => panic!("exp: Exact rounding was requested, but the result overflows"),
}
}
pub(crate) fn exp_underflow(precy: u64, rm: RoundingMode) -> (Float, Ordering) {
match rm {
Nearest | Down | Floor => (Float::ZERO, Less),
Up | Ceiling => (Float::min_positive_value_prec(precy), Greater),
Exact => panic!("exp: Exact rounding was requested, but the result underflows"),
}
}
fn exp_prec_round_normal_ref(x: &Float, precy: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact exp");
const BP: u64 = 64;
const MAX_EXPONENT_FLOAT: Float = Float::const_from_signed(Float::MAX_EXPONENT as SignedLimb);
let (log2_lo, log2_hi) = floor_and_ceiling(Float::ln_2_prec_round(BP, Floor));
let bound_emax = log2_hi.mul_prec_round_ref_val(MAX_EXPONENT_FLOAT, BP, Up).0;
if *x >= bound_emax {
return exp_overflow(precy, rm);
}
let bound_emax_lo = log2_lo
.mul_prec_round_ref_val(MAX_EXPONENT_FLOAT, BP, Floor)
.0;
if *x >= bound_emax_lo {
let xr = Rational::exact_from(x);
let emax_r = Rational::from(Float::MAX_EXPONENT);
let mut p = 128;
loop {
let lo = Rational::exact_from(Float::ln_2_prec_round(p, Floor).0) * &emax_r;
if xr < lo {
break;
}
let hi = Rational::exact_from(Float::ln_2_prec_round(p, Ceiling).0) * &emax_r;
if xr >= hi {
return exp_overflow(precy, rm);
}
p <<= 1;
}
}
let bound_emin = log2_hi
.mul_prec_round(
const { Float::const_from_signed((Float::MIN_EXPONENT as SignedLimb) - 2) },
BP,
Floor,
)
.0;
if *x <= bound_emin {
return exp_underflow(precy, rm);
}
let expx = i64::from(x.get_exponent().unwrap());
if expx < 0 && u64::exact_from(-expx) > precy {
return if x.is_sign_negative() && (rm == Down || rm == Floor) {
(one_neighbor(precy, false), Less) } else if x.is_sign_positive() && (rm == Up || rm == Ceiling) {
(one_neighbor(precy, true), Greater) } else {
(
Float::one_prec(precy),
if x.is_sign_positive() { Less } else { Greater },
)
};
}
if precy >= EXP_THRESHOLD {
exp_3(x, precy, rm)
} else {
exp_2(x, precy, rm)
}
}
pub(crate) fn one_neighbor(prec: u64, above: bool) -> Float {
let p = i64::exact_from(prec);
Float::from_rational_prec_round(
if above {
Rational::ONE + Rational::power_of_2(1 - p)
} else {
Rational::ONE - Rational::power_of_2(-p)
},
prec,
Exact,
)
.0
}
pub(crate) fn exp_rational_near_one(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let negative = x.sign() == Less;
let mut s = Rational::ONE; let mut term = Rational::ONE; let mut k = 1u64;
loop {
term *= x;
term /= Rational::from(k); let s_next = &s + &term; let (lo, hi) = if negative {
if s < s_next {
(s.clone(), s_next.clone())
} else {
(s_next.clone(), s.clone())
}
} else {
let next = (&term * x) / Rational::from(k + 1); (s_next.clone(), &s_next + next / (Rational::ONE - x))
};
s = s_next;
k += 1;
let (f_lo, mut o_lo) = Float::from_rational_prec_round_ref(&lo, prec, rm);
let (f_hi, mut o_hi) = Float::from_rational_prec_round_ref(&hi, prec, rm);
if o_lo == Equal {
o_lo = o_hi;
}
if o_hi == Equal {
o_hi = o_lo;
}
if o_lo == o_hi && f_lo == f_hi {
return (f_lo, o_lo);
}
}
}
fn exp_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact exp");
let positive = x.sign() == Greater;
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x <= Float::MIN_EXPONENT_I64 {
return exp_rational_near_one(x, prec, rm);
}
if -exp_x > i64::exact_from(prec) {
return match (positive, rm) {
(false, Down | Floor) => (one_neighbor(prec, false), Less), (true, Up | Ceiling) => (one_neighbor(prec, true), Greater), (true, _) => (Float::one_prec(prec), Less),
(false, _) => (Float::one_prec(prec), Greater),
};
}
if exp_x >= Float::MAX_EXPONENT_I64 {
return if positive {
exp_overflow(prec, rm)
} else {
exp_underflow(prec, rm)
};
}
let mut working_prec = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let (x_lo, x_o) = Float::from_rational_prec_round_ref(x, working_prec, Floor);
if x_o == Equal {
return exp_prec_round_normal_ref(&x_lo, prec, rm);
}
let (x_lo, x_hi) = floor_and_ceiling((x_lo, x_o));
let (e_lo, o_lo) = exp_prec_round_normal_ref(&x_lo, prec, rm);
let (e_hi, o_hi) = exp_prec_round_normal_ref(&x_hi, prec, rm);
if o_lo == o_hi && e_lo == e_hi {
return (e_lo, o_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
impl Float {
#[inline]
pub fn exp_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.exp_prec_round_ref(prec, rm)
}
pub fn exp_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Equal),
Infinity { sign } => {
if *sign {
(Self::INFINITY, Equal)
} else {
(Self::ZERO, Equal)
}
}
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => exp_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn exp_prec(self, prec: u64) -> (Self, Ordering) {
self.exp_prec_round(prec, Nearest)
}
#[inline]
pub fn exp_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.exp_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn exp_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.exp_prec_round(prec, rm)
}
#[inline]
pub fn exp_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.exp_prec_round_ref(prec, rm)
}
#[inline]
pub fn exp_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let mut x = Self::ZERO;
swap(self, &mut x);
let o;
(*self, o) = x.exp_prec_round(prec, rm);
o
}
#[inline]
pub fn exp_prec_assign(&mut self, prec: u64) -> Ordering {
self.exp_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn exp_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.exp_prec_round_assign(prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn exp_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::exp_rational_prec_round_ref(&x, prec, rm)
}
pub fn exp_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::one_prec(prec), Equal);
}
exp_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn exp_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::exp_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn exp_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::exp_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Exp for Float {
type Output = Self;
#[inline]
fn exp(self) -> Self {
let prec = self.significant_bits();
self.exp_prec_round(prec, Nearest).0
}
}
impl Exp for &Float {
type Output = Float;
#[inline]
fn exp(self) -> Float {
let prec = self.significant_bits();
self.exp_prec_round_ref(prec, Nearest).0
}
}
impl ExpAssign for Float {
#[inline]
fn exp_assign(&mut self) {
let prec = self.significant_bits();
self.exp_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_exp<T: PrimitiveFloat>(x: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_fn(Float::exp_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_exp_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(Float::exp_rational_prec_ref, x)
}