1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
use crate::{
repr::{Context, Repr, Word},
round::{mode, Round},
};
use dashu_base::Sign;
use dashu_int::{DoubleWord, IBig};
/// An arbitrary precision floating point number with arbitrary base and rounding mode.
///
/// The float number consists of a [Repr] and a [Context]. The [Repr] instance determines
/// the value of the number, and the [Context] contains runtime information (such as precision
/// limit, rounding mode, etc.)
///
/// For how the number is represented, see [Repr], for how the precision limit and rounding
/// mode is applied, see [Context].
///
/// The arithmetic operations on [FBig] follows the behavior of its associated context.
/// If a different precision limit and/or rounding mode is required, or the rounding
/// information has to be preserved, use the methods of the [Context] type.
///
/// # Generic Parameters
///
/// The const generic parameters will be abbreviated as `BASE` -> `B`, `RoundingMode` -> `R`.
/// THe `BASE` must be in range \[2, isize::MAX\], and the `RoundingMode` can be chosen from
/// the [mode] module.
///
/// With the default generic parameters, the floating number is of base 2 rounded towards zero.
/// This is the most efficient format for operations. To represent a decimal number, the alias
/// [DBig][crate::DBig] is provided, which is base 10 rounded to the nearest.
///
/// # Parsing and printing
///
/// To create a [FBig] instance, there are four ways:
/// 1. Use predifined constants (e.g. [FBig::ZERO], [FBig::ONE], [FBig::NEG_INFINITY]).
/// 1. Use the literal macro `fbig!` or `dbig!` defined in the [`dashu-macro`](https://docs.rs/dashu-macros/latest/dashu_macros/) crate.
/// 1. Construct from the significand and exponent using [from_parts()][FBig::from_parts] or [from_parts_const()][FBig::from_parts_const].
/// 1. Parse from a string.
///
/// Conversion from and to [str] is limited to native radix (i.e. base). To print or parse
/// with different radix, please use [to_binary()][FBig::to_binary], [to_decimal()][FBig::to_decimal]
/// or [with_base()][FBig::with_base], [with_base_and_precision()][FBig::with_base_and_precision] to convert.
///
/// For printing, currently only the [Display][core::fmt::Display] and [Debug][core::fmt::Debug] are supported.
/// Other formatting traits will be supported in future.
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// // parsing
/// let a = DBig::from_parts(123456789.into(), -5);
/// let b = DBig::from_str_native("1234.56789")?;
/// let c = DBig::from_str_native("1.23456789e3")?;
/// assert_eq!(a, b);
/// assert_eq!(b, c);
///
/// // printing
/// assert_eq!(format!("{}", DBig::from_str_native("12.34")?), "12.34");
/// let x = DBig::from_str_native("10.01")?
/// .with_precision(0) // use unlimited precision
/// .value();
/// if dashu_int::Word::BITS == 64 {
/// // number of digits to display depends on the word size
/// assert_eq!(
/// format!("{:?}", x.powi(100.into())),
/// "1105115697720767968..1441386704950100001 * 10 ^ -200 (prec: 0, rnd: HalfAway)"
/// );
/// }
/// # Ok::<(), ParseError>(())
/// ```
///
/// For detailed information of parsing, refer to the [from_str_native()][FBig::from_str_native] method.
///
/// # Binary operations
///
/// Binary operations on [FBig] instances are restricted to the same base and same rounding mode. This is
/// designed to make sure that no hidden conversion is performed during the operations. However, for equality
/// test and comparsion, two [FBig] instances can have different rounding modes (but not different bases),
/// because rounding will never happends during comparison.
///
/// # Convert from/to `f32`/`f64`
///
/// The conversion between [FBig] and [f32]/[f64] is only defined for base 2 [FBig]. To convert
/// from/to other bases, please first convert to base 2, and then change the base using [with_base()][FBig::with_base]
/// or [with_base_and_precision()][FBig::with_base_and_precision].
///
/// Converting from [f32]/[f64] (using [TryFrom][core::convert::TryFrom]) is lossless, except for
/// that `NAN` values will result in an [Err]. Converting to [f32]/[f64] (using [to_f32()][FBig::to_f32]
/// and [to_f64()][FBig::to_f64]) is lossy, and the rounding direction is contained in the result of these
/// two methods.
///
/// The infinities are converted as it is, and the subnormals are converted using its actual values.
///
pub struct FBig<RoundingMode: Round = mode::Zero, const BASE: Word = 2> {
pub(crate) repr: Repr<BASE>,
pub(crate) context: Context<RoundingMode>,
}
impl<R: Round, const B: Word> FBig<R, B> {
/// Create a [FBig] instance from raw parts, internal use only
#[inline]
pub(crate) const fn new(repr: Repr<B>, context: Context<R>) -> Self {
Self { repr, context }
}
/// Create a [FBig] instance from [Repr] and [Context].
///
/// This method should not be used in most cases. It's designed to be used when
/// you hold a [Repr] instance and want to create an [FBig] from that.
///
/// # Examples
///
/// ```
/// # use dashu_float::DBig;
/// use dashu_float::{Repr, Context};
///
/// assert_eq!(DBig::from_repr(Repr::one(), Context::new(1)), DBig::ONE);
/// assert_eq!(DBig::from_repr(Repr::infinity(), Context::new(1)), DBig::INFINITY);
/// ```
///
/// # Panics
///
/// Panics if the [Repr] has more digits than the precision limit specified in the context.
/// Note that this condition is not checked in release build.
#[inline]
pub fn from_repr(repr: Repr<B>, context: Context<R>) -> Self {
debug_assert!(
repr.is_infinite() || !context.is_limited() || repr.digits() <= context.precision
);
Self { repr, context }
}
const fn zero() -> Self {
Self::new(Repr::zero(), Context::new(0))
}
/// [FBig] with value 0 and unlimited precision
///
/// To test if the float number is zero, use `self.repr().is_zero()`.
pub const ZERO: Self = Self::zero();
const fn one() -> Self {
Self::new(Repr::one(), Context::new(0))
}
/// [FBig] with value 1 and unlimited precision
///
/// To test if the float number is one, use `self.repr().one()`.
pub const ONE: Self = Self::one();
const fn neg_one() -> Self {
Self::new(Repr::neg_one(), Context::new(0))
}
/// [FBig] with value -1 and unlimited precision
pub const NEG_ONE: Self = Self::neg_one();
const fn inf() -> Self {
Self::new(Repr::infinity(), Context::new(0))
}
/// [FBig] instance representing the positive infinity (+∞)
///
/// To test if the float number is infinite, use `self.repr().infinite()`.
pub const INFINITY: Self = Self::inf();
const fn neg_inf() -> Self {
Self::new(Repr::neg_infinity(), Context::new(0))
}
/// [FBig] instance representing the negative infinity (-∞)
///
/// To test if the float number is infinite, use `self.repr().infinite()`.
pub const NEG_INFINITY: Self = Self::neg_inf();
/// Get the maximum precision set for the float number.
///
/// It's equivalent to `self.context().precision()`.
///
/// # Examples
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// use dashu_float::Repr;
///
/// let a = DBig::from_str_native("1.234")?;
/// assert!(a.repr().significand() <= &Repr::<10>::BASE.pow(a.precision()));
/// # Ok::<(), ParseError>(())
/// ```
#[inline]
pub const fn precision(&self) -> usize {
self.context.precision
}
/// Get the number of the significant digits in the float number
///
/// It's equivalent to `self.repr().digits()`.
///
/// This value is also the actual precision needed for the float number. Shrink to this
/// value using [with_precision()][FBig::with_precision] will not cause loss of float precision.
///
/// # Examples
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// use dashu_base::Approximation::*;
///
/// let a = DBig::from_str_native("-1.234e-3")?;
/// assert_eq!(a.digits(), 4);
/// assert!(matches!(a.clone().with_precision(4), Exact(_)));
/// assert!(matches!(a.clone().with_precision(3), Inexact(_, _)));
/// # Ok::<(), ParseError>(())
/// ```
#[inline]
pub fn digits(&self) -> usize {
self.repr.digits()
}
/// Get the context associated with the float number
#[inline]
pub const fn context(&self) -> Context<R> {
self.context
}
/// Get a reference to the underlying numeric representation
#[inline]
pub const fn repr(&self) -> &Repr<B> {
&self.repr
}
/// Get the underlying numeric representation
///
/// # Examples
///
/// ```
/// # use dashu_float::DBig;
/// use dashu_float::Repr;
///
/// let a = DBig::ONE;
/// assert_eq!(a.into_repr(), Repr::<10>::one());
/// ```
#[inline]
pub fn into_repr(self) -> Repr<B> {
self.repr
}
/// Convert raw parts (significand, exponent) into a float number.
///
/// The precision will be inferred from significand (the lowest k such that `significand <= base^k`)
///
/// # Examples
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// let a = DBig::from_parts((-1234).into(), -2);
/// assert_eq!(a, DBig::from_str_native("-12.34")?);
/// assert_eq!(a.precision(), 4); // 1234 has 4 (decimal) digits
/// # Ok::<(), ParseError>(())
/// ```
#[inline]
pub fn from_parts(significand: IBig, exponent: isize) -> Self {
let repr = Repr::new(significand, exponent);
let precision = repr.digits().max(1); // set precision to 1 if signficand is zero
let context = Context::new(precision);
Self::new(repr, context)
}
/// Convert raw parts (significand, exponent) into a float number in a `const` context.
///
/// It requires that the significand fits in a [DoubleWord].
///
/// The precision will be inferred from significand (the lowest k such that `significand <= base^k`).
/// If the `min_precision` is provided, then the higher one from the given and inferred precision
/// will be used as the final precision.
///
/// # Examples
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// use dashu_base::Sign;
///
/// const A: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, None);
/// assert_eq!(A, DBig::from_str_native("-12.34")?);
/// assert_eq!(A.precision(), 4); // 1234 has 4 (decimal) digits
///
/// const B: DBig = DBig::from_parts_const(Sign::Negative, 1234, -2, Some(5));
/// assert_eq!(B.precision(), 5); // overrided by the argument
/// # Ok::<(), ParseError>(())
/// ```
#[inline]
pub const fn from_parts_const(
sign: Sign,
mut significand: DoubleWord,
mut exponent: isize,
min_precision: Option<usize>,
) -> Self {
if significand == 0 {
return Self::ZERO;
}
let mut digits = 0;
// normalize
if B.is_power_of_two() {
let base_bits = B.trailing_zeros();
let shift = significand.trailing_zeros() / base_bits;
significand >>= shift * base_bits;
exponent += shift as isize;
digits = ((DoubleWord::BITS - significand.leading_zeros() + base_bits - 1) / base_bits)
as usize;
} else {
let mut pow: DoubleWord = 1;
while significand % (B as DoubleWord) == 0 {
significand /= B as DoubleWord;
exponent += 1;
}
while let Some(next) = pow.checked_mul(B as DoubleWord) {
digits += 1;
if next > significand {
break;
}
pow = next;
}
}
let repr = Repr {
significand: IBig::from_parts_const(sign, significand),
exponent,
};
let precision = match min_precision {
Some(prec) => {
if prec > digits {
prec
} else {
digits
}
}
None => digits,
};
Self::new(repr, Context::new(precision))
}
/// Return the value of the least significant digit of the float number x,
/// such that `x + ulp` is the first float number greater than x (given the precision from the context).
///
/// # Examples
///
/// ```
/// # use dashu_int::error::ParseError;
/// # use dashu_float::DBig;
/// assert_eq!(DBig::from_str_native("1.23")?.ulp(), DBig::from_str_native("0.01")?);
/// assert_eq!(DBig::from_str_native("01.23")?.ulp(), DBig::from_str_native("0.001")?);
/// # Ok::<(), ParseError>(())
/// ```
#[inline]
pub fn ulp(&self) -> Self {
if self.repr.is_infinite() {
return self.clone();
}
let repr = Repr {
significand: IBig::ONE,
exponent: self.repr.exponent + self.repr.digits() as isize
- self.context.precision as isize,
};
Self::new(repr, self.context)
}
}
// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
impl<R: Round, const B: Word> Clone for FBig<R, B> {
#[inline]
fn clone(&self) -> Self {
Self {
repr: self.repr.clone(),
context: self.context,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
self.repr.clone_from(&source.repr);
self.context = source.context;
}
}
impl<R: Round, const B: Word> Default for FBig<R, B> {
/// Default value: 0.
#[inline]
fn default() -> Self {
Self::ZERO
}
}