impl<const SCALE: u32> crate::D<crate::int::types::Int<2>, SCALE> {
#[inline]
#[must_use]
pub fn pow(self, exp: u32) -> Self {
let mut acc = Self::ONE;
let mut base = self;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
acc *= base;
}
e >>= 1;
if e > 0 {
base *= base;
}
}
acc
}
#[inline]
#[must_use]
pub fn powi(self, exp: i32) -> Self {
if exp >= 0 {
self.pow(exp as u32)
} else {
Self::ONE / self.pow(exp.unsigned_abs())
}
}
#[inline]
#[must_use]
pub fn powf_strict(self, exp: crate::D<crate::int::types::Int<2>, SCALE>) -> Self {
self.powf_strict_with(exp, crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn powf_strict_with(
self,
exp: crate::D<crate::int::types::Int<2>, SCALE>,
mode: crate::support::rounding::RoundingMode,
) -> Self {
Self::from_bits(crate::policy::pow::dispatch::<_, SCALE>(self.to_bits(), exp.to_bits(), mode))
}
#[inline]
#[must_use]
pub fn powf_approx(self, exp: crate::D<crate::int::types::Int<2>, SCALE>, 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: crate::D<crate::int::types::Int<2>, SCALE>,
working_digits: u32,
mode: crate::support::rounding::RoundingMode,
) -> Self {
if working_digits == crate::types::log_exp::STRICT_GUARD {
return self.powf_strict_with(exp, mode);
}
Self::from_bits(crate::policy::pow::dispatch_with::<_, SCALE>(self.to_bits(), exp.to_bits(), working_digits, mode))
}
#[cfg(all(feature = "strict", not(feature = "fast")))]
#[inline]
#[must_use]
pub fn powf(self, exp: crate::D<crate::int::types::Int<2>, SCALE>) -> Self {
self.powf_strict(exp)
}
#[inline]
#[must_use]
pub fn sqrt_strict(self) -> Self {
self.sqrt_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn sqrt_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
Self(crate::policy::sqrt::dispatch::<_, SCALE>(self.0, mode))
}
#[cfg(all(feature = "strict", not(feature = "fast")))]
#[inline]
#[must_use]
pub fn sqrt(self) -> Self {
self.sqrt_strict()
}
#[cfg(all(feature = "strict", not(feature = "fast")))]
#[inline]
#[must_use]
pub fn cbrt(self) -> Self {
self.cbrt_strict()
}
#[inline]
#[must_use]
pub fn cbrt_strict(self) -> Self {
self.cbrt_strict_with(crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn cbrt_strict_with(self, mode: crate::support::rounding::RoundingMode) -> Self {
Self(crate::policy::cbrt::dispatch::<_, SCALE>(self.0, mode))
}
#[inline]
#[must_use]
pub fn hypot_strict(self, other: Self) -> Self {
self.hypot_strict_with(other, crate::support::rounding::DEFAULT_ROUNDING_MODE)
}
#[inline]
#[must_use]
pub fn hypot_strict_with(
self,
other: Self,
mode: crate::support::rounding::RoundingMode,
) -> Self {
Self(crate::policy::hypot::dispatch::<_, SCALE>(self.0, other.0, mode))
}
#[cfg(all(feature = "strict", not(feature = "fast")))]
#[inline]
#[must_use]
pub fn hypot(self, other: Self) -> Self {
self.hypot_strict(other)
}
#[inline]
#[must_use]
pub fn checked_pow(self, exp: u32) -> Option<Self> {
let mut acc = Self::ONE;
let mut base = self;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
acc = acc.checked_mul(base)?;
}
e >>= 1;
if e > 0 {
base = base.checked_mul(base)?;
}
}
Some(acc)
}
#[inline]
#[must_use]
pub fn wrapping_pow(self, exp: u32) -> Self {
let mut acc = Self::ONE;
let mut base = self;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
acc = acc.wrapping_mul(base);
}
e >>= 1;
if e > 0 {
base = base.wrapping_mul(base);
}
}
acc
}
#[inline]
#[must_use]
pub fn saturating_pow(self, exp: u32) -> Self {
if exp == 0 {
return Self::ONE;
}
let mut acc = Self::ONE;
let mut base = self;
let mut e = exp;
let result_negative_if_overflow = self.is_negative() && (exp & 1) == 1;
while e > 0 {
if e & 1 == 1 {
match acc.checked_mul(base) {
Some(q) => acc = q,
None => {
return if result_negative_if_overflow {
Self::MIN
} else {
Self::MAX
};
}
}
}
e >>= 1;
if e > 0 {
match base.checked_mul(base) {
Some(q) => base = q,
None => {
return if result_negative_if_overflow {
Self::MIN
} else {
Self::MAX
};
}
}
}
}
acc
}
#[inline]
#[must_use]
pub fn overflowing_pow(self, exp: u32) -> (Self, bool) {
let mut acc = Self::ONE;
let mut base = self;
let mut e = exp;
let mut overflowed = false;
while e > 0 {
if e & 1 == 1 {
let (q, o) = acc.overflowing_mul(base);
acc = q;
overflowed |= o;
}
e >>= 1;
if e > 0 {
let (q, o) = base.overflowing_mul(base);
base = q;
overflowed |= o;
}
}
(acc, overflowed)
}
}
#[cfg(test)]
mod tests {
#[test]
fn strict_sqrt_is_correctly_rounded() {
fn check<const S: u32>(raw: i128) {
let x = crate::D::<crate::int::types::Int<2>, S>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
let q = x.sqrt_strict().to_bits().as_i128();
assert!(q >= 0, "sqrt result must be non-negative");
let mult = 10u128.pow(S);
let (n_hi, n_lo) = crate::algos::support::mg_divide::mul_u128_to_u256(raw as u128, mult);
let (qsq_hi, qsq_lo) = crate::algos::support::mg_divide::mul_u128_to_u256(q as u128, q as u128);
let q_u = q as u128;
let (uphi, uplo) = {
let (lo, c) = qsq_lo.overflowing_add(q_u);
(qsq_hi + c as u128, lo)
};
let n_le_upper = n_hi < uphi || (n_hi == uphi && n_lo <= uplo);
assert!(n_le_upper, "sqrt({raw} @ s{S}) = {q}: N exceeds (q+0.5)^2");
if q > 0 {
let (nphi, nplo) = {
let (lo, c) = n_lo.overflowing_add(q_u);
(n_hi + c as u128, lo)
};
let above_lower = nphi > qsq_hi || (nphi == qsq_hi && nplo > qsq_lo);
assert!(above_lower, "sqrt({raw} @ s{S}) = {q}: N below (q-0.5)^2");
}
}
for &raw in &[
1_i128,
2,
3,
4,
5,
999_999_999_999,
1_000_000_000_000,
1_500_000_000_000,
123_456_789_012_345,
i128::MAX,
i128::MAX / 7,
] {
check::<0>(raw);
check::<6>(raw);
check::<12>(raw);
check::<19>(raw);
}
for &raw in &[1_i128, 2, 17, i128::MAX, i128::MAX / 3] {
check::<38>(raw);
}
}
#[test]
fn strict_cbrt_is_correctly_rounded() {
use i256::U256;
fn check<const S: u32>(raw: i128) {
let x = crate::D::<crate::int::types::Int<2>, S>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
let q = x.cbrt_strict().to_bits().as_i128();
assert_eq!(q.signum(), raw.signum(), "cbrt sign mismatch");
let qa = q.unsigned_abs();
let ra = raw.unsigned_abs();
let m = U256::from(10u8).pow(2 * S);
let n = U256::from(ra) * m;
let eight_n = n << 3;
let two_q = U256::from(qa) * U256::from(2u8);
let upper = {
let t = two_q + U256::from(1u8);
t * t * t
};
assert!(
eight_n <= upper,
"cbrt({raw} @ s{S}) = {q}: 8N exceeds (2q+1)^3"
);
if qa > 0 {
let t = two_q - U256::from(1u8);
let lower = t * t * t;
assert!(
eight_n > lower,
"cbrt({raw} @ s{S}) = {q}: 8N at/below (2q-1)^3"
);
}
}
for &raw in &[
1_i128,
2,
7,
8,
9,
26,
27,
28,
999_999_999_999,
1_000_000_000_000,
123_456_789_012_345,
-8,
-27,
-1_000_000_000_000,
] {
check::<0>(raw);
check::<6>(raw);
check::<12>(raw);
}
for &raw in &[i128::MAX, i128::MIN + 1, i128::MAX / 11] {
check::<0>(raw);
check::<2>(raw);
}
}
}