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::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_compute_tier_i64(y, x) }
#[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,
};
use crate::fixed_point::universal::fasc::stack_evaluator::compute::compute_checked_add;
#[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) }
#[inline]
fn exp_ceilinged(v: &ComputeStorage) -> bool {
use crate::fixed_point::universal::fasc::stack_evaluator::compute::exp_sentinel_reached;
exp_sentinel_reached(v)
}
fn compute_one() -> ComputeStorage { upscale_to_compute(one_storage()) }
fn compute_pi_half() -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{
use crate::fixed_point::frac_config;
let shift = 64 - (frac_config::COMPUTE_FRAC_BITS as u32);
let q64 = crate::fixed_point::domains::binary_fixed::transcendental::pi_half_i128();
let round = (q64 >> (shift - 1)) & 1;
((q64 >> shift) + round) 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) }
}
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
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 raw_slice(values: &[Self]) -> &[BinaryStorage] {
unsafe { std::slice::from_raw_parts(values.as_ptr() as *const BinaryStorage, values.len()) }
}
#[inline]
pub fn from_raw_slice(raws: &[BinaryStorage]) -> &[Self] {
unsafe { std::slice::from_raw_parts(raws.as_ptr() as *const Self, raws.len()) }
}
#[inline]
pub fn raw_slice_mut(values: &mut [Self]) -> &mut [BinaryStorage] {
unsafe { std::slice::from_raw_parts_mut(values.as_mut_ptr() as *mut BinaryStorage, values.len()) }
}
#[inline]
pub fn from_raw_slice_mut(raws: &mut [BinaryStorage]) -> &mut [Self] {
unsafe { std::slice::from_raw_parts_mut(raws.as_mut_ptr() as *mut Self, raws.len()) }
}
#[inline]
pub fn from_int(v: i32) -> Self {
Self::try_from_int(v).expect("FixedPoint::from_int: value outside the profile's range")
}
#[inline]
pub fn try_from_int(v: i32) -> Result<Self, OverflowDetected> {
#[cfg(table_format = "q16_16")]
{
let raw = (v as i64) << FRAC_BITS;
i32::try_from(raw).map(|raw| Self { raw }).map_err(|_| OverflowDetected::TierOverflow)
}
#[cfg(table_format = "q32_32")]
{ Ok(Self { raw: (v as i64) << 32 }) }
#[cfg(table_format = "q64_64")]
{ Ok(Self { raw: (v as i128) << 64 }) }
#[cfg(table_format = "q128_128")]
{ Ok(Self { raw: I256::from_i128(v as i128) << 128usize }) }
#[cfg(table_format = "q256_256")]
{ Ok(Self { raw: I512::from_i128(v as i128) << 256usize }) }
}
pub(crate) fn mul_count(self, n: usize) -> Self {
use crate::fixed_point::universal::fasc::stack_evaluator::compute::compute_mul_div_int;
let c = compute_mul_div_int(upscale_to_compute(self.raw), n as i64, 1)
.expect("FixedPoint::mul_count: product exceeds the compute tier");
Self { raw: downscale_to_storage(c).expect("FixedPoint::mul_count: product exceeds storage") }
}
pub(crate) fn div_count(self, n: usize) -> Self {
match i32::try_from(n).ok().and_then(|n| Self::try_from_int(n).ok()) {
Some(nf) => self / nf,
None => {
use crate::fixed_point::universal::fasc::stack_evaluator::compute::compute_mul_div_int;
let c = compute_mul_div_int(upscale_to_compute(self.raw), 1, n as i64)
.expect("FixedPoint::div_count: n > 0");
Self { raw: downscale_to_storage(c).expect("FixedPoint::div_count: quotient fits storage") }
}
}
}
#[inline]
pub fn to_int(self) -> i32 {
self.try_to_int().expect("FixedPoint::to_int: integer part outside the i32 range")
}
#[inline]
pub fn try_to_int(self) -> Result<i32, OverflowDetected> {
#[cfg(table_format = "q16_16")]
{ Ok(self.raw >> FRAC_BITS) }
#[cfg(table_format = "q32_32")]
{ i32::try_from(self.raw >> 32).map_err(|_| OverflowDetected::TierOverflow) }
#[cfg(table_format = "q64_64")]
{ i32::try_from(self.raw >> 64).map_err(|_| OverflowDetected::TierOverflow) }
#[cfg(table_format = "q128_128")]
{ i32::try_from((self.raw >> 128u32).as_i128()).map_err(|_| OverflowDetected::TierOverflow) }
#[cfg(table_format = "q256_256")]
{
let floor = self.raw >> 256usize;
if !floor.fits_in_i128() {
return Err(OverflowDetected::TierOverflow);
}
i32::try_from(floor.as_i128()).map_err(|_| OverflowDetected::TierOverflow)
}
}
#[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 {
Self::try_from_f32(v).unwrap_or_else(|e| panic!("FixedPoint::from_f32: {}", Self::float_input_error(e)))
}
pub fn try_from_f32(v: f32) -> Result<Self, OverflowDetected> {
let bits = v.to_bits();
let negative = (bits >> 31) != 0;
let raw_exp = ((bits >> 23) & 0xFF) as i32;
let fraction = (bits & 0x7F_FFFF) as u64;
if raw_exp == 0xFF {
return Err(if fraction == 0 { OverflowDetected::TierOverflow } else { OverflowDetected::InvalidInput });
}
let (mantissa, exp_offset) = if raw_exp == 0 {
(fraction, -126 - 23)
} else {
(fraction | 0x80_0000, raw_exp - 127 - 23)
};
Ok(Self { raw: Self::truncated_raw(mantissa, exp_offset + FRAC_BITS, negative)? })
}
pub fn from_f64(v: f64) -> Self {
Self::try_from_f64(v).unwrap_or_else(|e| panic!("FixedPoint::from_f64: {}", Self::float_input_error(e)))
}
pub fn try_from_f64(v: f64) -> Result<Self, OverflowDetected> {
let bits = v.to_bits();
let negative = (bits >> 63) != 0;
let raw_exp = ((bits >> 52) & 0x7FF) as i32;
let fraction = bits & 0x000F_FFFF_FFFF_FFFF;
if raw_exp == 0x7FF {
return Err(if fraction == 0 { OverflowDetected::TierOverflow } else { OverflowDetected::InvalidInput });
}
let (mantissa, exp_offset) = if raw_exp == 0 {
(fraction, -1022 - 52)
} else {
(fraction | 0x0010_0000_0000_0000, raw_exp - 1023 - 52)
};
Ok(Self { raw: Self::truncated_raw(mantissa, exp_offset + FRAC_BITS, negative)? })
}
fn float_input_error(e: OverflowDetected) -> &'static str {
match e {
OverflowDetected::InvalidInput => "NaN",
_ => "infinity or value outside the profile's range",
}
}
pub fn to_f32(self) -> f32 {
f32::from_bits(self.float_bits(23, 8) as u32)
}
pub fn to_f64(self) -> f64 {
f64::from_bits(self.float_bits(52, 11))
}
fn float_bits(self, fraction_bits: u32, exponent_bits: u32) -> u64 {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{
let magnitude = (self.raw as i128).unsigned_abs();
let words = [magnitude as u64, (magnitude >> 64) as u64];
ieee_bits(self.raw < 0, &words, FRAC_BITS, fraction_bits, exponent_bits)
}
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{
let negative = self.raw.is_negative();
let magnitude = if negative { -self.raw } else { self.raw };
ieee_bits(negative, &magnitude.words, FRAC_BITS, fraction_bits, exponent_bits)
}
}
pub fn from_str(s: &str) -> Self {
Self::try_from_str(s).expect("FixedPoint::from_str: parse failed")
}
pub fn try_from_str(s: &str) -> Result<Self, OverflowDetected> {
match super::decimal_literal::parse(s, FRAC_BITS as u32, crate::fixed_point::frac_config::STORAGE_BITS) {
Some(converted) => converted.map(|c| Self { raw: super::decimal_literal::to_storage(&c) }),
None => Self::try_from_str_canonical(s),
}
}
fn try_from_str_canonical(s: &str) -> Result<Self, OverflowDetected> {
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 result = gmath_parse(s).and_then(|expr| evaluate(&expr));
mode::set_mode(old_mode);
Self::try_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));
assert!(
!exp_ceilinged(&ep) && !exp_ceilinged(&en),
"sinh overflow: exp at compute ceiling"
);
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));
assert!(
!exp_ceilinged(&ep) && !exp_ceilinged(&en),
"cosh overflow: exp at compute ceiling"
);
let sum = compute_checked_add(ep, en).expect("cosh overflow");
let result = compute_halve_direct(sum);
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 den = match compute_checked_add(e2x, one) {
Ok(v) => v,
Err(_) => return Self::one(),
};
let num = compute_sub_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> {
if self <= Self::ZERO { return Err(OverflowDetected::DomainError); }
self.try_direct_unary(direct_ln)
}
pub fn inv_sqrt(self) -> Self {
self.try_inv_sqrt().expect("inv_sqrt: domain error or overflow")
}
pub fn try_inv_sqrt(self) -> Result<Self, OverflowDetected> {
use crate::fixed_point::universal::fasc::stack_evaluator::compute::{
compute_divide, make_compute_int, sqrt_at_compute_tier,
};
if self <= Self::ZERO { return Err(OverflowDetected::DomainError); }
let s = sqrt_at_compute_tier(upscale_to_compute(self.raw));
let inv = compute_divide(make_compute_int(1), s)?;
Ok(Self { raw: downscale_to_storage(inv)? })
}
pub fn try_sqrt(self) -> Result<Self, OverflowDetected> {
if self < Self::ZERO { return Err(OverflowDetected::DomainError); }
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) {
Self::sincos_wide_q64((angle_q32_32 as i128) << 32)
}
#[cfg(any(table_format = "q16_16", table_format = "q32_32"))]
pub fn sincos_wide_q64(angle_q64: i128) -> (Self, Self) {
use crate::fixed_point::domains::binary_fixed::transcendental::{
sin_binary_i128, cos_binary_i128,
};
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> {
use crate::fixed_point::universal::fasc::stack_evaluator::compute::compute_is_zero;
let c = upscale_to_compute(self.raw);
let s = direct_sin(c);
let c_val = direct_cos(c);
if compute_is_zero(&c_val) { return Err(OverflowDetected::DomainError); }
Ok(Self { raw: downscale_to_storage(compute_divide_direct(s, c_val))? })
}
pub fn try_atan(self) -> Result<Self, OverflowDetected> { self.try_direct_unary(direct_atan) }
pub fn try_asin(self) -> Result<Self, OverflowDetected> {
let one = Self::one();
let neg_one = Self::ZERO - one;
if self > one || self < neg_one { return Err(OverflowDetected::DomainError); }
if self == one {
return Ok(Self { raw: downscale_to_storage(compute_pi_half())? });
}
if self == neg_one {
return Ok(Self { raw: downscale_to_storage(compute_neg_direct(compute_pi_half()))? });
}
let c = upscale_to_compute(self.raw);
let x2 = compute_mul_direct(c, c);
let denom = direct_sqrt(compute_sub_direct(compute_one(), x2));
let ratio = compute_divide_direct(c, denom);
Ok(Self { raw: downscale_to_storage(direct_atan(ratio))? })
}
pub fn try_acos(self) -> Result<Self, OverflowDetected> {
let one = Self::one();
let neg_one = Self::ZERO - one;
if self > one || self < neg_one { return Err(OverflowDetected::DomainError); }
let pi_half = compute_pi_half();
let asin_c = if self == one {
pi_half
} else if self == neg_one {
compute_neg_direct(pi_half)
} else {
let c = upscale_to_compute(self.raw);
let x2 = compute_mul_direct(c, c);
let denom = direct_sqrt(compute_sub_direct(compute_one(), x2));
direct_atan(compute_divide_direct(c, denom))
};
Ok(Self { raw: downscale_to_storage(compute_sub_direct(pi_half, asin_c))? })
}
pub fn try_sinh(self) -> Result<Self, OverflowDetected> {
let c = upscale_to_compute(self.raw);
let ep = direct_exp(c);
let en = direct_exp(compute_neg_direct(c));
if exp_ceilinged(&ep) || exp_ceilinged(&en) {
return Err(OverflowDetected::TierOverflow);
}
Ok(Self { raw: downscale_to_storage(compute_halve_direct(compute_sub_direct(ep, en)))? })
}
pub fn try_cosh(self) -> Result<Self, OverflowDetected> {
let c = upscale_to_compute(self.raw);
let ep = direct_exp(c);
let en = direct_exp(compute_neg_direct(c));
if exp_ceilinged(&ep) || exp_ceilinged(&en) {
return Err(OverflowDetected::TierOverflow);
}
let sum = compute_checked_add(ep, en)?;
Ok(Self { raw: downscale_to_storage(compute_halve_direct(sum))? })
}
pub fn try_tanh(self) -> Result<Self, OverflowDetected> {
let c = upscale_to_compute(self.raw);
let e2x = direct_exp(compute_add_direct(c, c));
let one = compute_one();
let den = match compute_checked_add(e2x, one) {
Ok(v) => v,
Err(_) => return Ok(Self::one()),
};
let num = compute_sub_direct(e2x, one);
Ok(Self { raw: downscale_to_storage(compute_divide_direct(num, den))? })
}
pub fn try_asinh(self) -> Result<Self, OverflowDetected> {
let c = upscale_to_compute(self.raw);
let x2 = compute_mul_direct(c, c);
let inner = direct_sqrt(compute_add_direct(x2, compute_one()));
Ok(Self { raw: downscale_to_storage(direct_ln(compute_add_direct(c, inner)))? })
}
pub fn try_acosh(self) -> Result<Self, OverflowDetected> {
if self < Self::one() { return Err(OverflowDetected::DomainError); }
let c = upscale_to_compute(self.raw);
let x2 = compute_mul_direct(c, c);
let inner = direct_sqrt(compute_sub_direct(x2, compute_one()));
Ok(Self { raw: downscale_to_storage(direct_ln(compute_add_direct(c, inner)))? })
}
pub fn try_atanh(self) -> Result<Self, OverflowDetected> {
let one = Self::one();
if self >= one || self <= Self::ZERO - one { return Err(OverflowDetected::DomainError); }
let c = upscale_to_compute(self.raw);
let one_c = compute_one();
let ratio = compute_divide_direct(compute_add_direct(one_c, c), compute_sub_direct(one_c, c));
Ok(Self { raw: downscale_to_storage(compute_halve_direct(direct_ln(ratio)))? })
}
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 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 truncated_raw(mantissa: u64, shift: i32, negative: bool) -> Result<BinaryStorage, OverflowDetected> {
#[cfg(any(table_format = "q16_16", table_format = "q32_32", table_format = "q64_64"))]
{
#[cfg(table_format = "q16_16")]
const WIDTH: u32 = 32;
#[cfg(table_format = "q32_32")]
const WIDTH: u32 = 64;
#[cfg(table_format = "q64_64")]
const WIDTH: u32 = 128;
let magnitude: u128 = if shift >= 0 {
if 64 - mantissa.leading_zeros() as i32 + shift > 128 {
return Err(OverflowDetected::TierOverflow);
}
(mantissa as u128) << shift
} else if shift > -128 {
(mantissa as u128) >> (-shift)
} else {
0
};
let limit = 1u128 << (WIDTH - 1);
if magnitude > limit || (magnitude == limit && !negative) {
return Err(OverflowDetected::TierOverflow);
}
let value = if negative { (magnitude as i128).wrapping_neg() } else { magnitude as i128 };
Ok(value as BinaryStorage)
}
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
{
#[cfg(table_format = "q128_128")]
let (width, one, zero) = (256i32, I256::from_i128(1), I256::zero());
#[cfg(table_format = "q256_256")]
let (width, one, zero) = (512i32, I512::from_i128(1), I512::zero());
let magnitude = if shift < 0 {
if shift <= -64 { zero } else { from_u64(mantissa >> (-shift)) }
} else {
let length = 64 - mantissa.leading_zeros() as i32 + shift;
if length < width {
from_u64(mantissa) << (shift as usize)
} else if length == width && negative && mantissa.is_power_of_two() {
return Ok(one << ((width - 1) as usize));
} else {
return Err(OverflowDetected::TierOverflow);
}
};
Ok(if negative { -magnitude } else { magnitude })
}
}
}
#[cfg(table_format = "q128_128")]
#[inline]
fn from_u64(v: u64) -> I256 {
I256::from_i128(v as i128)
}
#[cfg(table_format = "q256_256")]
#[inline]
fn from_u64(v: u64) -> I512 {
I512::from_i128(v as i128)
}
fn ieee_bits(negative: bool, magnitude: &[u64], frac_bits: i32, fraction_bits: u32, exponent_bits: u32) -> u64 {
let length = word_bit_length(magnitude);
if length == 0 {
return 0;
}
let sign = (negative as u64) << (fraction_bits + exponent_bits);
let bias = (1i32 << (exponent_bits - 1)) - 1;
let top = length as i32 - 1 - frac_bits; let lowest = (top - fraction_bits as i32).max(1 - bias - fraction_bits as i32);
let shift = lowest + frac_bits; let mut significand = if shift <= 0 {
magnitude[0] << (-shift) as u32
} else {
let shift = shift as u32;
let kept = word_bits_from(magnitude, shift);
let round = word_bit(magnitude, shift - 1);
let sticky = word_any_below(magnitude, shift - 1);
kept + (round && (sticky || kept & 1 == 1)) as u64
};
let mut lowest = lowest;
if significand >> (fraction_bits + 1) != 0 {
significand >>= 1;
lowest += 1;
}
if significand == 0 {
return sign;
}
if significand >> fraction_bits == 0 {
return sign | significand; }
let biased = lowest + fraction_bits as i32 + bias;
let infinite = (1i32 << exponent_bits) - 1;
if biased >= infinite {
return sign | ((infinite as u64) << fraction_bits);
}
sign | ((biased as u64) << fraction_bits) | (significand & ((1u64 << fraction_bits) - 1))
}
fn word_bit_length(words: &[u64]) -> u32 {
for i in (0..words.len()).rev() {
if words[i] != 0 {
return i as u32 * 64 + 64 - words[i].leading_zeros();
}
}
0
}
fn word_bit(words: &[u64], i: u32) -> bool {
words.get((i / 64) as usize).map_or(false, |w| (w >> (i % 64)) & 1 == 1)
}
fn word_any_below(words: &[u64], i: u32) -> bool {
let (w, b) = ((i / 64) as usize, i % 64);
words.iter().take(w.min(words.len())).any(|&x| x != 0)
|| (b > 0 && words.get(w).map_or(false, |&x| x & ((1u64 << b) - 1) != 0))
}
fn word_bits_from(words: &[u64], i: u32) -> u64 {
let (w, b) = ((i / 64) as usize, i % 64);
let low = words.get(w).map_or(0, |&x| x >> b);
let high = if b == 0 { 0 } else { words.get(w + 1).map_or(0, |&x| x << (64 - b)) };
low | high
}
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 {
self.try_add(rhs).expect("FixedPoint: addition overflow")
}
}
impl Sub for FixedPoint {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
self.try_sub(rhs).expect("FixedPoint: subtraction overflow")
}
}
impl Mul for FixedPoint {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
self.try_mul(rhs).expect("FixedPoint: multiplication overflow")
}
}
impl Div for FixedPoint {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
match self.try_div(rhs) {
Ok(v) => v,
Err(OverflowDetected::DivisionByZero) => panic!("FixedPoint: division by zero"),
Err(_) => panic!("FixedPoint: division overflow"),
}
}
}
impl Neg for FixedPoint {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self.try_neg().expect("FixedPoint: negation overflow")
}
}
impl FixedPoint {
#[inline]
pub fn try_add(self, rhs: Self) -> Result<Self, OverflowDetected> {
self.raw.checked_add(rhs.raw).map(|raw| Self { raw }).ok_or(OverflowDetected::TierOverflow)
}
#[inline]
pub fn try_sub(self, rhs: Self) -> Result<Self, OverflowDetected> {
self.raw.checked_sub(rhs.raw).map(|raw| Self { raw }).ok_or(OverflowDetected::TierOverflow)
}
#[inline]
pub fn try_neg(self) -> Result<Self, OverflowDetected> {
self.raw.checked_neg().map(|raw| Self { raw }).ok_or(OverflowDetected::TierOverflow)
}
#[inline]
pub fn try_mul(self, rhs: Self) -> Result<Self, OverflowDetected> {
try_fixed_multiply(self.raw, rhs.raw).map(|raw| Self { raw })
}
#[inline]
pub fn try_div(self, rhs: Self) -> Result<Self, OverflowDetected> {
try_fixed_divide(self.raw, rhs.raw).map(|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 try_fixed_multiply(a: BinaryStorage, b: BinaryStorage) -> Result<BinaryStorage, OverflowDetected> {
let overflow = OverflowDetected::TierOverflow;
#[cfg(table_format = "q16_16")]
{
let wide = (a as i64) * (b as i64);
let round_bit = ((wide >> (FRAC_BITS - 1)) & 1) as i32;
let q = wide >> FRAC_BITS;
if q as i32 as i64 != q { return Err(overflow); }
(q as i32).checked_add(round_bit).ok_or(overflow)
}
#[cfg(table_format = "q32_32")]
{
let wide = (a as i128) * (b as i128);
let round_bit = ((wide >> 31) & 1) as i64;
let q = wide >> 32;
if q as i64 as i128 != q { return Err(overflow); }
(q as i64).checked_add(round_bit).ok_or(overflow)
}
#[cfg(table_format = "q64_64")]
{
let product = crate::fixed_point::i256::mul_i128_to_i256(a, b);
let mut scaled = product >> 64u32;
if product.words[0] >= 1u64 << 63 { scaled = scaled + I256::from_i128(1); }
if !scaled.fits_in_i128() { return Err(overflow); }
Ok(scaled.as_i128())
}
#[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 w = abs_a.mul_to_i512(abs_b).words;
let top = 1u64 << 63;
let bump = if result_neg { w[1] > top || (w[1] == top && w[0] != 0) } else { w[1] >= top };
if w[6] != 0 || w[7] != 0 || w[5] > top {
return Err(overflow);
}
let mut m = [w[2], w[3], w[4], w[5]];
let mut carry = bump as u64;
for x in m.iter_mut() {
let (v, c) = x.overflowing_add(carry);
*x = v;
carry = c as u64;
}
if m[3] >= top {
return if result_neg && m == [0, 0, 0, top] { Ok(I256::min_value()) } else { Err(overflow) };
}
let mag = I256 { words: m };
Ok(if result_neg { -mag } else { mag })
}
#[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 w = abs_a.mul_to_i1024(abs_b).words;
let top = 1u64 << 63;
let bump = if result_neg {
w[3] > top || (w[3] == top && (w[0] | w[1] | w[2]) != 0)
} else {
w[3] >= top
};
if w[12..].iter().any(|&x| x != 0) || w[11] > top {
return Err(overflow);
}
let mut m = [0u64; 8];
m.copy_from_slice(&w[4..12]);
let mut carry = bump as u64;
for x in m.iter_mut() {
let (v, c) = x.overflowing_add(carry);
*x = v;
carry = c as u64;
}
if m[7] >= top {
let exact_min = m[7] == top && m[..7].iter().all(|&x| x == 0);
return if result_neg && exact_min { Ok(I512::min_value()) } else { Err(overflow) };
}
let mag = I512 { words: m };
Ok(if result_neg { -mag } else { mag })
}
}
#[inline]
fn try_fixed_divide(a: BinaryStorage, b: BinaryStorage) -> Result<BinaryStorage, OverflowDetected> {
let overflow = OverflowDetected::TierOverflow;
#[cfg(table_format = "q16_16")]
{
let num = (a as i64) << FRAC_BITS;
let den = b as i64;
if den == 0 { return Err(OverflowDetected::DivisionByZero); }
let q = num / den;
let rem2 = (num - q * den).unsigned_abs() << 1;
let dabs = den.unsigned_abs();
let positive = (num < 0) == (den < 0);
let bump = if positive { rem2 >= dabs } else { rem2 > dabs };
i32::try_from(if bump { q + if positive { 1 } else { -1 } } else { q }).map_err(|_| overflow)
}
#[cfg(table_format = "q32_32")]
{
let num = (a as i128) << 32;
let den = b as i128;
if den == 0 { return Err(OverflowDetected::DivisionByZero); }
let q = num / den;
let rem2 = (num - q * den).unsigned_abs() << 1;
let dabs = den.unsigned_abs();
let positive = (num < 0) == (den < 0);
let bump = if positive { rem2 >= dabs } else { rem2 > dabs };
i64::try_from(if bump { q + if positive { 1 } else { -1 } } else { q }).map_err(|_| overflow)
}
#[cfg(table_format = "q64_64")]
{
let num = I256::from_i128(a) << 64usize;
let den = I256::from_i128(b);
if den.is_zero() { return Err(OverflowDetected::DivisionByZero); }
let q = num / den;
let rem = num - q * den;
let rem_abs = if rem.is_negative() { -rem } else { rem };
let den_abs = if den.is_negative() { -den } else { den };
let positive = num.is_negative() == den.is_negative();
let rem2 = rem_abs + rem_abs;
let bump = if positive { rem2 >= den_abs } else { rem2 > den_abs };
let one = I256::from_i128(1);
let q = if bump { if positive { q + one } else { q - one } } else { q };
if !q.fits_in_i128() { return Err(overflow); }
Ok(q.as_i128())
}
#[cfg(table_format = "q128_128")]
{
let num = I512::from_i256(a) << 128usize;
let den = I512::from_i256(b);
if den.is_zero() { return Err(OverflowDetected::DivisionByZero); }
let q = num / den;
let rem = num - q * den;
let rem_abs = if rem.is_negative() { -rem } else { rem };
let den_abs = if den.is_negative() { -den } else { den };
let positive = num.is_negative() == den.is_negative();
let rem2 = rem_abs + rem_abs;
let bump = if positive { rem2 >= den_abs } else { rem2 > den_abs };
let one = I512::from_i128(1);
let q = if bump { if positive { q + one } else { q - one } } else { q };
if !q.fits_in_i256() { return Err(overflow); }
Ok(q.as_i256())
}
#[cfg(table_format = "q256_256")]
{
let num = I1024::from_i512(a) << 256usize;
let den = I1024::from_i512(b);
if den.is_zero() { return Err(OverflowDetected::DivisionByZero); }
let q = num / den;
let rem = num - q * den;
let rem_abs = if rem < I1024::zero() { -rem } else { rem };
let den_abs = if den < I1024::zero() { -den } else { den };
let positive = (num < I1024::zero()) == (den < I1024::zero());
let rem2 = rem_abs + rem_abs;
let bump = if positive { rem2 >= den_abs } else { rem2 > den_abs };
let one = I1024::from_i128(1);
let q = if bump { if positive { q + one } else { q - one } } else { q };
if !q.fits_in_i512() { return Err(overflow); }
Ok(q.as_i512())
}
}
#[cfg(test)]
mod literal_parser_tests {
use super::FixedPoint;
#[test]
fn exact_parser_matches_the_canonical_path() {
let int_digits = crate::fixed_point::frac_config::INTEGER_BITS as u64 * 30103 / 100000 + 2;
let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
let mut next = |bound: u64| {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
(state >> 33) % bound
};
let mut checked = 0;
for _ in 0..20_000 {
let mut s = String::new();
if next(2) == 0 { s.push('-'); }
let int_len = 1 + next(int_digits);
for _ in 0..int_len { s.push((b'0' + next(10) as u8) as char); }
let frac_len = next(46);
if frac_len > 0 || next(2) == 0 {
s.push('.');
for _ in 0..frac_len.max(1) { s.push((b'0' + next(10) as u8) as char); }
}
let exact = FixedPoint::try_from_str(&s);
let canonical = FixedPoint::try_from_str_canonical(&s);
match (&exact, &canonical) {
(Ok(a), Ok(b)) => assert_eq!(a, b, "{s}"),
(Err(_), Err(_)) => {}
_ => panic!("{s}: exact {exact:?}, canonical {canonical:?}"),
}
checked += exact.is_ok() as u32;
}
assert!(checked > 2_000, "{checked}");
}
}