use std::ops::{Add, Div, Mul, Neg, Sub};
use crate::Float;
pub fn gamma(n: i32) -> Float {
let n = n as Float;
(n * Float::EPSILON) / (1.0 - n * Float::EPSILON)
}
pub fn next_double(v: Float) -> Float {
if v.is_infinite() && v > 0.0 {
v
} else {
let v = if v == -0.0 { 0.0 } else { v };
let bits = if v >= 0.0 {
v.to_bits() + 1
} else {
v.to_bits() - 1
};
Float::from_bits(bits)
}
}
pub fn previous_double(v: Float) -> Float {
if v.is_infinite() && v < 0.0 {
v
} else {
let v = if v == 0.0 { -0.0 } else { v };
let bits = if v > 0.0 {
v.to_bits() - 1
} else {
v.to_bits() + 1
};
Float::from_bits(bits)
}
}
#[derive(Copy, Clone)]
pub struct EFloat {
pub value: Float,
pub low: Float,
pub high: Float,
}
impl EFloat {
fn new(value: Float, low: Float, high: Float) -> Self {
Self {
value,
low,
high,
}
}
pub fn sqrt(&self) -> Self {
Self::new(
self.value.sqrt(),
previous_double(self.low.sqrt()),
next_double(self.high.sqrt()),
)
}
pub fn quadratic(a: Self, b: Self, c: Self) -> Option<(Self, Self)> {
let disc = b.value * b.value - 4.0 * a.value * c.value;
if disc < 0.0 {
return None;
}
let disc_root = Self::from(disc).sqrt();
let mut t0 = (-b - disc_root) / (Self::from(2.0) * a);
let mut t1 = (-b + disc_root) / (Self::from(2.0) * a);
if t0.value > t1.value {
std::mem::swap(&mut t0, &mut t1);
}
Some((t0, t1))
}
pub fn abs_error(&self) -> Float {
next_double(
(self.high - self.value).abs().max((self.value - self.low).abs())
)
}
}
impl From<Float> for EFloat {
fn from(value: Float) -> Self {
Self::new(
value,
value,
value,
)
}
}
impl Neg for EFloat {
type Output = Self;
fn neg(self) -> Self {
Self::new(
-self.value,
-self.low,
-self.high,
)
}
}
impl Add for EFloat {
type Output = Self;
fn add(self, other: Self) -> Self {
Self::new(
self.value + other.value,
previous_double(self.low + other.low),
next_double(self.high + other.high),
)
}
}
impl Sub for EFloat {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::new(
self.value - other.value,
previous_double(self.low - other.high),
next_double(self.high - other.low),
)
}
}
impl Mul for EFloat {
type Output = Self;
fn mul(self, other: Self) -> Self {
let prod_bounds = [
self.low * other.low,
self.low * other.high,
self.high * other.low,
self.high * other.high,
];
let min = prod_bounds[0]
.min(prod_bounds[1])
.min(prod_bounds[2])
.min(prod_bounds[3]);
let max = prod_bounds[0]
.max(prod_bounds[1])
.max(prod_bounds[2])
.max(prod_bounds[3]);
Self::new(
self.value * other.value,
previous_double(min),
next_double(max),
)
}
}
impl Div for EFloat {
type Output = Self;
fn div(self, other: Self) -> Self {
if other.low < 0.0 && other.high > 0.0 {
Self::new(
self.value / other.value,
crate::NEG_INF,
crate::INF,
)
} else {
let div_bounds = [
self.low / other.low,
self.low / other.high,
self.high / other.low,
self.high / other.high,
];
let min = div_bounds[0]
.min(div_bounds[1])
.min(div_bounds[2])
.min(div_bounds[3]);
let max = div_bounds[0]
.max(div_bounds[1])
.max(div_bounds[2])
.max(div_bounds[3]);
Self::new(
self.value / other.value,
previous_double(min),
next_double(max),
)
}
}
}