use std::fmt;
use std::ops::{Add, Sub, Mul, Div, Neg, AddAssign, SubAssign, MulAssign, DivAssign};
use crate::fixed_point::canonical::{
LazyExpr, StackValue, evaluate, gmath_parse, CompactShadow,
};
use crate::fixed_point::universal::fasc::stack_evaluator::{
BinaryStorage, ComputeStorage, upscale_to_compute, downscale_to_storage,
};
pub use crate::fixed_point::core_types::errors::OverflowDetected;
#[cfg(table_format = "q64_64")]
use crate::fixed_point::multiply_binary_i128;
#[cfg(table_format = "q64_64")]
use crate::fixed_point::I256;
#[cfg(table_format = "q128_128")]
use crate::fixed_point::{I256, I512};
#[cfg(table_format = "q256_256")]
use crate::fixed_point::{I512, I1024};
#[cfg(table_format = "q16_16")]
const STORAGE_TIER: u8 = 1;
#[cfg(table_format = "q32_32")]
const STORAGE_TIER: u8 = 2;
#[cfg(table_format = "q64_64")]
const STORAGE_TIER: u8 = 3;
#[cfg(table_format = "q128_128")]
const STORAGE_TIER: u8 = 4;
#[cfg(table_format = "q256_256")]
const STORAGE_TIER: u8 = 5;
#[cfg(table_format = "q16_16")]
const FRAC_BITS: i32 = crate::fixed_point::frac_config::FRAC_BITS as i32;
#[cfg(table_format = "q32_32")]
const FRAC_BITS: i32 = 32;
#[cfg(table_format = "q64_64")]
const FRAC_BITS: i32 = 64;
#[cfg(table_format = "q128_128")]
const FRAC_BITS: i32 = 128;
#[cfg(table_format = "q256_256")]
const FRAC_BITS: i32 = 256;
fn direct_exp(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i1024(x) }
}
fn direct_ln(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i1024(x) }
}
fn direct_sqrt(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sqrt_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sqrt_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sqrt_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sqrt_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sqrt_binary_i1024(x) }
}
fn direct_sin(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sin_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sin_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sin_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sin_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::sin_binary_i1024(x) }
}
fn direct_cos(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::cos_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::cos_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::cos_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::cos_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::cos_binary_i1024(x) }
}
fn direct_atan2(y: ComputeStorage, x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan2_binary_i128(y as i128, x as i128) as i64 }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan2_binary_i128(y, x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan2_binary_i256(y, x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan2_binary_i512(y, x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan2_binary_i1024(y, x) }
}
use crate::fixed_point::universal::fasc::stack_evaluator::{
compute_add, compute_subtract, compute_negate, compute_multiply, compute_divide, compute_halve,
};
#[inline] fn compute_add_direct(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage { compute_add(a, b) }
#[inline] fn compute_sub_direct(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage { compute_subtract(a, b) }
#[inline] fn compute_neg_direct(a: ComputeStorage) -> ComputeStorage { compute_negate(a) }
#[inline] fn compute_mul_direct(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage { compute_multiply(a, b) }
#[inline] fn compute_divide_direct(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage {
compute_divide(a, b).expect("division by zero in transcendental composition")
}
#[inline] fn compute_halve_direct(a: ComputeStorage) -> ComputeStorage { compute_halve(a) }
fn compute_one() -> ComputeStorage { upscale_to_compute(one_storage()) }
fn compute_pi_half() -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i128() as i64 }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i128() }
#[cfg(table_format = "q64_64")]
{ upscale_to_compute(crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i128()) }
#[cfg(table_format = "q128_128")]
{ upscale_to_compute(crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i256()) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i1024() }
}
fn one_storage() -> BinaryStorage {
#[cfg(table_format = "q16_16")]
{ 1i32 << crate::fixed_point::frac_config::FRAC_BITS }
#[cfg(table_format = "q32_32")]
{ 1i64 << 32 }
#[cfg(table_format = "q64_64")]
{ 1i128 << 64 }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::i256::I256::from_i128(1) << 128 }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::i512::I512::from_i128(1) << 256 }
}
fn direct_atan(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan_binary_i64(x) }
#[cfg(table_format = "q32_32")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan_binary_i128(x) }
#[cfg(table_format = "q64_64")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan_binary_i256(x) }
#[cfg(table_format = "q128_128")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan_binary_i512(x) }
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::transcendental::atan_binary_i1024(x) }
}
#[cfg(table_format = "q16_16")]
const MAX_DECIMAL_DIGITS: usize = crate::fixed_point::frac_config::MAX_DECIMAL_DIGITS;
#[cfg(table_format = "q32_32")]
const MAX_DECIMAL_DIGITS: usize = 9;
#[cfg(table_format = "q64_64")]
const MAX_DECIMAL_DIGITS: usize = 19;
#[cfg(table_format = "q128_128")]
const MAX_DECIMAL_DIGITS: usize = 38;
#[cfg(table_format = "q256_256")]
const MAX_DECIMAL_DIGITS: usize = 77;
#[derive(Clone, Copy, Debug)]
pub struct FixedPoint {
raw: BinaryStorage,
}
impl PartialEq for FixedPoint {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
}
impl Eq for FixedPoint {}
impl PartialOrd for FixedPoint {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for FixedPoint {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.raw.cmp(&other.raw)
}
}
impl FixedPoint {
#[cfg(table_format = "q16_16")]
pub const ZERO: Self = Self { raw: 0i32 };
#[cfg(table_format = "q32_32")]
pub const ZERO: Self = Self { raw: 0i64 };
#[cfg(table_format = "q64_64")]
pub const ZERO: Self = Self { raw: 0i128 };
#[cfg(table_format = "q128_128")]
pub const ZERO: Self = Self { raw: I256::zero() };
#[cfg(table_format = "q256_256")]
pub const ZERO: Self = Self { raw: I512::zero() };
#[inline]
pub fn one() -> Self {
#[cfg(table_format = "q16_16")]
{ Self { raw: 1i32 << FRAC_BITS } }
#[cfg(table_format = "q32_32")]
{ Self { raw: 1i64 << 32 } }
#[cfg(table_format = "q64_64")]
{ Self { raw: 1i128 << 64 } }
#[cfg(table_format = "q128_128")]
{ Self { raw: I256::from_i128(1) << 128usize } }
#[cfg(table_format = "q256_256")]
{ Self { raw: I512::from_i128(1) << 256usize } }
}
#[inline]
pub fn from_raw(raw: BinaryStorage) -> Self {
Self { raw }
}
#[inline]
pub fn raw(self) -> BinaryStorage {
self.raw
}
#[inline]
pub fn from_int(v: i32) -> Self {
#[cfg(table_format = "q16_16")]
{ Self { raw: (v as i32) << FRAC_BITS } }
#[cfg(table_format = "q32_32")]
{ Self { raw: (v as i64) << 32 } }
#[cfg(table_format = "q64_64")]
{ Self { raw: (v as i128) << 64 } }
#[cfg(table_format = "q128_128")]
{ Self { raw: I256::from_i128(v as i128) << 128usize } }
#[cfg(table_format = "q256_256")]
{ Self { raw: I512::from_i128(v as i128) << 256usize } }
}
#[inline]
pub fn to_int(self) -> i32 {
#[cfg(table_format = "q16_16")]
{ (self.raw >> FRAC_BITS) as i32 }
#[cfg(table_format = "q32_32")]
{ (self.raw >> 32) as i32 }
#[cfg(table_format = "q64_64")]
{ (self.raw >> 64) as i32 }
#[cfg(table_format = "q128_128")]
{ (self.raw >> 128u32).as_i128() as i32 }
#[cfg(table_format = "q256_256")]
{ (self.raw >> 256usize).as_i128() as i32 }
}
#[inline]
pub fn abs(self) -> Self {
if self.is_negative() { -self } else { self }
}
#[inline]
pub fn is_negative(self) -> bool {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{ self.raw < 0 }
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{ self.raw.is_negative() }
}
#[inline]
pub fn is_zero(self) -> bool {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{ self.raw == 0 }
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{ self.raw.is_zero() }
}
pub fn from_f32(v: f32) -> Self {
let bits = v.to_bits();
if bits & 0x7FFF_FFFF == 0 {
return Self::ZERO;
}
let sign = (bits >> 31) != 0;
let raw_exp = ((bits >> 23) & 0xFF) as i32;
let raw_mantissa = bits & 0x7F_FFFF;
if raw_exp == 0xFF {
panic!("FixedPoint::from_f32: infinity or NaN");
}
let (mantissa, exp_offset) = if raw_exp == 0 {
(raw_mantissa as i128, -126 - 23)
} else {
((raw_mantissa | 0x80_0000) as i128, raw_exp - 127 - 23)
};
let shift = exp_offset + FRAC_BITS;
let raw = Self::shift_mantissa_to_raw(mantissa, shift);
if sign { -Self { raw } } else { Self { raw } }
}
pub fn from_f64(v: f64) -> Self {
let bits = v.to_bits();
if bits & 0x7FFF_FFFF_FFFF_FFFF == 0 {
return Self::ZERO;
}
let sign = (bits >> 63) != 0;
let raw_exp = ((bits >> 52) & 0x7FF) as i32;
let raw_mantissa = bits & 0x000F_FFFF_FFFF_FFFF;
if raw_exp == 0x7FF {
panic!("FixedPoint::from_f64: infinity or NaN");
}
let (mantissa, exp_offset) = if raw_exp == 0 {
(raw_mantissa as i128, -1022 - 52)
} else {
((raw_mantissa | 0x0010_0000_0000_0000) as i128, raw_exp - 1023 - 52)
};
let shift = exp_offset + FRAC_BITS;
let raw = Self::shift_mantissa_to_raw(mantissa, shift);
if sign { -Self { raw } } else { Self { raw } }
}
pub fn to_f32(self) -> f32 {
let sv = self.to_stack_value();
let s = sv.to_decimal_string(10);
s.parse::<f32>().unwrap_or(0.0)
}
pub fn to_f64(self) -> f64 {
let sv = self.to_stack_value();
let s = sv.to_decimal_string(MAX_DECIMAL_DIGITS);
s.parse::<f64>().unwrap_or(0.0)
}
pub fn from_str(s: &str) -> Self {
use crate::fixed_point::universal::fasc::mode;
let old_mode = mode::get_mode();
mode::set_mode(mode::GmathMode {
compute: mode::ComputeMode::Binary,
output: mode::OutputMode::Binary,
});
let expr = gmath_parse(s).expect("FixedPoint::from_str: parse failed");
let result = evaluate(&expr).expect("FixedPoint::from_str: eval failed");
mode::set_mode(old_mode);
Self::from_stack_value(result)
}
#[inline]
fn direct_unary<F: FnOnce(ComputeStorage) -> ComputeStorage>(self, f: F) -> Self {
let compute = upscale_to_compute(self.raw);
let result = f(compute);
Self { raw: downscale_to_storage(result).expect("transcendental overflow") }
}
#[inline]
fn try_direct_unary<F: FnOnce(ComputeStorage) -> ComputeStorage>(self, f: F) -> Result<Self, OverflowDetected> {
let compute = upscale_to_compute(self.raw);
let result = f(compute);
Ok(Self { raw: downscale_to_storage(result)? })
}
pub fn exp(self) -> Self { self.direct_unary(direct_exp) }
pub fn ln(self) -> Self { self.direct_unary(direct_ln) }
pub fn sqrt(self) -> Self { self.direct_unary(direct_sqrt) }
pub fn sin(self) -> Self { self.direct_unary(direct_sin) }
pub fn cos(self) -> Self { self.direct_unary(direct_cos) }
pub fn sincos(self) -> (Self, Self) {
self.try_sincos().expect("sincos: overflow or domain error")
}
pub fn tan(self) -> Self {
let c = upscale_to_compute(self.raw);
let s = direct_sin(c);
let c_val = direct_cos(c);
let result = compute_divide_direct(s, c_val);
Self { raw: downscale_to_storage(result).expect("tan overflow") }
}
pub fn atan(self) -> Self { self.direct_unary(direct_atan) }
pub fn asin(self) -> Self {
let c = upscale_to_compute(self.raw);
let one = compute_one();
let x2 = compute_mul_direct(c, c);
let denom = direct_sqrt(compute_sub_direct(one, x2));
let ratio = compute_divide_direct(c, denom);
Self { raw: downscale_to_storage(direct_atan(ratio)).expect("asin overflow") }
}
pub fn acos(self) -> Self {
let c = upscale_to_compute(self.raw);
let one = compute_one();
let x2 = compute_mul_direct(c, c);
let denom = direct_sqrt(compute_sub_direct(one, x2));
let ratio = compute_divide_direct(c, denom);
let asin_val = direct_atan(ratio);
let pi_half = compute_pi_half();
Self { raw: downscale_to_storage(compute_sub_direct(pi_half, asin_val)).expect("acos overflow") }
}
pub fn sinh(self) -> Self {
let c = upscale_to_compute(self.raw);
let ep = direct_exp(c);
let en = direct_exp(compute_neg_direct(c));
let result = compute_halve_direct(compute_sub_direct(ep, en));
Self { raw: downscale_to_storage(result).expect("sinh overflow") }
}
pub fn cosh(self) -> Self {
let c = upscale_to_compute(self.raw);
let ep = direct_exp(c);
let en = direct_exp(compute_neg_direct(c));
let result = compute_halve_direct(compute_add_direct(ep, en));
Self { raw: downscale_to_storage(result).expect("cosh overflow") }
}
pub fn sinhcosh(self) -> (Self, Self) {
self.try_sinhcosh().expect("sinhcosh: overflow or domain error")
}
pub fn tanh(self) -> Self {
let c = upscale_to_compute(self.raw);
let two_x = compute_add_direct(c, c);
let e2x = direct_exp(two_x);
let one = compute_one();
let num = compute_sub_direct(e2x, one);
let den = compute_add_direct(e2x, one);
Self { raw: downscale_to_storage(compute_divide_direct(num, den)).expect("tanh overflow") }
}
pub fn asinh(self) -> Self {
let c = upscale_to_compute(self.raw);
let one = compute_one();
let x2 = compute_mul_direct(c, c);
let inner = direct_sqrt(compute_add_direct(x2, one));
Self { raw: downscale_to_storage(direct_ln(compute_add_direct(c, inner))).expect("asinh overflow") }
}
pub fn acosh(self) -> Self {
let c = upscale_to_compute(self.raw);
let one = compute_one();
let x2 = compute_mul_direct(c, c);
let inner = direct_sqrt(compute_sub_direct(x2, one));
Self { raw: downscale_to_storage(direct_ln(compute_add_direct(c, inner))).expect("acosh overflow") }
}
pub fn atanh(self) -> Self {
let c = upscale_to_compute(self.raw);
let one = compute_one();
let num = compute_add_direct(one, c);
let den = compute_sub_direct(one, c);
let ratio = compute_divide_direct(num, den);
Self { raw: downscale_to_storage(compute_halve_direct(direct_ln(ratio))).expect("atanh overflow") }
}
pub fn pow(self, exponent: Self) -> Self {
let xc = upscale_to_compute(self.raw);
let yc = upscale_to_compute(exponent.raw);
let ln_x = direct_ln(xc);
let y_ln_x = compute_mul_direct(yc, ln_x);
Self { raw: downscale_to_storage(direct_exp(y_ln_x)).expect("pow overflow") }
}
pub fn atan2(self, x: Self) -> Self {
let yc = upscale_to_compute(self.raw);
let xc = upscale_to_compute(x.raw);
let result = direct_atan2(yc, xc);
Self { raw: downscale_to_storage(result).expect("atan2 overflow") }
}
pub fn try_exp(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_exp) }
pub fn try_ln(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_ln) }
pub fn try_sqrt(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_sqrt) }
pub fn try_sin(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_sin) }
pub fn try_cos(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_cos) }
pub fn try_sincos(self) -> Result<(Self, Self), OverflowDetected> {
use super::linalg::{upscale_to_compute, round_to_storage, sincos_at_compute_tier};
let compute_val = upscale_to_compute(self.raw());
let (sin_c, cos_c) = sincos_at_compute_tier(compute_val);
Ok((Self::from_raw(round_to_storage(sin_c)), Self::from_raw(round_to_storage(cos_c))))
}
pub fn try_sinhcosh(self) -> Result<(Self, Self), OverflowDetected> {
use crate::fixed_point::universal::fasc::stack_evaluator::sinhcosh_at_compute_tier;
let compute_val = upscale_to_compute(self.raw);
let (sinh_c, cosh_c) = sinhcosh_at_compute_tier(compute_val);
Ok((
Self { raw: downscale_to_storage(sinh_c)? },
Self { raw: downscale_to_storage(cosh_c)? },
))
}
#[cfg(any(table_format = "q16_16", table_format = "q32_32"))]
pub fn sincos_wide(angle_q32_32: i64) -> (Self, Self) {
use crate::fixed_point::domains::binary_fixed::transcendental::{
sin_binary_i128, cos_binary_i128,
};
let angle_q64 = (angle_q32_32 as i128) << 32;
let sin_q64 = sin_binary_i128(angle_q64);
let cos_q64 = cos_binary_i128(angle_q64);
#[cfg(table_format = "q16_16")]
{
use crate::fixed_point::frac_config;
let shift = 64 - frac_config::FRAC_BITS;
let sin_round = (sin_q64 >> (shift - 1)) & 1;
let cos_round = (cos_q64 >> (shift - 1)) & 1;
let sin_raw = ((sin_q64 >> shift) + sin_round) as i32;
let cos_raw = ((cos_q64 >> shift) + cos_round) as i32;
(Self::from_raw(sin_raw), Self::from_raw(cos_raw))
}
#[cfg(table_format = "q32_32")]
{
let sin_round = (sin_q64 >> 31) & 1;
let cos_round = (cos_q64 >> 31) & 1;
let sin_raw = ((sin_q64 >> 32) + sin_round) as i64;
let cos_raw = ((cos_q64 >> 32) + cos_round) as i64;
(Self::from_raw(sin_raw), Self::from_raw(cos_raw))
}
}
pub fn try_tan(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::tan) }
pub fn try_atan(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::atan) }
pub fn try_asin(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::asin) }
pub fn try_acos(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::acos) }
pub fn try_sinh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::sinh) }
pub fn try_cosh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::cosh) }
pub fn try_tanh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::tanh) }
pub fn try_asinh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::asinh) }
pub fn try_acosh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::acosh) }
pub fn try_atanh(self) -> Result<Self, OverflowDetected> { self.try_apply_unary(LazyExpr::atanh) }
pub fn try_pow(self, exponent: Self) -> Result<Self, OverflowDetected> {
let sv1 = self.to_stack_value();
let sv2 = exponent.to_stack_value();
let expr = LazyExpr::from(sv1).pow(LazyExpr::from(sv2));
let result = evaluate(&expr)?;
Self::try_from_stack_value(result)
}
pub fn try_atan2(self, x: Self) -> Result<Self, OverflowDetected> {
let sv_y = self.to_stack_value();
let sv_x = x.to_stack_value();
let expr = LazyExpr::from(sv_y).atan2(LazyExpr::from(sv_x));
let result = evaluate(&expr)?;
Self::try_from_stack_value(result)
}
#[inline]
pub(crate) fn to_stack_value(self) -> StackValue {
StackValue::Binary(STORAGE_TIER, self.raw, CompactShadow::None)
}
pub(crate) fn from_stack_value(sv: StackValue) -> Self {
Self::try_from_stack_value(sv).expect("FixedPoint: domain conversion failed")
}
pub(crate) fn try_from_stack_value(sv: StackValue) -> Result<Self, OverflowDetected> {
match sv.as_binary_storage() {
Some(raw) => Ok(Self { raw }),
None => {
let zero_sv = StackValue::Binary(STORAGE_TIER, Self::ZERO.raw, CompactShadow::None);
let expr = LazyExpr::from(sv) + LazyExpr::from(zero_sv);
let result = evaluate(&expr)?;
result.as_binary_storage()
.map(|raw| Self { raw })
.ok_or(OverflowDetected::InvalidInput)
}
}
}
#[allow(dead_code)]
fn apply_unary(self, f: fn(LazyExpr) -> LazyExpr) -> Self {
self.try_apply_unary(f).expect("transcendental: overflow or domain error")
}
fn try_apply_unary(self, f: fn(LazyExpr) -> LazyExpr) -> Result<Self, OverflowDetected> {
let sv = self.to_stack_value();
let expr = f(LazyExpr::from(sv));
let result = evaluate(&expr)?;
Self::try_from_stack_value(result)
}
fn shift_mantissa_to_raw(mantissa: i128, shift: i32) -> BinaryStorage {
#[cfg(table_format = "q16_16")]
{
if shift >= 32 {
panic!("FixedPoint: value too large for Q16.16");
} else if shift >= 0 {
let wide = mantissa.checked_shl(shift as u32)
.expect("FixedPoint: value too large for Q16.16");
wide as i32
} else if shift > -128 {
(mantissa >> ((-shift) as u32)) as i32
} else {
0i32
}
}
#[cfg(table_format = "q32_32")]
{
if shift >= 64 {
panic!("FixedPoint: value too large for Q32.32");
} else if shift >= 0 {
let wide = mantissa.checked_shl(shift as u32)
.expect("FixedPoint: value too large for Q32.32");
wide as i64
} else if shift > -128 {
(mantissa >> ((-shift) as u32)) as i64
} else {
0i64
}
}
#[cfg(table_format = "q64_64")]
{
if shift >= 128 {
panic!("FixedPoint: value too large for Q64.64");
} else if shift >= 0 {
mantissa.checked_shl(shift as u32)
.expect("FixedPoint: value too large for Q64.64")
} else if shift > -128 {
mantissa >> ((-shift) as u32)
} else {
0i128
}
}
#[cfg(table_format = "q128_128")]
{
let m = I256::from_i128(mantissa);
if shift >= 256 {
panic!("FixedPoint: value too large for Q128.128");
} else if shift >= 0 {
m << (shift as usize)
} else if shift > -256 {
m >> ((-shift) as u32)
} else {
I256::zero()
}
}
#[cfg(table_format = "q256_256")]
{
let m = I512::from_i128(mantissa);
if shift >= 512 {
panic!("FixedPoint: value too large for Q256.256");
} else if shift >= 0 {
m << (shift as usize)
} else if shift > -512 {
m >> ((-shift) as usize)
} else {
I512::zero()
}
}
}
}
impl fmt::Display for FixedPoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sv = self.to_stack_value();
fmt::Display::fmt(&sv, f)
}
}
impl Default for FixedPoint {
#[inline]
fn default() -> Self {
Self::ZERO
}
}
impl Add for FixedPoint {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{ Self { raw: self.raw.wrapping_add(rhs.raw) } }
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{ Self { raw: self.raw + rhs.raw } }
}
}
impl Sub for FixedPoint {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{ Self { raw: self.raw.wrapping_sub(rhs.raw) } }
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{ Self { raw: self.raw - rhs.raw } }
}
}
impl Mul for FixedPoint {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self { raw: fixed_multiply(self.raw, rhs.raw) }
}
}
impl Div for FixedPoint {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
Self { raw: fixed_divide(self.raw, rhs.raw) }
}
}
impl Neg for FixedPoint {
type Output = Self;
#[inline]
fn neg(self) -> Self {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{ Self { raw: self.raw.wrapping_neg() } }
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{ Self { raw: -self.raw } }
}
}
impl AddAssign for FixedPoint {
#[inline]
fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; }
}
impl SubAssign for FixedPoint {
#[inline]
fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; }
}
impl MulAssign for FixedPoint {
#[inline]
fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; }
}
impl DivAssign for FixedPoint {
#[inline]
fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; }
}
#[inline]
fn fixed_multiply(a: BinaryStorage, b: BinaryStorage) -> BinaryStorage {
#[cfg(table_format = "q16_16")]
{
let wide = (a as i64) * (b as i64);
(wide >> FRAC_BITS) as i32
}
#[cfg(table_format = "q32_32")]
{
let wide = (a as i128) * (b as i128);
(wide >> 32) as i64
}
#[cfg(table_format = "q64_64")]
{
multiply_binary_i128(a, b)
}
#[cfg(table_format = "q128_128")]
{
let a_neg = a.is_negative();
let b_neg = b.is_negative();
let result_neg = a_neg != b_neg;
let abs_a = if a_neg { -a } else { a };
let abs_b = if b_neg { -b } else { b };
let product = abs_a.mul_to_i512(abs_b);
let shifted = (product >> 128usize).as_i256();
if result_neg { -shifted } else { shifted }
}
#[cfg(table_format = "q256_256")]
{
let a_neg = a.is_negative();
let b_neg = b.is_negative();
let result_neg = a_neg != b_neg;
let abs_a = if a_neg { -a } else { a };
let abs_b = if b_neg { -b } else { b };
let product = abs_a.mul_to_i1024(abs_b);
let shifted = (product >> 256usize).as_i512();
if result_neg { -shifted } else { shifted }
}
}
#[inline]
fn fixed_divide(a: BinaryStorage, b: BinaryStorage) -> BinaryStorage {
#[cfg(table_format = "q16_16")]
{
let num = (a as i64) << FRAC_BITS;
let den = b as i64;
assert!(den != 0, "FixedPoint: division by zero");
(num / den) as i32
}
#[cfg(table_format = "q32_32")]
{
let num = (a as i128) << 32;
let den = b as i128;
assert!(den != 0, "FixedPoint: division by zero");
(num / den) as i64
}
#[cfg(table_format = "q64_64")]
{
let num = I256::from_i128(a) << 64usize;
let den = I256::from_i128(b);
assert!(!den.is_zero(), "FixedPoint: division by zero");
(num / den).as_i128()
}
#[cfg(table_format = "q128_128")]
{
let num = I512::from_i256(a) << 128usize;
let den = I512::from_i256(b);
assert!(!den.is_zero(), "FixedPoint: division by zero");
(num / den).as_i256()
}
#[cfg(table_format = "q256_256")]
{
let num = I1024::from_i512(a) << 256usize;
let den = I1024::from_i512(b);
assert!(!den.is_zero(), "FixedPoint: division by zero");
(num / den).as_i512()
}
}