use crate::core_type::I128;
impl<const SCALE: u32> I128<SCALE> {
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn ln(self) -> Self {
todo!("strict: integer-only ln not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn ln(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().ln())
}
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn log(self, base: Self) -> Self {
todo!("strict: integer-only log not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn log(self, base: Self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().log(base.to_f64_lossy()))
}
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn log2(self) -> Self {
todo!("strict: integer-only log2 not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn log2(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().log2())
}
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn log10(self) -> Self {
todo!("strict: integer-only log10 not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn log10(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().log10())
}
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn exp(self) -> Self {
todo!("strict: integer-only exp not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn exp(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().exp())
}
#[cfg(feature = "strict")]
#[inline]
#[must_use]
pub fn exp2(self) -> Self {
todo!("strict: integer-only exp2 not yet implemented")
}
#[cfg(not(feature = "strict"))]
#[inline]
#[must_use]
pub fn exp2(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().exp2())
}
}
#[cfg(all(test, not(feature = "strict")))]
mod tests {
use crate::consts::DecimalConsts;
use crate::core_type::I128s12;
const LOG_EXP_TOLERANCE_LSB: i128 = 32;
const ROUND_TRIP_TOLERANCE_LSB: i128 = 128;
const FOUR_LSB: i128 = 4;
fn within_lsb(actual: I128s12, expected: I128s12, lsb: i128) -> bool {
let diff = (actual.to_bits() - expected.to_bits()).abs();
diff <= lsb
}
#[test]
fn exp_zero_is_one() {
assert_eq!(I128s12::ZERO.exp(), I128s12::ONE);
}
#[test]
fn exp2_zero_is_one() {
assert_eq!(I128s12::ZERO.exp2(), I128s12::ONE);
}
#[test]
fn ln_one_is_zero() {
assert_eq!(I128s12::ONE.ln(), I128s12::ZERO);
}
#[test]
fn log2_one_is_zero() {
assert_eq!(I128s12::ONE.log2(), I128s12::ZERO);
}
#[test]
fn log10_one_is_zero() {
assert_eq!(I128s12::ONE.log10(), I128s12::ZERO);
}
#[test]
fn log2_of_eight_is_three() {
let eight = I128s12::from_int(8);
let result = eight.log2();
let expected = I128s12::from_int(3);
assert!(
within_lsb(result, expected, LOG_EXP_TOLERANCE_LSB),
"log2(8) bits {}, expected 3 bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn log10_of_thousand_is_three() {
let thousand = I128s12::from_int(1000);
let result = thousand.log10();
let expected = I128s12::from_int(3);
assert!(
within_lsb(result, expected, LOG_EXP_TOLERANCE_LSB),
"log10(1000) bits {}, expected 3 bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn log10_of_power_of_ten() {
for n in [1_i64, 2, 4, 6] {
let pow_of_ten = I128s12::from_int(10_i64.pow(n as u32));
let result = pow_of_ten.log10();
let expected = I128s12::from_int(n);
assert!(
within_lsb(result, expected, LOG_EXP_TOLERANCE_LSB),
"log10(10^{n}) bits {}, expected {n} bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
}
#[test]
fn log2_of_power_of_two() {
for n in [1_i64, 2, 4, 8, 16] {
let pow_of_two = I128s12::from_int(2_i64.pow(n as u32));
let result = pow_of_two.log2();
let expected = I128s12::from_int(n);
assert!(
within_lsb(result, expected, LOG_EXP_TOLERANCE_LSB),
"log2(2^{n}) bits {}, expected {n} bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
}
#[test]
fn exp_of_ln_round_trip() {
for raw in [
100_000_000_000_i128, 500_000_000_000_i128, 1_234_567_890_123_i128, 4_567_891_234_567_i128, 7_890_123_456_789_i128, 45_678_912_345_679_i128, 78_901_234_567_890_i128, ] {
let x = I128s12::from_bits(raw);
let recovered = x.ln().exp();
assert!(
within_lsb(recovered, x, ROUND_TRIP_TOLERANCE_LSB),
"exp(ln(x)) != x for raw={raw}: got bits {} (delta {})",
recovered.to_bits(),
(recovered.to_bits() - x.to_bits()).abs(),
);
}
}
#[test]
fn exp_of_ln_e_round_trip() {
let e = I128s12::e();
let recovered = e.ln().exp();
assert!(
within_lsb(recovered, e, LOG_EXP_TOLERANCE_LSB),
"exp(ln(e)) != e: got bits {} (delta {})",
recovered.to_bits(),
(recovered.to_bits() - e.to_bits()).abs(),
);
}
#[test]
fn ln_of_exp_round_trip() {
for raw in [
-2_345_678_901_234_i128, -500_000_000_000_i128, 500_000_000_000_i128, 1_234_567_890_123_i128, 7_890_123_456_789_i128, ] {
let x = I128s12::from_bits(raw);
let recovered = x.exp().ln();
assert!(
within_lsb(recovered, x, FOUR_LSB),
"ln(exp(x)) != x for raw={raw}: got bits {} (delta {})",
recovered.to_bits(),
(recovered.to_bits() - x.to_bits()).abs(),
);
}
}
#[test]
fn log_base_e_matches_ln() {
let e = I128s12::e();
for raw in [
500_000_000_000_i128, 1_234_567_890_123_i128, 4_567_891_234_567_i128, 7_890_123_456_789_i128, ] {
let x = I128s12::from_bits(raw);
let via_log = x.log(e);
let via_ln = x.ln();
assert!(
within_lsb(via_log, via_ln, FOUR_LSB),
"log(x, e) != ln(x) for raw={raw}: log bits {}, ln bits {}",
via_log.to_bits(),
via_ln.to_bits(),
);
}
}
#[test]
fn log_base_two_matches_log2() {
let two = I128s12::from_int(2);
for raw in [
500_000_000_000_i128, 1_234_567_890_123_i128, 4_567_891_234_567_i128, 7_890_123_456_789_i128, ] {
let x = I128s12::from_bits(raw);
let via_log = x.log(two);
let via_log2 = x.log2();
assert!(
within_lsb(via_log, via_log2, FOUR_LSB),
"log(x, 2) != log2(x) for raw={raw}: log bits {}, log2 bits {}",
via_log.to_bits(),
via_log2.to_bits(),
);
}
}
#[test]
fn log_base_ten_matches_log10() {
let ten = I128s12::from_int(10);
for raw in [
500_000_000_000_i128, 1_234_567_890_123_i128, 4_567_891_234_567_i128, 7_890_123_456_789_i128, ] {
let x = I128s12::from_bits(raw);
let via_log = x.log(ten);
let via_log10 = x.log10();
assert!(
within_lsb(via_log, via_log10, FOUR_LSB),
"log(x, 10) != log10(x) for raw={raw}: log bits {}, log10 bits {}",
via_log.to_bits(),
via_log10.to_bits(),
);
}
}
#[test]
fn exp2_matches_integer_power_of_two() {
for n in [0_i64, 1, 2, 4, 8] {
let result = I128s12::from_int(n).exp2();
let expected = I128s12::from_int(2_i64.pow(n as u32));
assert!(
within_lsb(result, expected, LOG_EXP_TOLERANCE_LSB),
"exp2({n}) bits {}, expected 2^{n} bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
}
}