Files
mandelbrot/src/bignum/rug_backend.rs
T

129 lines
3.7 KiB
Rust

//! [`Big`] over `rug::Float` (MPFR), for native builds.
use core::cmp::Ordering;
use rug::{Assign, Float};
use super::DecimalParts;
/// Arbitrary-precision binary float. See the module docs.
#[derive(Clone, Debug, PartialEq)]
pub struct Big(Float);
/// MPFR precision for `bits`, clamped to what it accepts.
fn prec(bits: usize) -> u32 {
(bits as u32).clamp(rug::float::prec_min(), rug::float::prec_max())
}
impl Big {
/// `x` at `bits` of precision (exact when `bits >= 53`). Non-finite `x`
/// reads as 0.
pub fn from_f64(x: f64, bits: usize) -> Self {
let x = if x.is_finite() { x } else { 0.0 };
Self(Float::with_val(prec(bits), x))
}
pub fn zero(bits: usize) -> Self {
Self(Float::new(prec(bits)))
}
/// Parse a decimal (`-0.75`, `1.5e-20`, any number of digits) at `bits`
/// (at least 53) of precision.
pub fn from_decimal_str(s: &str, bits: usize) -> Option<Self> {
let parsed = Float::parse(s.trim()).ok()?;
let x = Float::with_val(prec(bits.max(53)), parsed);
x.is_finite().then_some(Self(x))
}
pub fn precision(&self) -> usize {
self.0.prec() as usize
}
/// Change the precision, rounding to nearest if it shrinks.
pub fn with_precision(mut self, bits: usize) -> Self {
self.0.set_prec(prec(bits));
self
}
pub fn to_f64(&self) -> f64 {
self.0.to_f64()
}
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
pub fn is_negative(&self) -> bool {
self.0.cmp0() == Some(Ordering::Less)
}
pub fn abs(self) -> Self {
Self(self.0.abs())
}
pub fn sqr(&self) -> Self {
Self(Float::with_val(self.0.prec(), self.0.square_ref()))
}
/// `floor(log2|x|)`, or `None` for zero. Exact, at any exponent.
pub fn log2_floor(&self) -> Option<isize> {
// MPFR normalizes the significand to [0.5, 1).
self.0.get_exp().map(|e| e as isize - 1)
}
pub fn ln(&self) -> Self {
Self(Float::with_val(self.0.prec(), self.0.ln_ref()))
}
pub fn exp(&self) -> Self {
Self(Float::with_val(self.0.prec(), self.0.exp_ref()))
}
/// `atan2(self, x)`: the angle of `(x, self)`.
pub fn atan2(&self, x: &Big) -> Self {
let p = self.0.prec().max(x.0.prec());
Self(Float::with_val(p, self.0.atan2_ref(&x.0)))
}
pub fn sin_cos(&self) -> (Self, Self) {
let p = self.0.prec();
let (mut s, mut c) = (Float::new(p), Float::new(p));
(&mut s, &mut c).assign(self.0.sin_cos_ref());
(Self(s), Self(c))
}
/// `sig` significant decimal digits (rounded to nearest).
pub fn to_decimal_parts(&self, sig: usize) -> DecimalParts {
if self.0.is_zero() {
return DecimalParts::new(false, "", 0);
}
let (negative, digits, exp) = self.0.to_sign_string_exp(10, Some(sig));
DecimalParts::new(negative, &digits, exp.unwrap_or(0) as isize)
}
pub(super) fn add_ref(&self, rhs: &Big) -> Big {
let p = self.0.prec().max(rhs.0.prec());
Big(Float::with_val(p, &self.0 + &rhs.0))
}
pub(super) fn sub_ref(&self, rhs: &Big) -> Big {
let p = self.0.prec().max(rhs.0.prec());
Big(Float::with_val(p, &self.0 - &rhs.0))
}
pub(super) fn mul_ref(&self, rhs: &Big) -> Big {
let p = self.0.prec().max(rhs.0.prec());
Big(Float::with_val(p, &self.0 * &rhs.0))
}
pub(super) fn negated(self) -> Big {
Big(-self.0)
}
/// `self · 2^k`, exact. `|k|` stays far below `i32::MAX` (precision and
/// zoom depth are capped around 2^20 bits).
pub(super) fn mul_pow2(self, k: isize) -> Big {
Big(self.0 << k as i32)
}
}