├── .github └── workflows │ ├── 1-rustfmt.yml │ └── 2-tests.yml ├── .gitignore ├── .rusty-hook.toml ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── RELEASE_NOTES.md ├── astro-float-macro ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── util.rs ├── astro-float-num ├── Cargo.toml ├── README.md ├── src │ ├── common │ │ ├── buf.rs │ │ ├── consts.rs │ │ ├── int.rs │ │ ├── mod.rs │ │ └── util.rs │ ├── conv.rs │ ├── ctx.rs │ ├── defs.rs │ ├── ext.rs │ ├── for_3rd │ │ ├── de.rs │ │ ├── mod.rs │ │ └── ser.rs │ ├── lib.rs │ ├── macro_util.rs │ ├── mantissa │ │ ├── cbrt.rs │ │ ├── conv.rs │ │ ├── div.rs │ │ ├── fft.rs │ │ ├── mantissa.rs │ │ ├── mod.rs │ │ ├── mul.rs │ │ ├── sqrt.rs │ │ ├── toom2.rs │ │ ├── toom3.rs │ │ └── util.rs │ ├── num.rs │ ├── ops │ │ ├── acos.rs │ │ ├── acosh.rs │ │ ├── asin.rs │ │ ├── asinh.rs │ │ ├── atan.rs │ │ ├── atanh.rs │ │ ├── cbrt.rs │ │ ├── consts │ │ │ ├── e.rs │ │ │ ├── ln10.rs │ │ │ ├── ln2.rs │ │ │ ├── mod.rs │ │ │ └── pi.rs │ │ ├── cos.rs │ │ ├── cosh.rs │ │ ├── log.rs │ │ ├── mod.rs │ │ ├── pow.rs │ │ ├── series.rs │ │ ├── sin.rs │ │ ├── sinh.rs │ │ ├── sqrt.rs │ │ ├── tan.rs │ │ ├── tanh.rs │ │ ├── tests.rs │ │ └── util.rs │ ├── parser.rs │ └── strop.rs └── tests │ ├── README.md │ ├── integration_tests.rs │ └── mpfr │ ├── common.rs │ ├── compare_const_test.rs │ ├── compare_ops_test.rs │ ├── compare_special_test.rs │ └── mod.rs ├── doc └── README.md ├── rustfmt.toml ├── src └── lib.rs └── tests ├── mod.rs └── tests └── expr.rs /.github/workflows/1-rustfmt.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: 7 | branches: [ main, develop ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | check-formatting: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Check formatting 20 | run: cargo fmt --all --check -------------------------------------------------------------------------------- /.github/workflows/2-tests.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" , "develop" ] 6 | pull_request: 7 | branches: [ "main", "develop" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build-and-test-x64: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Run tests 20 | run: cargo test --workspace --release --verbose 21 | - name: Run no_std tests 22 | run: cargo test --workspace --release --verbose --no-default-features --features random,serde 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | /target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.rusty-hook.toml: -------------------------------------------------------------------------------- 1 | [hooks] 2 | pre-push = "cargo fmt --all --check" 3 | 4 | [logging] 5 | verbose = true 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | The following is information than may be useful for contributors. The information is advisory in nature. The only mandatory requirement is a genuine interest and desire to participate from the contributor. 2 | 3 | All communication is held in [issues](https://github.com/stencillogic/astro-float/issues) and [PRs](https://github.com/stencillogic/astro-float/pulls). 4 | 5 | There are several ways to contribute to the project. The list includes, but is not limited by: 6 | 7 | - opening issues, participating in discussions 8 | - implementing features, or fixing bugs which are defined by current open issues, opening pull requests 9 | - writing documentation, or articles about the library 10 | - developing and improving algorithms used in the library 11 | 12 | Rust is the primary programming language of the library. The following are reccomendations to follow with regard to the coding standards: 13 | 14 | - use of unsafe code must be kept at a minimum; any use of unsafe must be justified. 15 | - use of `unwrap()`, `expect()`, any unhandled errors are only acceptable when it was proved that they will never appear (i.e. there are checks that guarantee the code will never panic or return error). 16 | - new code must be covered by regression tests. 17 | - code must be formatted with [rustfmt](https://rust-lang.github.io/rustfmt/). 18 | - all public items (functions, structs, etc.) must be documented. 19 | - any pull request must pass all tests before it can be merged. 20 | - public api should follow [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/). 21 | - library's documentation should be kept up to date. 22 | - common sense and proper reasoning supersede any written rule. 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "astro-float" 3 | version = "0.9.5" 4 | edition = "2021" 5 | authors = ["stencillogic "] 6 | license = "MIT" 7 | description = "Multiple precision floating-point numbers library." 8 | categories = ["algorithms", "data-structures", "science", "no-std"] 9 | keywords = ["bigfloat", "numeric", "mathematics", "bignum"] 10 | readme = "README.md" 11 | repository = "https://github.com/stencillogic/astro-float" 12 | 13 | [dependencies] 14 | astro-float-num = { version = "0.3.6", default-features = false } 15 | astro-float-macro = "0.4.5" 16 | 17 | [features] 18 | default = ["std", "random", "serde"] 19 | std = ["astro-float-num/std"] 20 | random = ["astro-float-num/random"] 21 | serde = ["astro-float-num/serde"] 22 | 23 | [dev-dependencies] 24 | trybuild = "1.0" 25 | rusty-hook = "0.11.2" 26 | 27 | [workspace] 28 | members = [ 29 | "astro-float-num", 30 | "astro-float-macro", 31 | ] 32 | 33 | [profile.release] 34 | opt-level = 3 35 | lto = true 36 | codegen-units = 1 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 stencillogic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Rust](https://github.com/stencillogic/astro-float/workflows/Rust/badge.svg) 2 | ![Minimum rustc version](https://img.shields.io/badge/rustc-1.62.1+-blue.svg) 3 | ![Test coverage](https://img.shields.io/badge/coverage-87.05%25-blue.svg) 4 | 5 | Astro-float (astronomically large floating-point numbers) is an arbitrary precision floating-point numbers library designed for performance, portability, and implemented purely in Rust. 6 | 7 | The library implements the basic operations and functions. It uses classical algorithms such as Karatsuba, Toom-Cook, Schönhage-Strassen algorithm, and others. 8 | 9 | The library can work without the standard library provided there is a memory allocator. 10 | 11 | ## What's new 12 | 13 | Information about the latest changes is available in [Release notes](https://github.com/stencillogic/astro-float/blob/main/RELEASE_NOTES.md) 14 | 15 | ## Usage 16 | 17 | Below is an example of using the library. 18 | For more information please refer to the library documentation: https://docs.rs/astro-float/latest/astro_float/ 19 | 20 | 21 | Calculate Pi with 1024 bit precision rounded to the nearest even number. 22 | 23 | ``` rust 24 | use astro_float::Consts; 25 | use astro_float::RoundingMode; 26 | use astro_float::ctx::Context; 27 | use astro_float::expr; 28 | 29 | // Create a context with precision 1024, and rounding to even. 30 | let mut ctx = Context::new(1024, RoundingMode::ToEven, 31 | Consts::new().expect("Constants cache initialized"), 32 | -10000, 10000); 33 | 34 | // Compute pi: pi = 6*arctan(1/sqrt(3)) 35 | let pi = expr!(6 * atan(1 / sqrt(3)), &mut ctx); 36 | 37 | // Use library's constant value for verifying the result. 38 | let pi_lib = ctx.const_pi(); 39 | 40 | // Compare computed constant with library's constant 41 | assert_eq!(pi.cmp(&pi_lib), Some(0)); 42 | ``` 43 | 44 | ## Performance 45 | 46 | Benchmark can be found here: https://github.com/stencillogic/bigfloat-bench. 47 | 48 | ## Contributing 49 | 50 | Issues regarding bugs or new features can be opened here: https://github.com/stencillogic/astro-float/issues 51 | 52 | For more information please check [CONTRIBUTING.md](https://github.com/stencillogic/astro-float/blob/main/CONTRIBUTING.md) -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | **0.9.0** 4 | 5 | - Error compensation in the `expr` macro has been reworked. 6 | - Exponent range is introduced in the context. 7 | - Stability improvements. 8 | 9 | **0.8.0** 10 | 11 | - Scalable conversion to decimal and from decimal base implemented: conversion and formatting functions now require constants cache. 12 | - Implementation of traits `FromStr` and `Display` and `serde` feature are now not available in no_std. 13 | - Mechanism of correct rounding has been removed from the `expr!` macro to increase its stablility. 14 | - Macro `expr!` now supports constants `pi`, `e`, `ln_2`, and `ln_10` in expressions. 15 | - New public functions `BigFloat::nan()`, `BigFloat::format()`, new constant `EXPONENT_BIT_SIZE`. 16 | - Bug fixes and code refinements. 17 | - Improved portability and stability. 18 | 19 | **0.7.0** 20 | 21 | - Improved integration tests. 22 | - Bug fixes and code refinement. 23 | - Improved portability. 24 | - Test coverage badge added. 25 | - Smallvec replaced with Vec. 26 | - Unsafe code revisited. 27 | 28 | **0.6.0** 29 | 30 | - `expr!` macro implemented. 31 | - BigFloat stores information about its inexactness. 32 | - `FromExt` conversion trait added for BigFloat. 33 | 34 | **0.5.0** 35 | 36 | - Release notes introduction. 37 | - Correct rounding of all arithmetic operations, math functions, and constant values. 38 | - Api compliance with https://rust-lang.github.io/api-guidelines/ 39 | -------------------------------------------------------------------------------- /astro-float-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "astro-float-macro" 3 | version = "0.4.5" 4 | edition = "2021" 5 | authors = ["stencillogic "] 6 | license = "MIT" 7 | description = "Macros for multiple precision floating point numbers library astro-float." 8 | categories = ["algorithms", "data-structures", "science"] 9 | keywords = ["bigfloat", "numeric", "mathematics", "bignum"] 10 | repository = "https://github.com/stencillogic/astro-float" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | quote = { version = "1", default-features = false } 17 | syn = { version = "1", default-features = false, features = ["parsing", "proc-macro", "full", "extra-traits", "printing"] } 18 | astro-float-num = { version = "0.3.6", default-features = false } 19 | proc-macro2 = { version = "1", default-features = false } -------------------------------------------------------------------------------- /astro-float-macro/README.md: -------------------------------------------------------------------------------- 1 | ![Rust](https://github.com/stencillogic/astro-float/workflows/Rust/badge.svg) 2 | ![Minimum rustc version](https://img.shields.io/badge/rustc-1.62.1+-lightgray.svg) 3 | 4 | Procedural macros for [astro-float](https://github.com/stencillogic/astro-float). 5 | -------------------------------------------------------------------------------- /astro-float-macro/src/util.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions. 2 | 3 | use astro_float_num::BigFloat; 4 | use astro_float_num::Consts; 5 | use astro_float_num::Radix; 6 | use astro_float_num::RoundingMode; 7 | use proc_macro2::Span; 8 | use proc_macro2::TokenStream; 9 | use quote::quote; 10 | use syn::spanned::Spanned; 11 | use syn::Error; 12 | use syn::ExprCall; 13 | 14 | pub fn str_to_bigfloat_expr(s: &str, span: Span, cc: &mut Consts) -> Result { 15 | let f = BigFloat::parse(s, Radix::Dec, usize::MAX, RoundingMode::ToEven, cc); 16 | if let Some(err) = f.err() { 17 | return Err(Error::new( 18 | span, 19 | format!("failed to parse BigFloat from {}: {}", s, err), 20 | )); 21 | } 22 | 23 | let q = if f.inexact() { 24 | quote!(astro_float::macro_util::check_exponent_range(astro_float::BigFloat::parse(#s, astro_float::Radix::Dec, p_wrk, astro_float::RoundingMode::ToEven, cc), emin, emax)) 25 | } else if let Some((m, n, s, e, inexact)) = f.as_raw_parts() { 26 | let stoken = if s.is_positive() { 27 | quote!(astro_float::Sign::Pos) 28 | } else { 29 | quote!(astro_float::Sign::Neg) 30 | }; 31 | quote!(astro_float::macro_util::check_exponent_range(astro_float::BigFloat::from_raw_parts(&[#(#m),*], #n, #stoken, #e, #inexact), emin, emax)) 32 | } else { 33 | quote!(astro_float::BigFloat::nan()) 34 | }; 35 | 36 | Ok(q) 37 | } 38 | 39 | pub fn check_arg_num(narg: usize, expr: &ExprCall) -> Result<(), Error> { 40 | if expr.args.len() != narg { 41 | return Err(Error::new( 42 | expr.func.span(), 43 | if narg == 1 { "expected 1 argument." } else { "expected 2 arguments." }, 44 | )); 45 | } 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /astro-float-num/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "astro-float-num" 3 | version = "0.3.6" 4 | edition = "2021" 5 | authors = ["stencillogic "] 6 | license = "MIT" 7 | description = "Multiple precision floating point numbers implemented purely in Rust." 8 | categories = ["algorithms", "data-structures", "science"] 9 | keywords = ["bigfloat", "numeric", "mathematics", "bignum"] 10 | readme = "README.md" 11 | repository = "https://github.com/stencillogic/astro-float" 12 | 13 | [dev-dependencies] 14 | rand = "0.8.5" 15 | serde_json = "1.0.89" 16 | 17 | [target.'cfg(target_arch = "x86_64")'.dev-dependencies] 18 | rug = { version = "~1.20.0", features = ["float", "rand"] } 19 | gmp-mpfr-sys = { version = "~1.6.0", features = [] } 20 | 21 | [dependencies] 22 | serde = { version = "1.0.147", optional = true } 23 | rand = { version = "0.8.5", optional = true } 24 | lazy_static = { version = "1.4.0", default-features = false, features = [] } 25 | itertools = { version = "0.10.3", default-features = false, features = [] } 26 | 27 | [features] 28 | default = ["std", "random", "serde"] 29 | std = [] 30 | random = ["dep:rand"] 31 | serde = ["dep:serde"] 32 | -------------------------------------------------------------------------------- /astro-float-num/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /astro-float-num/src/common/buf.rs: -------------------------------------------------------------------------------- 1 | //! Buffer for holding mantissa gidits. 2 | 3 | use crate::defs::Error; 4 | use crate::defs::Word; 5 | use crate::defs::WORD_BIT_SIZE; 6 | use core::ops::Deref; 7 | use core::ops::DerefMut; 8 | use core::ops::Index; 9 | use core::ops::IndexMut; 10 | use core::slice::SliceIndex; 11 | 12 | use crate::common::util::shift_slice_left; 13 | use crate::common::util::shift_slice_right; 14 | 15 | #[cfg(not(feature = "std"))] 16 | use alloc::vec::Vec; 17 | 18 | /// Buffer for holding mantissa gidits. 19 | #[derive(Debug, Hash)] 20 | pub struct WordBuf { 21 | inner: Vec, 22 | } 23 | 24 | impl WordBuf { 25 | #[inline] 26 | pub fn new(sz: usize) -> Result { 27 | let mut inner = Vec::new(); 28 | inner.try_reserve_exact(sz)?; 29 | unsafe { 30 | // values of the newely allocated words stay unitialized for performance reasons 31 | inner.set_len(sz); 32 | } 33 | Ok(WordBuf { inner }) 34 | } 35 | 36 | #[inline] 37 | pub fn fill(&mut self, d: Word) { 38 | self.inner.fill(d); 39 | } 40 | 41 | #[inline] 42 | pub fn len(&self) -> usize { 43 | self.inner.len() 44 | } 45 | 46 | /// Decrease length of the buffer to l bits. Data is shifted. 47 | pub fn trunc_to(&mut self, l: usize) { 48 | let n = (l + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE; 49 | let sz = self.len(); 50 | shift_slice_right(&mut self.inner, (sz - n) * WORD_BIT_SIZE); 51 | self.inner.truncate(n); 52 | } 53 | 54 | /// Decrease length of the buffer to l bits. Data is not moved. 55 | pub fn trunc_to_2(&mut self, l: usize) { 56 | let n = (l + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE; 57 | self.inner.truncate(n); 58 | } 59 | 60 | /// Try to exted the size to fit the precision p. Data is shifted to the left. 61 | pub fn try_extend(&mut self, p: usize) -> Result<(), Error> { 62 | let n = (p + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE; 63 | let l = self.inner.len(); 64 | self.inner.try_reserve(n - l)?; 65 | unsafe { 66 | // values of the newely allocated words stay unitialized for performance reasons 67 | self.inner.set_len(n); 68 | } 69 | shift_slice_left(&mut self.inner, (n - l) * WORD_BIT_SIZE); 70 | Ok(()) 71 | } 72 | 73 | /// Try to exted the size to fit the precision p. Fill new elements with 0. Data is not moved. 74 | pub fn try_extend_2(&mut self, p: usize) -> Result<(), Error> { 75 | let n = (p + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE; 76 | if n > self.inner.capacity() { 77 | self.inner.try_reserve(n - self.inner.capacity())?; 78 | } 79 | if n > self.inner.len() { 80 | self.inner.resize(n, 0); 81 | } 82 | Ok(()) 83 | } 84 | 85 | /// Try to extend the size to fit the precision p. Data is shifted to the left by d bits. 86 | pub fn try_extend_3(&mut self, p: usize, d: usize) -> Result<(), Error> { 87 | let n = (p + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE; 88 | let l = self.inner.len(); 89 | self.inner.try_reserve(n - l)?; 90 | unsafe { 91 | // values of the newely allocated words stay unitialized for performance reasons 92 | self.inner.set_len(n); 93 | } 94 | self.inner[l] = 0; 95 | shift_slice_left(&mut self.inner, d); 96 | Ok(()) 97 | } 98 | 99 | // Remove trailing words containing zeroes. 100 | pub fn trunc_trailing_zeroes(&mut self) { 101 | let mut n = 0; 102 | 103 | for v in self.inner.iter() { 104 | if *v == 0 { 105 | n += 1; 106 | } else { 107 | break; 108 | } 109 | } 110 | 111 | if n > 0 { 112 | let sz = self.len(); 113 | shift_slice_right(&mut self.inner, n * WORD_BIT_SIZE); 114 | self.inner.truncate(sz - n); 115 | } 116 | } 117 | 118 | // Remove leading words containing zeroes. 119 | pub fn trunc_leading_zeroes(&mut self) { 120 | let mut n = 0; 121 | 122 | for v in self.inner.iter().rev() { 123 | if *v == 0 { 124 | n += 1; 125 | } else { 126 | break; 127 | } 128 | } 129 | 130 | if n > 0 { 131 | let sz = self.len(); 132 | self.inner.truncate(sz - n); 133 | } 134 | } 135 | } 136 | 137 | impl> IndexMut for WordBuf { 138 | #[inline] 139 | fn index_mut(&mut self, index: I) -> &mut Self::Output { 140 | self.inner.index_mut(index) 141 | } 142 | } 143 | 144 | impl> Index for WordBuf { 145 | type Output = I::Output; 146 | 147 | #[inline] 148 | fn index(&self, index: I) -> &Self::Output { 149 | self.inner.index(index) 150 | } 151 | } 152 | 153 | impl Deref for WordBuf { 154 | type Target = [Word]; 155 | 156 | #[inline] 157 | fn deref(&self) -> &[Word] { 158 | self.inner.deref() 159 | } 160 | } 161 | 162 | impl DerefMut for WordBuf { 163 | #[inline] 164 | fn deref_mut(&mut self) -> &mut [Word] { 165 | self.inner.deref_mut() 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /astro-float-num/src/common/consts.rs: -------------------------------------------------------------------------------- 1 | //! Static constants. 2 | 3 | use crate::{defs::DEFAULT_P, num::BigFloatNumber, Exponent, WORD_BIT_SIZE}; 4 | use lazy_static::lazy_static; 5 | 6 | #[cfg(feature = "std")] 7 | use crate::ops::consts::Consts; 8 | #[cfg(feature = "std")] 9 | use core::cell::RefCell; 10 | 11 | lazy_static! { 12 | 13 | /// 1 14 | pub(crate) static ref ONE: BigFloatNumber = BigFloatNumber::from_word(1, DEFAULT_P).expect("Constant ONE initialization."); 15 | 16 | /// 2 17 | pub(crate) static ref TWO: BigFloatNumber = BigFloatNumber::from_word(2, DEFAULT_P).expect("Constant TWO initialization."); 18 | 19 | /// 4 20 | pub(crate) static ref FOUR: BigFloatNumber = BigFloatNumber::from_word(4, DEFAULT_P).expect("Constant FOUR initialization."); 21 | 22 | /// 6 23 | pub(crate) static ref SIX: BigFloatNumber = BigFloatNumber::from_word(6, DEFAULT_P).expect("Constant SIX initialization."); 24 | 25 | /// 8 26 | pub(crate) static ref EIGHT: BigFloatNumber = BigFloatNumber::from_word(8, DEFAULT_P).expect("Constant EIGHT initialization."); 27 | 28 | /// 16 29 | pub(crate) static ref SIXTEEN: BigFloatNumber = BigFloatNumber::from_word(16, DEFAULT_P).expect("Constant SIXTEEN initialization."); 30 | 31 | /// 3 32 | pub(crate) static ref THREE: BigFloatNumber = BigFloatNumber::from_word(3, DEFAULT_P).expect("Constant THREE initialization."); 33 | 34 | /// 5 35 | pub(crate) static ref FIVE: BigFloatNumber = BigFloatNumber::from_word(5, DEFAULT_P).expect("Constant FIVE initialization."); 36 | 37 | /// 10 38 | pub(crate) static ref TEN: BigFloatNumber = BigFloatNumber::from_word(10, DEFAULT_P).expect("Constant TEN initialization."); 39 | 40 | /// 15 41 | pub(crate) static ref FIFTEEN: BigFloatNumber = BigFloatNumber::from_word(15, DEFAULT_P).expect("Constant FIFTEEN initialization."); 42 | 43 | /// 24 44 | pub(crate) static ref C24: BigFloatNumber = BigFloatNumber::from_word(24, DEFAULT_P).expect("Constant C24 initialization."); 45 | 46 | /// 40 47 | pub(crate) static ref FOURTY: BigFloatNumber = BigFloatNumber::from_word(40, DEFAULT_P).expect("Constant FOURTY initialization."); 48 | 49 | /// 120 50 | pub(crate) static ref C120: BigFloatNumber = BigFloatNumber::from_word(120, DEFAULT_P).expect("Constant C24 initialization."); 51 | } 52 | 53 | // TODO: Consider using in std environment everywhere Consts are needed. 54 | #[cfg(feature = "std")] 55 | thread_local! { 56 | pub static TENPOWERS: RefCell = RefCell::new(Consts::new().expect("Failed to initialize thread-local constants cache")); 57 | } 58 | 59 | pub const TRIG_EXP_THRES: Exponent = -(WORD_BIT_SIZE as Exponent); 60 | -------------------------------------------------------------------------------- /astro-float-num/src/common/int.rs: -------------------------------------------------------------------------------- 1 | //! Lightweigh integer. 2 | 3 | use crate::common::util::add_carry; 4 | use crate::common::util::shift_slice_right; 5 | use crate::common::util::{shift_slice_left, sub_borrow}; 6 | use crate::defs::{DoubleWord, SignedWord, Word, WORD_BASE, WORD_MAX}; 7 | use core::ops::Deref; 8 | use core::ops::DerefMut; 9 | use itertools::izip; 10 | 11 | // Internal repr of SliceWithSign. 12 | #[derive(Debug)] 13 | enum SliceWithSignType<'a> { 14 | Mut(&'a mut [Word]), 15 | Immut(&'a [Word]), 16 | } 17 | 18 | // Slice of words with sign is a lightweight integer number representation. 19 | #[derive(Debug)] 20 | pub struct SliceWithSign<'a> { 21 | m: SliceWithSignType<'a>, 22 | sign: i8, 23 | } 24 | 25 | impl<'a> SliceWithSign<'a> { 26 | pub fn new_mut(m: &'a mut [Word], sign: i8) -> Self { 27 | SliceWithSign { 28 | m: SliceWithSignType::Mut(m), 29 | sign, 30 | } 31 | } 32 | 33 | pub fn new(m: &'a [Word], sign: i8) -> Self { 34 | SliceWithSign { 35 | m: SliceWithSignType::Immut(m), 36 | sign, 37 | } 38 | } 39 | 40 | #[inline] 41 | pub fn add(&self, s2: &SliceWithSign<'_>, dst: &mut SliceWithSign<'_>) { 42 | self.add_sub(s2, dst, 1); 43 | } 44 | 45 | #[inline] 46 | pub fn sub(&self, s2: &SliceWithSign<'_>, dst: &mut SliceWithSign<'_>) { 47 | self.add_sub(s2, dst, -1); 48 | } 49 | 50 | #[inline] 51 | pub fn add_assign(&mut self, s2: &SliceWithSign<'_>) { 52 | self.add_sub_assign(s2, 1); 53 | } 54 | 55 | #[inline] 56 | pub fn sub_assign(&mut self, s2: &SliceWithSign<'_>) { 57 | self.add_sub_assign(s2, -1); 58 | } 59 | 60 | fn add_sub(&self, s2: &SliceWithSign<'_>, dst: &mut SliceWithSign<'_>, op: i8) { 61 | if (self.sign != s2.sign && op > 0) || (op < 0 && self.sign == s2.sign) { 62 | // subtract 63 | let cmp = Self::abs_cmp(self, s2); 64 | 65 | if cmp > 0 { 66 | dst.sign = self.sign; 67 | Self::abs_sub(self, s2, dst); 68 | } else if cmp < 0 { 69 | dst.sign = s2.sign * op; 70 | Self::abs_sub(s2, self, dst); 71 | } else { 72 | dst.fill(0); 73 | }; 74 | } else { 75 | dst.sign = self.sign; 76 | Self::abs_add(self, s2, dst); 77 | } 78 | } 79 | 80 | fn add_sub_assign(&mut self, s2: &SliceWithSign<'_>, op: i8) { 81 | if (self.sign != s2.sign && op > 0) || (op < 0 && self.sign == s2.sign) { 82 | // subtract 83 | let cmp = Self::abs_cmp(self, s2); 84 | if cmp > 0 { 85 | Self::abs_sub_assign_1(self, s2); 86 | } else if cmp < 0 { 87 | Self::abs_sub_assign_2(self, s2); 88 | self.sign = s2.sign * op; 89 | } else { 90 | self.fill(0); 91 | }; 92 | } else { 93 | Self::abs_add_assign(self, s2); 94 | } 95 | } 96 | 97 | #[cfg(test)] 98 | pub fn mul_assign<'c>(&mut self, s2: &SliceWithSign<'c>, work_buf: &mut [Word]) { 99 | work_buf.fill(0); 100 | for (i, d1mi) in self.deref().iter().enumerate() { 101 | let d1mi = *d1mi as DoubleWord; 102 | if d1mi == 0 { 103 | continue; 104 | } 105 | 106 | let mut k = 0; 107 | for (m2j, m3ij) in s2.deref().iter().zip(work_buf[i..].iter_mut()) { 108 | let m = d1mi * (*m2j as DoubleWord) + *m3ij as DoubleWord + k; 109 | 110 | *m3ij = m as Word; 111 | k = m >> (crate::WORD_BIT_SIZE); 112 | } 113 | work_buf[i + s2.len()] += k as Word; 114 | } 115 | self.deref_mut().copy_from_slice(work_buf); 116 | self.sign *= s2.sign; 117 | } 118 | 119 | #[inline] 120 | pub fn shift_left(&mut self, shift: usize) { 121 | shift_slice_left(self, shift); 122 | } 123 | 124 | #[inline] 125 | pub fn shift_right(&mut self, shift: usize) { 126 | shift_slice_right(self, shift); 127 | } 128 | 129 | pub fn div_by_word(&mut self, d: Word) { 130 | debug_assert!(d != 0); 131 | 132 | let d = d as DoubleWord; 133 | let mut rh = 0; 134 | let m = self.deref_mut(); 135 | let mut iter = m.iter_mut().rev(); 136 | let mut val; 137 | 138 | if let Some(v) = iter.next() { 139 | val = v; 140 | } else { 141 | return; 142 | } 143 | 144 | if (*val as DoubleWord) < d { 145 | rh = *val as DoubleWord; 146 | *val = 0; 147 | if let Some(v) = iter.next() { 148 | val = v; 149 | } else { 150 | return; 151 | } 152 | } 153 | 154 | loop { 155 | let qh = rh * WORD_BASE as DoubleWord + *val as DoubleWord; 156 | 157 | rh = qh % d; 158 | 159 | *val = (qh / d) as Word; 160 | 161 | if let Some(v) = iter.next() { 162 | val = v; 163 | } else { 164 | break; 165 | } 166 | } 167 | } 168 | 169 | pub fn copy_from(&mut self, s2: &SliceWithSign<'_>) { 170 | match &mut self.m { 171 | SliceWithSignType::Mut(m) => { 172 | match &s2.m { 173 | SliceWithSignType::Mut(s) => m[..s.len()].copy_from_slice(s), 174 | SliceWithSignType::Immut(s) => m[..s.len()].copy_from_slice(s), 175 | }; 176 | } 177 | _ => panic!(), 178 | } 179 | } 180 | 181 | fn abs_add(s1: &[Word], s2: &[Word], dst: &mut [Word]) { 182 | let mut c = 0; 183 | 184 | let (iter1, mut iter2) = if s1.len() < s2.len() { 185 | (s1.iter(), s2.iter()) 186 | } else { 187 | (s2.iter(), s1.iter()) 188 | }; 189 | 190 | let mut iter3 = dst.iter_mut(); 191 | 192 | for (a, b, x) in izip!(iter1, iter2.by_ref(), iter3.by_ref()) { 193 | c = add_carry(*a, *b, c, x); 194 | } 195 | 196 | for (b, x) in iter2.zip(iter3.by_ref()) { 197 | c = add_carry(0, *b, c, x); 198 | } 199 | 200 | if c > 0 { 201 | *iter3.next().unwrap() = c as Word; // dst is supposed to be longer than s1 and s2 to process carry successfully. 202 | } 203 | 204 | for v in iter3 { 205 | *v = 0; 206 | } 207 | } 208 | 209 | fn abs_add_assign(s1: &mut [Word], s2: &[Word]) { 210 | let mut c = 0; 211 | let mut iter1 = s1.iter_mut(); 212 | let iter2 = s2.iter(); 213 | 214 | for (b, a) in izip!(iter2, iter1.by_ref()) { 215 | c = add_carry(*a, *b, c, a); 216 | } 217 | 218 | for a in iter1 { 219 | c = add_carry(*a, 0, c, a); 220 | } 221 | } 222 | 223 | // prereq: val of s1 >= val of s2 224 | fn abs_sub_assign_1(s1: &mut [Word], s2: &[Word]) { 225 | let mut c = 0; 226 | let mut iter1 = s1.iter_mut(); 227 | let iter2 = s2.iter(); 228 | 229 | for (b, a) in izip!(iter2, iter1.by_ref()) { 230 | c = sub_borrow(*a, *b, c, a); 231 | } 232 | 233 | for a in iter1 { 234 | c = sub_borrow(*a, 0, c, a); 235 | } 236 | 237 | debug_assert!(c == 0); 238 | } 239 | 240 | // prereq: val of s2 > val of s1 241 | fn abs_sub_assign_2(s1: &mut [Word], s2: &[Word]) { 242 | let mut c = 0; 243 | 244 | for (a, b) in s2.iter().zip(s1.iter_mut()) { 245 | c = sub_borrow(*a, *b, c, b); 246 | } 247 | 248 | debug_assert!(c == 0); 249 | } 250 | 251 | fn abs_sub(s1: &[Word], s2: &[Word], dst: &mut [Word]) { 252 | let mut c = 0; 253 | 254 | let mut iter1 = s1.iter(); 255 | let iter2 = s2.iter(); 256 | let mut iter3 = dst.iter_mut(); 257 | 258 | for (b, a, d) in izip!(iter2, iter1.by_ref(), iter3.by_ref()) { 259 | c = sub_borrow(*a, *b, c, d); 260 | } 261 | 262 | if c > 0 { 263 | for (a, d) in iter1.zip(iter3.by_ref()) { 264 | c = sub_borrow(*a, 0, c, d); 265 | } 266 | } else { 267 | for (a, d) in iter1.zip(iter3.by_ref()) { 268 | *d = *a; 269 | } 270 | } 271 | 272 | for v in iter3 { 273 | *v = 0; 274 | } 275 | } 276 | 277 | // decrement absolute value by 1 278 | pub fn decrement_abs(&mut self) { 279 | for v in self.iter_mut() { 280 | if *v == 0 { 281 | *v = WORD_MAX; 282 | } else { 283 | *v -= 1; 284 | return; 285 | } 286 | } 287 | panic!("numeric overflow"); 288 | } 289 | 290 | pub fn is_zero(&self) -> bool { 291 | for v in self.iter() { 292 | if *v != 0 { 293 | return false; 294 | } 295 | } 296 | true 297 | } 298 | 299 | fn abs_cmp(s1: &[Word], s2: &[Word]) -> SignedWord { 300 | let len = s1.len().min(s2.len()); 301 | 302 | for v in &s1[len..] { 303 | if *v != 0 { 304 | return 1; 305 | } 306 | } 307 | 308 | for v in &s2[len..] { 309 | if *v != 0 { 310 | return -1; 311 | } 312 | } 313 | 314 | for (a, b) in core::iter::zip(s1[..len].iter().rev(), s2[..len].iter().rev()) { 315 | let diff = *a as SignedWord - *b as SignedWord; 316 | if diff != 0 { 317 | return diff; 318 | } 319 | } 320 | 321 | 0 322 | } 323 | 324 | #[inline] 325 | pub fn set_sign(&mut self, sign: i8) { 326 | self.sign = sign; 327 | } 328 | 329 | #[inline] 330 | pub fn sign(&self) -> i8 { 331 | self.sign 332 | } 333 | 334 | pub fn cmp(&self, d2: &Self) -> SignedWord { 335 | if self.is_zero() && d2.is_zero() { 336 | return 0; 337 | } 338 | 339 | if self.sign != d2.sign { 340 | return self.sign as SignedWord; 341 | } 342 | 343 | Self::abs_cmp(self, d2) * self.sign as SignedWord 344 | } 345 | } 346 | 347 | impl<'a> Deref for SliceWithSign<'a> { 348 | type Target = [Word]; 349 | 350 | #[inline] 351 | fn deref(&self) -> &[Word] { 352 | match &self.m { 353 | SliceWithSignType::Mut(m) => m, 354 | SliceWithSignType::Immut(m) => m, 355 | } 356 | } 357 | } 358 | 359 | impl<'a> DerefMut for SliceWithSign<'a> { 360 | #[inline] 361 | fn deref_mut(&mut self) -> &mut [Word] { 362 | match &mut self.m { 363 | SliceWithSignType::Mut(m) => m, 364 | _ => panic!(), 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /astro-float-num/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | //! Mathematical constants. 2 | 3 | pub mod buf; 4 | pub mod consts; 5 | pub mod int; 6 | pub mod util; 7 | -------------------------------------------------------------------------------- /astro-float-num/src/ctx.rs: -------------------------------------------------------------------------------- 1 | //! Context is used in expressions returning `BigFloat`. 2 | 3 | use crate::BigFloat; 4 | use crate::Consts; 5 | use crate::Error; 6 | use crate::Exponent; 7 | use crate::RoundingMode; 8 | use crate::EXPONENT_MAX; 9 | use crate::EXPONENT_MIN; 10 | 11 | /// Context contains parameters, like rounding mode and precision, as well as constant values, and is used with `expr!` macro. 12 | #[derive(Debug)] 13 | pub struct Context { 14 | cc: Consts, 15 | p: usize, 16 | rm: RoundingMode, 17 | emin: Exponent, 18 | emax: Exponent, 19 | } 20 | 21 | impl Context { 22 | /// Create a new context. 23 | /// The value of `emin` will be clamped to a range between EXPONENT_MIN and 0. 24 | /// The value of `emax` will be clamped to a range between 0 and EXPONENT_MAX. 25 | pub fn new(p: usize, rm: RoundingMode, cc: Consts, emin: Exponent, emax: Exponent) -> Self { 26 | Context { 27 | cc, 28 | p, 29 | rm, 30 | emin: emin.clamp(EXPONENT_MIN, 0), 31 | emax: emax.clamp(0, EXPONENT_MAX), 32 | } 33 | } 34 | 35 | /// Destructures the context and returns its parts: target precision, rounding mode, 36 | /// constant cache, minimum exponent, maximum exponent. 37 | pub fn to_raw_parts(self) -> (usize, RoundingMode, Consts, Exponent, Exponent) { 38 | let Context { 39 | p, 40 | rm, 41 | cc, 42 | emin, 43 | emax, 44 | } = self; 45 | (p, rm, cc, emin, emax) 46 | } 47 | 48 | /// Sets the precision of the context. 49 | pub fn set_precision(&mut self, p: usize) { 50 | self.p = p; 51 | } 52 | 53 | /// Sets the rounding mode of the context. 54 | pub fn set_rounding_mode(&mut self, rm: RoundingMode) { 55 | self.rm = rm; 56 | } 57 | 58 | /// Sets the constant cache of the context. 59 | pub fn set_consts(&mut self, cc: Consts) { 60 | self.cc = cc; 61 | } 62 | 63 | /// Sets the minimum exponent. 64 | /// The value of `emin` will be clamped to a range between EXPONENT_MIN and 0. 65 | pub fn set_emin(&mut self, emin: Exponent) { 66 | self.emin = emin.clamp(EXPONENT_MIN, 0); 67 | } 68 | 69 | /// Sets the maximum exponent. 70 | /// The value of `emax` will be clamped to a range between 0 and EXPONENT_MAX. 71 | pub fn set_emax(&mut self, emax: Exponent) { 72 | self.emax = emax.clamp(0, EXPONENT_MAX); 73 | } 74 | 75 | /// Returns the precision of the context. 76 | pub fn precision(&self) -> usize { 77 | self.p 78 | } 79 | 80 | /// Returns the rounding mode of the context. 81 | pub fn rounding_mode(&self) -> RoundingMode { 82 | self.rm 83 | } 84 | 85 | /// Returns a mutable reference to the constant cache of the context. 86 | pub fn consts(&mut self) -> &mut Consts { 87 | &mut self.cc 88 | } 89 | 90 | /// Returns the value of the pi number. 91 | pub fn const_pi(&mut self) -> BigFloat { 92 | self.cc.pi(self.p, self.rm) 93 | } 94 | 95 | /// Returns the value of the Euler number. 96 | pub fn const_e(&mut self) -> BigFloat { 97 | self.cc.e(self.p, self.rm) 98 | } 99 | 100 | /// Returns the value of the natural logarithm of 2. 101 | pub fn const_ln2(&mut self) -> BigFloat { 102 | self.cc.ln_2(self.p, self.rm) 103 | } 104 | 105 | /// Returns the value of the natural logarithm of 10. 106 | pub fn const_ln10(&mut self) -> BigFloat { 107 | self.cc.ln_10(self.p, self.rm) 108 | } 109 | 110 | /// Returns the minimum exponent. 111 | pub fn emin(&self) -> Exponent { 112 | self.emin 113 | } 114 | 115 | /// Returns the maximum exponent. 116 | pub fn emax(&self) -> Exponent { 117 | self.emax 118 | } 119 | 120 | /// Clones `self` and returns the cloned context. 121 | /// 122 | /// # Errors 123 | /// 124 | /// - MemoryAllocation: failed to allocate memory for the constants cache. 125 | #[allow(clippy::should_implement_trait)] 126 | pub fn clone(&self) -> Result { 127 | let cc = Consts::new()?; 128 | Ok(Context { 129 | p: self.p, 130 | rm: self.rm, 131 | cc, 132 | emin: self.emin, 133 | emax: self.emax, 134 | }) 135 | } 136 | } 137 | 138 | /// Represents a type that can be used as context in `expr!` macro. 139 | /// 140 | /// ## Examples 141 | /// 142 | /// ``` 143 | /// # use astro_float_num::RoundingMode; 144 | /// # use astro_float_num::Consts; 145 | /// # use astro_float_num::ctx::Contextable; 146 | /// let p = 123; 147 | /// let rm = RoundingMode::Down; 148 | /// let mut cc = Consts::new().expect("Constants cache allocated"); 149 | /// let pi = cc.pi(p, rm); 150 | /// 151 | /// // Make context out of tuple. 152 | /// let mut ctx = (p, rm, &mut cc); 153 | /// 154 | /// assert_eq!(p, ctx.precision()); 155 | /// assert_eq!(rm, ctx.rounding_mode()); 156 | /// assert_eq!(pi, ctx.const_pi()); 157 | /// ``` 158 | pub trait Contextable { 159 | /// Returns the precision of the context. 160 | fn precision(&self) -> usize; 161 | 162 | /// Returns the rounding mode of the context. 163 | fn rounding_mode(&self) -> RoundingMode; 164 | 165 | /// Returns a mutable reference to the constant cache of the context. 166 | fn consts(&mut self) -> &mut Consts; 167 | 168 | /// Returns the value of the pi number. 169 | fn const_pi(&mut self) -> BigFloat; 170 | 171 | /// Returns the value of the Euler number. 172 | fn const_e(&mut self) -> BigFloat; 173 | 174 | /// Returns the value of the natural logarithm of 2. 175 | fn const_ln2(&mut self) -> BigFloat; 176 | 177 | /// Returns the value of the natural logarithm of 10. 178 | fn const_ln10(&mut self) -> BigFloat; 179 | 180 | /// Returns the minimum exponent. 181 | fn emin(&self) -> Exponent; 182 | 183 | /// Returns the maximum exponent. 184 | fn emax(&self) -> Exponent; 185 | } 186 | 187 | impl Contextable for (usize, RoundingMode, &mut Consts) { 188 | fn precision(&self) -> usize { 189 | self.0 190 | } 191 | 192 | fn rounding_mode(&self) -> RoundingMode { 193 | self.1 194 | } 195 | 196 | fn consts(&mut self) -> &mut Consts { 197 | self.2 198 | } 199 | 200 | fn const_pi(&mut self) -> BigFloat { 201 | let (p, rm) = (self.0, self.1); 202 | self.consts().pi(p, rm) 203 | } 204 | 205 | fn const_e(&mut self) -> BigFloat { 206 | let (p, rm) = (self.0, self.1); 207 | self.consts().e(p, rm) 208 | } 209 | 210 | fn const_ln2(&mut self) -> BigFloat { 211 | let (p, rm) = (self.0, self.1); 212 | self.consts().ln_2(p, rm) 213 | } 214 | 215 | fn const_ln10(&mut self) -> BigFloat { 216 | let (p, rm) = (self.0, self.1); 217 | self.consts().ln_10(p, rm) 218 | } 219 | 220 | fn emin(&self) -> Exponent { 221 | EXPONENT_MIN 222 | } 223 | 224 | fn emax(&self) -> Exponent { 225 | EXPONENT_MAX 226 | } 227 | } 228 | 229 | impl Contextable for (usize, RoundingMode, &mut Consts, Exponent, Exponent) { 230 | fn precision(&self) -> usize { 231 | self.0 232 | } 233 | 234 | fn rounding_mode(&self) -> RoundingMode { 235 | self.1 236 | } 237 | 238 | fn consts(&mut self) -> &mut Consts { 239 | self.2 240 | } 241 | 242 | fn const_pi(&mut self) -> BigFloat { 243 | let (p, rm) = (self.0, self.1); 244 | self.consts().pi(p, rm) 245 | } 246 | 247 | fn const_e(&mut self) -> BigFloat { 248 | let (p, rm) = (self.0, self.1); 249 | self.consts().e(p, rm) 250 | } 251 | 252 | fn const_ln2(&mut self) -> BigFloat { 253 | let (p, rm) = (self.0, self.1); 254 | self.consts().ln_2(p, rm) 255 | } 256 | 257 | fn const_ln10(&mut self) -> BigFloat { 258 | let (p, rm) = (self.0, self.1); 259 | self.consts().ln_10(p, rm) 260 | } 261 | 262 | fn emin(&self) -> Exponent { 263 | self.3.clamp(EXPONENT_MIN, 0) 264 | } 265 | 266 | fn emax(&self) -> Exponent { 267 | self.4.clamp(0, EXPONENT_MAX) 268 | } 269 | } 270 | 271 | impl Contextable for Context { 272 | fn precision(&self) -> usize { 273 | Context::precision(self) 274 | } 275 | 276 | fn rounding_mode(&self) -> RoundingMode { 277 | Context::rounding_mode(self) 278 | } 279 | 280 | fn consts(&mut self) -> &mut Consts { 281 | Context::consts(self) 282 | } 283 | 284 | fn const_pi(&mut self) -> BigFloat { 285 | Context::const_pi(self) 286 | } 287 | 288 | fn const_e(&mut self) -> BigFloat { 289 | Context::const_e(self) 290 | } 291 | 292 | fn const_ln2(&mut self) -> BigFloat { 293 | Context::const_ln2(self) 294 | } 295 | 296 | fn const_ln10(&mut self) -> BigFloat { 297 | Context::const_ln10(self) 298 | } 299 | 300 | fn emin(&self) -> Exponent { 301 | Context::emin(self) 302 | } 303 | 304 | fn emax(&self) -> Exponent { 305 | Context::emax(self) 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /astro-float-num/src/defs.rs: -------------------------------------------------------------------------------- 1 | //! Definitions. 2 | 3 | use core::fmt::Display; 4 | 5 | #[cfg(feature = "std")] 6 | use std::collections::TryReserveError; 7 | 8 | #[cfg(not(feature = "std"))] 9 | use alloc::collections::TryReserveError; 10 | 11 | /// A word. 12 | #[cfg(not(target_arch = "x86"))] 13 | pub type Word = u64; 14 | 15 | /// Doubled word. 16 | #[cfg(not(target_arch = "x86"))] 17 | pub type DoubleWord = u128; 18 | 19 | /// Word with sign. 20 | #[cfg(not(target_arch = "x86"))] 21 | pub type SignedWord = i128; 22 | 23 | /// A word. 24 | #[cfg(target_arch = "x86")] 25 | pub type Word = u32; 26 | 27 | /// Doubled word. 28 | #[cfg(target_arch = "x86")] 29 | pub type DoubleWord = u64; 30 | 31 | /// Word with sign. 32 | #[cfg(target_arch = "x86")] 33 | pub type SignedWord = i64; 34 | 35 | /// An exponent. 36 | pub type Exponent = i32; 37 | 38 | /// Maximum exponent value. 39 | #[cfg(not(target_arch = "x86"))] 40 | pub const EXPONENT_MAX: Exponent = Exponent::MAX; 41 | 42 | /// Maximum exponent value. 43 | #[cfg(target_arch = "x86")] 44 | pub const EXPONENT_MAX: Exponent = Exponent::MAX / 4; 45 | 46 | /// Minimum exponent value. 47 | #[cfg(not(target_arch = "x86"))] 48 | pub const EXPONENT_MIN: Exponent = Exponent::MIN; 49 | 50 | /// Minimum exponent value. 51 | #[cfg(target_arch = "x86")] 52 | pub const EXPONENT_MIN: Exponent = Exponent::MIN / 4; 53 | 54 | /// Maximum value of a word. 55 | pub const WORD_MAX: Word = Word::MAX; 56 | 57 | /// Base of words. 58 | pub const WORD_BASE: DoubleWord = WORD_MAX as DoubleWord + 1; 59 | 60 | /// Size of a word in bits. 61 | pub const WORD_BIT_SIZE: usize = core::mem::size_of::() * 8; 62 | 63 | /// Word with the most significant bit set. 64 | pub const WORD_SIGNIFICANT_BIT: Word = WORD_MAX << (WORD_BIT_SIZE - 1); 65 | 66 | /// Default precision. 67 | pub const DEFAULT_P: usize = 128; 68 | 69 | /// The size of exponent type in bits. 70 | pub const EXPONENT_BIT_SIZE: usize = core::mem::size_of::() * 8; 71 | 72 | /// Sign. 73 | #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] 74 | pub enum Sign { 75 | /// Negative. 76 | Neg = -1, 77 | 78 | /// Positive. 79 | Pos = 1, 80 | } 81 | 82 | impl Sign { 83 | /// Changes the sign to the opposite. 84 | pub fn invert(&self) -> Self { 85 | match *self { 86 | Sign::Pos => Sign::Neg, 87 | Sign::Neg => Sign::Pos, 88 | } 89 | } 90 | 91 | /// Returns true if `self` is positive. 92 | pub fn is_positive(&self) -> bool { 93 | *self == Sign::Pos 94 | } 95 | 96 | /// Returns true if `self` is negative. 97 | pub fn is_negative(&self) -> bool { 98 | *self == Sign::Neg 99 | } 100 | 101 | /// Returns 1 for the positive sign and -1 for the negative sign. 102 | pub fn to_int(&self) -> i8 { 103 | *self as i8 104 | } 105 | } 106 | 107 | /// Possible errors. 108 | #[derive(Debug, Clone, Copy)] 109 | pub enum Error { 110 | /// The exponent value becomes greater than the upper limit of the range of exponent values. 111 | ExponentOverflow(Sign), 112 | 113 | /// Divizor is zero. 114 | DivisionByZero, 115 | 116 | /// Invalid argument. 117 | InvalidArgument, 118 | 119 | /// Memory allocation error. 120 | MemoryAllocation, 121 | } 122 | 123 | #[cfg(feature = "std")] 124 | impl std::error::Error for Error { 125 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 126 | None 127 | } 128 | } 129 | 130 | impl Display for Error { 131 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 132 | let repr = match self { 133 | Error::ExponentOverflow(s) => { 134 | if s.is_positive() { 135 | "positive overflow" 136 | } else { 137 | "negative overflow" 138 | } 139 | } 140 | Error::DivisionByZero => "division by zero", 141 | Error::InvalidArgument => "invalid argument", 142 | Error::MemoryAllocation => "memory allocation failure", 143 | }; 144 | f.write_str(repr) 145 | } 146 | } 147 | 148 | impl PartialEq for Error { 149 | fn eq(&self, other: &Self) -> bool { 150 | match (self, other) { 151 | (Self::ExponentOverflow(l0), Self::ExponentOverflow(r0)) => l0 == r0, 152 | _ => core::mem::discriminant(self) == core::mem::discriminant(other), 153 | } 154 | } 155 | } 156 | 157 | impl From for Error { 158 | fn from(_: TryReserveError) -> Self { 159 | Error::MemoryAllocation 160 | } 161 | } 162 | 163 | /// Radix. 164 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 165 | pub enum Radix { 166 | /// Binary. 167 | Bin = 2, 168 | 169 | /// Octal. 170 | Oct = 8, 171 | 172 | /// Decimal. 173 | Dec = 10, 174 | 175 | /// Hexadecimal. 176 | Hex = 16, 177 | } 178 | 179 | /// Rounding modes. 180 | #[derive(Eq, PartialEq, Debug, Copy, Clone)] 181 | pub enum RoundingMode { 182 | /// Skip rounding operation. 183 | None = 1, 184 | 185 | /// Round half toward positive infinity. 186 | Up = 2, 187 | 188 | /// Round half toward negative infinity. 189 | Down = 4, 190 | 191 | /// Round half toward zero. 192 | ToZero = 8, 193 | 194 | /// Round half away from zero. 195 | FromZero = 16, 196 | 197 | /// Round half to even. 198 | ToEven = 32, 199 | 200 | /// Round half to odd. 201 | ToOdd = 64, 202 | } 203 | -------------------------------------------------------------------------------- /astro-float-num/src/for_3rd/de.rs: -------------------------------------------------------------------------------- 1 | //! Deserialization of BigFloat. 2 | 3 | use core::fmt::Formatter; 4 | use core::str::FromStr; 5 | 6 | use crate::num::BigFloatNumber; 7 | use crate::BigFloat; 8 | use serde::de::Error; 9 | use serde::de::Visitor; 10 | use serde::{Deserialize, Deserializer}; 11 | 12 | pub struct BigFloatVisitor {} 13 | 14 | impl<'de> Deserialize<'de> for BigFloat { 15 | fn deserialize>(deserializer: D) -> Result { 16 | deserializer.deserialize_any(BigFloatVisitor {}) 17 | } 18 | } 19 | 20 | impl<'de> Visitor<'de> for BigFloatVisitor { 21 | type Value = BigFloat; 22 | 23 | fn expecting(&self, formatter: &mut Formatter) -> core::fmt::Result { 24 | write!(formatter, "except `String`, `Number`, `Bytes`") 25 | } 26 | 27 | fn visit_u64(self, v: u64) -> Result { 28 | match BigFloatNumber::from_usize(v as usize) { 29 | Ok(o) => Ok(o.into()), 30 | Err(e) => Err(Error::custom(format!("{e:?}"))), 31 | } 32 | } 33 | 34 | fn visit_f32(self, v: f32) -> Result { 35 | match BigFloatNumber::from_f64(64, v as f64) { 36 | Ok(o) => Ok(o.into()), 37 | Err(e) => Err(Error::custom(format!("{e:?}"))), 38 | } 39 | } 40 | 41 | fn visit_f64(self, v: f64) -> Result { 42 | match BigFloatNumber::from_f64(64, v) { 43 | Ok(o) => Ok(o.into()), 44 | Err(e) => Err(Error::custom(format!("{e:?}"))), 45 | } 46 | } 47 | 48 | fn visit_str(self, v: &str) -> Result { 49 | match BigFloat::from_str(v) { 50 | Ok(o) => Ok(o), 51 | Err(e) => Err(Error::custom(format!("{e:?}"))), 52 | } 53 | } 54 | 55 | fn visit_string(self, v: String) -> Result { 56 | self.visit_str(&v) 57 | } 58 | 59 | // lossless conversion 60 | // (&[Word], usize, Sign, Exponent) 61 | // (s * len, s , 1 , 1 ) 62 | // fn visit_bytes(self, _: &[u8]) -> Result { 63 | // todo!() 64 | // } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | 70 | use core::str::FromStr; 71 | 72 | use serde_json::from_str; 73 | 74 | use crate::BigFloat; 75 | 76 | #[cfg(not(feature = "std"))] 77 | use alloc::format; 78 | 79 | #[test] 80 | fn from_json() { 81 | let x = BigFloat::new(1); 82 | assert_eq!(x, from_str::("-0").unwrap()); 83 | assert_eq!(x, from_str::("0.0").unwrap()); 84 | 85 | let x = BigFloat::from_f64(0.3, 64); 86 | assert_eq!(x, from_str::("0.3").unwrap()); 87 | 88 | let x = BigFloat::from_str("0.3").unwrap(); 89 | assert_eq!(x, from_str::("\"0.3\"").unwrap()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /astro-float-num/src/for_3rd/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "serde")] 2 | mod de; 3 | #[cfg(feature = "serde")] 4 | mod ser; 5 | -------------------------------------------------------------------------------- /astro-float-num/src/for_3rd/ser.rs: -------------------------------------------------------------------------------- 1 | //! Serialization of BigFloat. 2 | //! Serialization to a string uses decimal radix. 3 | 4 | use crate::BigFloat; 5 | use serde::{Serialize, Serializer}; 6 | 7 | impl Serialize for BigFloat { 8 | fn serialize(&self, serializer: S) -> Result { 9 | serializer.serialize_str(&self.to_string()) 10 | } 11 | } 12 | 13 | #[cfg(test)] 14 | mod tests { 15 | use serde_json::to_string; 16 | 17 | use crate::BigFloat; 18 | 19 | #[test] 20 | fn to_json() { 21 | assert_eq!(to_string(&BigFloat::new(0)).unwrap(), "\"0.0\""); 22 | assert_eq!( 23 | to_string(&BigFloat::from_f32(0.3, 64 + 1)).unwrap(), 24 | "\"3.00000011920928955078125e-1\"" 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /astro-float-num/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Astro-float (astronomically large floating point numbers) is a library that implements arbitrary precision floating point numbers. 2 | //! 3 | //! See main crate [docs](https://docs.rs/astro-float/latest/astro_float/). 4 | 5 | #![cfg_attr(not(feature = "std"), no_std)] 6 | #![deny(missing_docs)] 7 | #![deny(clippy::suspicious)] 8 | #![allow(clippy::comparison_chain)] 9 | #![allow(clippy::collapsible_else_if)] 10 | #![allow(clippy::collapsible_if)] 11 | 12 | #[cfg(not(feature = "std"))] 13 | extern crate alloc; 14 | 15 | mod common; 16 | mod conv; 17 | pub mod ctx; 18 | mod defs; 19 | mod ext; 20 | mod mantissa; 21 | mod num; 22 | mod ops; 23 | mod parser; 24 | mod strop; 25 | 26 | #[cfg(feature = "std")] 27 | mod for_3rd; 28 | 29 | #[doc(hidden)] 30 | pub mod macro_util; 31 | 32 | pub use crate::defs::Error; 33 | pub use crate::defs::Exponent; 34 | pub use crate::defs::Radix; 35 | pub use crate::defs::RoundingMode; 36 | pub use crate::defs::Sign; 37 | pub use crate::defs::Word; 38 | pub use crate::ext::BigFloat; 39 | pub use crate::ext::FromExt; 40 | pub use crate::ext::INF_NEG; 41 | pub use crate::ext::INF_POS; 42 | pub use crate::ext::NAN; 43 | pub use crate::ops::consts::Consts; 44 | 45 | pub use crate::defs::EXPONENT_BIT_SIZE; 46 | pub use crate::defs::EXPONENT_MAX; 47 | pub use crate::defs::EXPONENT_MIN; 48 | pub use crate::defs::WORD_BASE; 49 | pub use crate::defs::WORD_BIT_SIZE; 50 | pub use crate::defs::WORD_MAX; 51 | pub use crate::defs::WORD_SIGNIFICANT_BIT; 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | 56 | #[test] 57 | fn test_bigfloat() { 58 | use crate::BigFloat; 59 | use crate::Consts; 60 | use crate::RoundingMode; 61 | 62 | // Precision with some space for error. 63 | let p = 1024 + 8; 64 | 65 | // Rounding of all operations 66 | let rm = RoundingMode::ToEven; 67 | 68 | // Initialize mathematical constants cache 69 | let mut cc = Consts::new().expect("An error occured when initializing constants"); 70 | 71 | // Compute pi: pi = 6*arctan(1/sqrt(3)) 72 | let six = BigFloat::from_word(6, 1); 73 | let three = BigFloat::from_word(3, p); 74 | 75 | let n = three.sqrt(p, rm); 76 | let n = n.reciprocal(p, rm); 77 | let n = n.atan(p, rm, &mut cc); 78 | let mut pi = six.mul(&n, p, rm); 79 | 80 | // Reduce precision to 1024 81 | pi.set_precision(1024, rm).expect("Precision updated"); 82 | 83 | // Use library's constant for verifying the result 84 | let pi_lib = cc.pi_num(1024, rm).unwrap().into(); 85 | 86 | // Compare computed constant with library's constant 87 | assert_eq!(pi.cmp(&pi_lib), Some(0)); 88 | 89 | // Print using decimal radix. 90 | //println!("{}", pi); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /astro-float-num/src/mantissa/cbrt.rs: -------------------------------------------------------------------------------- 1 | //! Cube root. 2 | 3 | use crate::common::buf::WordBuf; 4 | use crate::common::int::SliceWithSign; 5 | use crate::common::util::find_one_from; 6 | use crate::common::util::shift_slice_left; 7 | use crate::common::util::shift_slice_right; 8 | use crate::defs::Error; 9 | use crate::defs::WORD_BIT_SIZE; 10 | use crate::mantissa::util::root_estimate; 11 | use crate::mantissa::util::RightShiftedSlice; 12 | use crate::mantissa::Mantissa; 13 | use crate::Word; 14 | 15 | impl Mantissa { 16 | // normalize for division 17 | fn crbt_normalize_div( 18 | m1: &mut WordBuf, 19 | m2: &mut [Word], 20 | m_shift: usize, 21 | ) -> Result { 22 | if let Some(shift) = find_one_from(m2, 0) { 23 | shift_slice_left(m2, shift); 24 | 25 | if m_shift > shift { 26 | shift_slice_right(m1 as &mut [Word], m_shift - shift); 27 | } else if m_shift < shift { 28 | shift_slice_left(m1 as &mut [Word], shift - m_shift); 29 | } 30 | 31 | Ok(shift) 32 | } else { 33 | Ok(m_shift) 34 | } 35 | } 36 | 37 | /// Cube root with remainder. 38 | pub fn cbrt_rem(mut m: WordBuf) -> Result<(WordBuf, WordBuf), Error> { 39 | let mut sbuf = root_estimate(&m, 3)?; 40 | let mut tmpbuf = WordBuf::new(sbuf.len() * 5)?; 41 | let mut wb = WordBuf::new(sbuf.len() + 1)?; 42 | let mut m_shift = 0; 43 | 44 | // additional space for normalization 45 | m.try_extend_2((m.len() + 2) * WORD_BIT_SIZE)?; 46 | 47 | loop { 48 | let sqbuf = &mut tmpbuf[..sbuf.len() * 2]; 49 | 50 | // sq = s^2 51 | Self::mul_unbalanced(&sbuf, &sbuf, sqbuf)?; 52 | 53 | m_shift = Self::crbt_normalize_div(&mut m, sqbuf, m_shift)?; 54 | 55 | // m / sq 56 | let (mut qbuf, _rbuf) = Self::div_unbalanced(&m, sqbuf)?; 57 | 58 | // w = s * 2 59 | wb.iter_mut() 60 | .zip(RightShiftedSlice::new(&sbuf, WORD_BIT_SIZE - 1, 0, 1)) 61 | .for_each(|(u, v)| *u = v); 62 | 63 | qbuf.try_extend_2((qbuf.len().max(wb.len()) + 1) * WORD_BIT_SIZE)?; 64 | 65 | let s = SliceWithSign::new(&sbuf, 1); 66 | let w = SliceWithSign::new(&wb, 1); 67 | let mut q = SliceWithSign::new_mut(&mut qbuf, 1); 68 | 69 | // (q + w) / 3 70 | q.add_assign(&w); 71 | q.div_by_word(3); 72 | 73 | // compare with previous 74 | if q.cmp(&s) >= 0 { 75 | // remainder 76 | let mut rbuf = WordBuf::new(m.len() - 2)?; 77 | shift_slice_right(&mut m, m_shift); 78 | rbuf.copy_from_slice(&m[..m.len() - 2]); 79 | 80 | let mut r = SliceWithSign::new_mut(&mut rbuf, 1); 81 | 82 | Self::cbrt_remainder(&mut tmpbuf, &mut sbuf, &mut r)?; 83 | 84 | debug_assert!(r.sign() >= 0); 85 | 86 | break Ok((sbuf, rbuf)); 87 | } 88 | 89 | qbuf.trunc_leading_zeroes(); 90 | sbuf = qbuf; 91 | } 92 | } 93 | 94 | // compute remainder 95 | fn cbrt_remainder( 96 | tmpbuf: &mut WordBuf, 97 | sbuf: &mut WordBuf, 98 | r: &mut SliceWithSign, 99 | ) -> Result<(), Error> { 100 | let (sqbuf, rest) = tmpbuf.split_at_mut(sbuf.len() * 2); 101 | let scbuf = &mut rest[..sbuf.len() * 3]; 102 | 103 | // q^3 104 | Self::mul_unbalanced(sbuf, sbuf, sqbuf)?; 105 | Self::mul_unbalanced(sqbuf, sbuf, scbuf)?; 106 | let sc = SliceWithSign::new(scbuf, 1); 107 | 108 | r.sub_assign(&sc); 109 | 110 | Ok(()) 111 | } 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | 117 | use crate::{ 118 | defs::{WORD_MAX, WORD_SIGNIFICANT_BIT}, 119 | Word, 120 | }; 121 | 122 | use super::*; 123 | use rand::random; 124 | 125 | macro_rules! assert_sqrt { 126 | ($s1:expr, $qb:expr, $rb:expr, $MAX_BUF:ident, $op:literal) => { 127 | let mut wb = [0; MAX_BUF * 3]; 128 | let mut buf = [0; MAX_BUF * 3]; 129 | 130 | let q = SliceWithSign::new($qb, 1); 131 | let r = SliceWithSign::new($rb, 1); 132 | 133 | buf[q.len()..].fill(0); 134 | buf[..q.len()].copy_from_slice(&q); 135 | 136 | let mut qq = SliceWithSign::new_mut(&mut buf, 1); 137 | 138 | qq.mul_assign(&q, &mut wb); 139 | qq.mul_assign(&q, &mut wb); 140 | qq.add_assign(&r); 141 | 142 | assert_eq!(&qq[..$s1.len()], $s1, "{}", $op); 143 | }; 144 | } 145 | 146 | fn wordbuf_from_words(s: &[Word]) -> WordBuf { 147 | let mut ret = WordBuf::new(s.len()).unwrap(); 148 | ret.copy_from_slice(s); 149 | ret 150 | } 151 | 152 | #[test] 153 | fn test_cbrt_rem() { 154 | const MAX_BUF: usize = 100; 155 | 156 | /* let s1 = &[1, 1, 1, WORD_SIGNIFICANT_BIT]; 157 | 158 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(s1)).unwrap(); 159 | 160 | println!("\n{:?}\n{:?}", qb, rb); 161 | 162 | assert_sqrt!(s1, &qb, &rb, MAX_BUF, "zeroes between"); 163 | return; */ 164 | 165 | for s1 in [ 166 | &[WORD_MAX] as &[Word], 167 | &[WORD_MAX, WORD_MAX], 168 | &[WORD_MAX, WORD_MAX, WORD_MAX], 169 | &[WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX], 170 | &[WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX], 171 | &[WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX], 172 | &[WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX], 173 | &[WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX, WORD_MAX], 174 | ] { 175 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(s1)).unwrap(); 176 | 177 | assert_sqrt!(s1 as &[Word], &qb, &rb, MAX_BUF, "max"); 178 | } 179 | 180 | for s1 in [ 181 | &[WORD_SIGNIFICANT_BIT] as &[Word], 182 | &[0, WORD_SIGNIFICANT_BIT], 183 | &[0, 0, WORD_SIGNIFICANT_BIT], 184 | &[0, 0, 0, WORD_SIGNIFICANT_BIT], 185 | &[0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 186 | &[0, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 187 | &[0, 0, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 188 | &[0, 0, 0, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 189 | ] { 190 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(s1)).unwrap(); 191 | 192 | assert_sqrt!(s1 as &[Word], &qb, &rb, MAX_BUF, "significant"); 193 | } 194 | 195 | for s1 in [ 196 | &[WORD_SIGNIFICANT_BIT + 1] as &[Word], 197 | &[1, WORD_SIGNIFICANT_BIT], 198 | &[1, 0, WORD_SIGNIFICANT_BIT], 199 | &[1, 0, 0, WORD_SIGNIFICANT_BIT], 200 | &[1, 0, 0, 0, WORD_SIGNIFICANT_BIT], 201 | &[1, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 202 | &[1, 0, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 203 | &[1, 0, 0, 0, 0, 0, 0, WORD_SIGNIFICANT_BIT], 204 | ] { 205 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(s1)).unwrap(); 206 | 207 | assert_sqrt!(s1 as &[Word], &qb, &rb, MAX_BUF, "lsb + msb"); 208 | } 209 | 210 | for s1 in [ 211 | &[1 as Word] as &[Word], 212 | &[1, 2], 213 | &[1, 0, 3], 214 | &[1, 0, 0, 4], 215 | &[1, 0, 0, 0, 5], 216 | &[1, 0, 0, 0, 0, 6], 217 | &[1, 0, 0, 0, 0, 0, 7], 218 | &[1, 0, 0, 0, 0, 0, 0, 8], 219 | ] { 220 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(s1)).unwrap(); 221 | 222 | assert_sqrt!(s1 as &[Word], &qb, &rb, MAX_BUF, "subnormal"); 223 | } 224 | 225 | for _ in 0..1000 { 226 | let s1 = random_normalized_slice(1, MAX_BUF); 227 | 228 | let (qb, rb) = Mantissa::cbrt_rem(wordbuf_from_words(&s1)).unwrap(); 229 | 230 | //println!("\n{:?}\n{:?}\n{:?}", s1, qb, rb); 231 | 232 | assert_sqrt!(&s1 as &[Word], &qb, &rb, MAX_BUF, "rand"); 233 | } 234 | } 235 | 236 | fn random_normalized_slice(min_len: usize, max_len: usize) -> WordBuf { 237 | let l = if max_len > min_len { 238 | random::() % (max_len - min_len) + min_len 239 | } else { 240 | min_len 241 | }; 242 | 243 | let mut s1 = WordBuf::new(l).unwrap(); 244 | 245 | for v in s1.iter_mut() { 246 | *v = random(); 247 | } 248 | 249 | let l = s1.len(); 250 | s1[l - 1] |= WORD_SIGNIFICANT_BIT; 251 | s1 252 | } 253 | 254 | #[ignore] 255 | #[test] 256 | #[cfg(feature = "std")] 257 | fn cbrt_perf() { 258 | for _ in 0..5 { 259 | let sz = 34; 260 | let mut n = vec![]; 261 | let l = 10000; 262 | for _ in 0..l { 263 | let v = random_normalized_slice(sz, sz); 264 | n.push(v); 265 | } 266 | 267 | let start_time = std::time::Instant::now(); 268 | for ni in n.drain(..) { 269 | Mantissa::cbrt_rem(ni).unwrap(); 270 | } 271 | 272 | let time = start_time.elapsed(); 273 | println!("cbrt {}", time.as_millis()); 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /astro-float-num/src/mantissa/mod.rs: -------------------------------------------------------------------------------- 1 | //! Everything related to mantissa. 2 | 3 | mod cbrt; 4 | mod conv; 5 | mod div; 6 | mod fft; 7 | #[allow(clippy::module_inception)] 8 | mod mantissa; 9 | mod mul; 10 | mod sqrt; 11 | mod toom2; 12 | mod toom3; 13 | mod util; 14 | 15 | pub use mantissa::Mantissa; 16 | -------------------------------------------------------------------------------- /astro-float-num/src/mantissa/mul.rs: -------------------------------------------------------------------------------- 1 | //! Multiplication algos. 2 | 3 | use crate::common::buf::WordBuf; 4 | use crate::common::int::SliceWithSign; 5 | use crate::defs::DoubleWord; 6 | use crate::defs::Error; 7 | use crate::defs::Word; 8 | use crate::defs::WORD_BIT_SIZE; 9 | use crate::mantissa::Mantissa; 10 | 11 | impl Mantissa { 12 | pub(super) fn mul_basic(m1: &[Word], m2: &[Word], m3: &mut [Word]) { 13 | m3.fill(0); 14 | 15 | for (i, d1mi) in m1.iter().enumerate() { 16 | let d1mi = *d1mi as DoubleWord; 17 | if d1mi == 0 { 18 | continue; 19 | } 20 | 21 | let mut k = 0; 22 | for (m2j, m3ij) in m2.iter().zip(m3[i..].iter_mut()) { 23 | let m = d1mi * (*m2j as DoubleWord) + *m3ij as DoubleWord + k; 24 | *m3ij = m as Word; 25 | k = m >> (WORD_BIT_SIZE); 26 | } 27 | 28 | m3[i + m2.len()] += k as Word; 29 | } 30 | } 31 | 32 | fn mul_slices(m1: &[Word], m2: &[Word], m3: &mut [Word]) -> Result<(), Error> { 33 | debug_assert!(m1.len() <= m2.len()); 34 | 35 | if m1.len() <= 32 || m2.len() <= 32 { 36 | Self::mul_basic(m1, m2, m3); 37 | } else if m1.len() <= 220 || m2.len() <= 220 { 38 | Self::toom2(m1, m2, m3)?; 39 | } else if m1.len() <= 5400 && m2.len() <= 5400 { 40 | Self::toom3(m1, m2, m3)?; 41 | } else { 42 | Mantissa::fft_mul(m1, m2, m3)?; 43 | } 44 | Ok(()) 45 | } 46 | 47 | // general case multiplication 48 | pub(super) fn mul_unbalanced(m1: &[Word], m2: &[Word], m3: &mut [Word]) -> Result<(), Error> { 49 | let (sm, lg) = if m1.len() < m2.len() { (m1, m2) } else { (m2, m1) }; 50 | 51 | if lg.len() / 2 >= sm.len() && sm.len() > 70 { 52 | // balancing 53 | 54 | let mut buf = WordBuf::new(2 * sm.len())?; 55 | let mut even = true; 56 | let mut lb = 0; 57 | let mut ub = 0; 58 | 59 | for _ in 0..2 { 60 | while lb < lg.len() { 61 | ub = if lb + sm.len() <= lg.len() { lb + sm.len() } else { lg.len() }; 62 | 63 | Self::mul_slices(&lg[lb..ub], sm, &mut buf)?; 64 | 65 | let src = SliceWithSign::new(&buf[..ub - lb + sm.len()], 1); 66 | let mut dst = SliceWithSign::new_mut(&mut m3[lb..], 1); 67 | 68 | if even { 69 | dst.copy_from(&src); 70 | } else { 71 | dst.add_assign(&src); 72 | } 73 | 74 | lb += sm.len() * 2; 75 | } 76 | 77 | if even { 78 | if ub + sm.len() < m3.len() { 79 | m3[ub + sm.len()..].fill(0); 80 | } 81 | 82 | even = false; 83 | lb = sm.len(); 84 | } 85 | } 86 | 87 | Ok(()) 88 | } else { 89 | Self::mul_slices(sm, lg, m3) 90 | } 91 | } 92 | 93 | // short multiplication 94 | #[allow(dead_code)] // TODO: can it be faster than mul_unbalanced by more than 90% ? 95 | pub(super) fn mul_short(m1: &[Word], m2: &[Word], m3: &mut [Word]) -> Result<(), Error> { 96 | debug_assert!(m1.len() == m2.len()); // TODO: consider relaxing this 97 | let n = m1.len(); 98 | Self::mul_short_step(m1, m2, m3, n) 99 | } 100 | 101 | // short multiplication 102 | fn mul_short_step(m1: &[Word], m2: &[Word], m3: &mut [Word], n: usize) -> Result<(), Error> { 103 | if n <= 10 { 104 | Self::mul_unbalanced(m1, m2, m3)?; 105 | 106 | let mut c1 = SliceWithSign::new_mut(m3, 1); 107 | c1.shift_right(n * WORD_BIT_SIZE); 108 | } else { 109 | let k = n * 775 / 1000; 110 | let l = n - k; 111 | 112 | let a1 = SliceWithSign::new(&m1[l..], 1); // m1 div 2^l 113 | let a2 = SliceWithSign::new(&m1[..l], 1); // m1 mod 2^l 114 | let a3 = SliceWithSign::new(&m1[k..], 1); // m1 div 2^k 115 | 116 | let b1 = SliceWithSign::new(&m2[l..], 1); // m2 div 2^l 117 | let b2 = SliceWithSign::new(&m2[..l], 1); // m2 mod 2^l 118 | let b3 = SliceWithSign::new(&m2[k..], 1); // m2 div 2^k 119 | 120 | Self::mul_unbalanced(&a1, &b1, m3)?; 121 | 122 | let mut c1 = SliceWithSign::new_mut(m3, 1); 123 | c1.shift_right((k - l) * WORD_BIT_SIZE); 124 | 125 | let mut tmp_buf = WordBuf::new(a2.len() + b3.len() + a3.len() + b2.len())?; 126 | let (buf1, buf2) = tmp_buf.split_at_mut(a2.len() + b3.len()); 127 | 128 | Self::mul_short_step(&a2, &b3, buf1, l)?; 129 | let c2 = SliceWithSign::new(buf1, 1); 130 | 131 | Self::mul_short_step(&a3, &b2, buf2, l)?; 132 | let c3 = SliceWithSign::new(buf2, 1); 133 | 134 | c1.add_assign(&c2); 135 | c1.add_assign(&c3); 136 | } 137 | 138 | Ok(()) 139 | } 140 | } 141 | 142 | #[cfg(test)] 143 | mod tests { 144 | 145 | use super::*; 146 | use crate::defs::WORD_MAX; 147 | use rand::random; 148 | 149 | #[cfg(not(feature = "std"))] 150 | use alloc::vec::Vec; 151 | 152 | #[test] 153 | fn test_mul_unbalanced() { 154 | let sz1 = random::() % 10 + 1; 155 | let sz2 = random::() % 10 * sz1 + random::() % sz1 + sz1; 156 | let f = random_slice(1, sz1); 157 | let mut ret1 = WordBuf::new(sz1 + sz2).unwrap(); 158 | let mut ret2 = WordBuf::new(sz1 + sz2).unwrap(); 159 | for _ in 0..1000 { 160 | let v = random_slice(sz1, sz2); 161 | Mantissa::mul_unbalanced(&f, &v, &mut ret1).unwrap(); 162 | Mantissa::mul_slices(&f, &v, &mut ret2).unwrap(); 163 | assert!(ret1[..] == ret2[..]); 164 | } 165 | } 166 | 167 | #[ignore] 168 | #[test] 169 | fn test_mul_short() { 170 | let s1 = [1, 2, 3, 4]; 171 | let s2 = [1, 2, 3, 4]; 172 | let mut s3 = [0, 0, 0, 0, 0, 0, 0, 0]; 173 | 174 | Mantissa::mul_short(&s1, &s2, &mut s3).unwrap(); 175 | 176 | assert!(s3 == [25, 24, 16, 0, 0, 0, 0, 0]); 177 | 178 | let s1 = [ 179 | 1496867450, 1417658947, 3271802710, 2677751033, 3237139020, 3064555062, 1548441171, 180 | 778455770, 2436515277, 483318499, 181 | ]; 182 | let s2 = [ 183 | 3225363533, 3760565749, 1879799765, 4055875449, 305072033, 1248705486, 102752588, 184 | 2971455321, 1010393078, 2764359410, 185 | ]; 186 | 187 | let mut ret = WordBuf::new(20).unwrap(); 188 | Mantissa::mul_short(&s1, &s2, &mut ret).unwrap(); 189 | 190 | let mut s3 = WordBuf::new(20).unwrap(); 191 | Mantissa::mul_unbalanced(&s1, &s2, &mut s3).unwrap(); 192 | 193 | ret[0] &= WORD_MAX << 10; // 10 = ceil(log2(3*(p-1))) 194 | s3[10] &= WORD_MAX << 10; 195 | assert!(ret[..10] == s3[10..]); 196 | } 197 | 198 | #[ignore] 199 | #[test] 200 | #[cfg(feature = "std")] 201 | fn test_mul_short_perf() { 202 | for _ in 0..5 { 203 | let sz1 = 1000; 204 | let sz2 = 1000; 205 | let f = random_slice(sz1, sz1); 206 | let mut ret = WordBuf::new(sz1 + sz2).unwrap(); 207 | let mut n = vec![]; 208 | let l = 1000; 209 | for _ in 0..l { 210 | let v = random_slice(sz2, sz2); 211 | n.push(v); 212 | } 213 | 214 | // basic 215 | let start_time = std::time::Instant::now(); 216 | for ni in &n { 217 | Mantissa::mul_unbalanced(&f, ni, &mut ret).unwrap(); 218 | } 219 | let time = start_time.elapsed(); 220 | println!("mul_slices {}", time.as_millis()); 221 | 222 | // short 223 | let start_time = std::time::Instant::now(); 224 | for ni in &n { 225 | Mantissa::mul_short(&f, ni, &mut ret).unwrap(); 226 | } 227 | let time = start_time.elapsed(); 228 | println!("mul_short {}", time.as_millis()); 229 | } 230 | } 231 | 232 | fn random_slice(min_len: usize, max_len: usize) -> Vec { 233 | let mut s1 = Vec::new(); 234 | let l = if max_len > min_len { 235 | random::() % (max_len - min_len) + min_len 236 | } else { 237 | min_len 238 | }; 239 | for _ in 0..l { 240 | s1.push(random()); 241 | } 242 | s1 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /astro-float-num/src/mantissa/toom2.rs: -------------------------------------------------------------------------------- 1 | //! Karatsuba multiplication. 2 | 3 | use crate::common::buf::WordBuf; 4 | use crate::common::util::add_carry; 5 | use crate::defs::DoubleWord; 6 | use crate::defs::Error; 7 | use crate::defs::Word; 8 | use crate::defs::WORD_BASE; 9 | use crate::mantissa::Mantissa; 10 | use itertools::izip; 11 | 12 | impl Mantissa { 13 | fn add_slices(s1: &[Word], s2: &[Word], s3: &mut [Word]) { 14 | let mut c = 0; 15 | 16 | let (mut iter1, mut iter2) = if s1.len() > s2.len() { 17 | (s1.iter(), s2.iter()) 18 | } else { 19 | (s2.iter(), s1.iter()) 20 | }; 21 | let mut iter3 = s3.iter_mut(); 22 | 23 | for (a, b, x) in izip!(iter2.by_ref(), iter1.by_ref(), iter3.by_ref()) { 24 | c = add_carry(*a, *b, c, x); 25 | } 26 | 27 | for (a, x) in iter1.zip(iter3.by_ref()) { 28 | c = add_carry(*a, 0, c, x); 29 | } 30 | 31 | *iter3.next().unwrap() = c as Word; // s3 is supposed to be longer than s1 and s2 to process carry. 32 | } 33 | 34 | fn paired_sub(s1: &[Word], s2: &[Word], s3: &mut [Word]) { 35 | let mut c = 0; 36 | let mut iter3 = s3.iter_mut(); 37 | 38 | let (mut iter1, iter2) = if s1.len() > s2.len() { 39 | (s1.iter(), s2.iter()) 40 | } else { 41 | (s2.iter(), s1.iter()) 42 | }; 43 | 44 | for (a, b, x) in izip!(iter2, iter1.by_ref(), iter3.by_ref()) { 45 | let v = *x as DoubleWord; 46 | let s = *a as DoubleWord + *b as DoubleWord + c; 47 | 48 | if v >= s { 49 | *x = (v - s) as Word; 50 | c = 0; 51 | } else if v + WORD_BASE >= s { 52 | *x = (v + WORD_BASE - s) as Word; 53 | c = 1; 54 | } else { 55 | *x = (v + WORD_BASE * 2 - s) as Word; 56 | c = 2; 57 | } 58 | } 59 | 60 | for (a, x) in iter1.zip(iter3.by_ref()) { 61 | let v = *x as DoubleWord; 62 | let s = *a as DoubleWord + c; 63 | 64 | if v >= s { 65 | *x = (v - s) as Word; 66 | c = 0; 67 | } else if v + WORD_BASE >= s { 68 | *x = (v + WORD_BASE - s) as Word; 69 | c = 1; 70 | } else { 71 | *x = (v + WORD_BASE * 2 - s) as Word; 72 | c = 2; 73 | } 74 | } 75 | 76 | if c > 0 { 77 | for x in iter3 { 78 | let v = *x as DoubleWord; 79 | let s = c; 80 | 81 | if v >= s { 82 | *x = (v - s) as Word; 83 | c = 0; 84 | } else if v + WORD_BASE >= s { 85 | *x = (v + WORD_BASE - s) as Word; 86 | c = 1; 87 | } else { 88 | *x = (v + WORD_BASE * 2 - s) as Word; 89 | c = 2; 90 | } 91 | } 92 | } 93 | } 94 | 95 | fn add_assign_slices(s1: &mut [Word], s2: &[Word]) { 96 | let mut c = 0; 97 | 98 | let mut s3iter = s1.iter_mut(); 99 | for (a, b) in s2.iter().zip(s3iter.by_ref()) { 100 | c = add_carry(*a, *b, c, b); 101 | } 102 | 103 | if c > 0 { 104 | for b in s3iter { 105 | c = add_carry(0, *b, c, b); 106 | } 107 | } 108 | } 109 | 110 | pub(super) fn toom2(m1: &[Word], m2: &[Word], m3: &mut [Word]) -> Result<(), Error> { 111 | let n = (m1.len().min(m2.len()) + 1) >> 1; 112 | let n2 = n << 1; 113 | 114 | let (m11, m12) = m1.split_at(n); 115 | let (m21, m22) = m2.split_at(n); 116 | let (m31, m32) = m3.split_at_mut(n2); 117 | 118 | let x1l = m11.len().max(m12.len()) + 1; 119 | let x2l = m21.len().max(m22.len()) + 1; 120 | let buf_sz = (x1l + x2l) * 2; 121 | 122 | let mut buf_holder1; 123 | let mut buf_holder2; 124 | let buf = if buf_sz <= 256 { 125 | buf_holder2 = [0; 256]; 126 | &mut buf_holder2[..buf_sz] 127 | } else { 128 | buf_holder1 = WordBuf::new(buf_sz)?; 129 | &mut buf_holder1 130 | }; 131 | 132 | let (x1, rest) = buf.split_at_mut(x1l); 133 | let (x2, rest) = rest.split_at_mut(x2l); 134 | let z2buf = rest; 135 | 136 | Self::add_slices(m11, m12, x1); 137 | Self::add_slices(m21, m22, x2); 138 | 139 | Self::mul_unbalanced(x1, x2, z2buf)?; 140 | Self::mul_unbalanced(m11, m21, m31)?; 141 | Self::mul_unbalanced(m12, m22, m32)?; 142 | 143 | Self::paired_sub(m31, m32, z2buf); 144 | Self::add_assign_slices(&mut m3[n..], z2buf); 145 | 146 | Ok(()) 147 | } 148 | } 149 | 150 | #[cfg(test)] 151 | mod tests { 152 | 153 | use super::*; 154 | use crate::defs::{DoubleWord, WORD_BIT_SIZE}; 155 | use rand::random; 156 | 157 | #[cfg(not(feature = "std"))] 158 | use alloc::vec::Vec; 159 | 160 | #[test] 161 | fn test_toom2() { 162 | // d1*d2 163 | let s1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; 164 | let s2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; 165 | let mut ref_s = [0; 36]; 166 | mul(&s1, &s2, &mut ref_s); 167 | 168 | let mut ret_s = [0; 36]; 169 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 170 | 171 | assert!(ret_s == ref_s); 172 | 173 | // 999..99 * 999..99 174 | let s1 = [Word::MAX; 8]; 175 | let s2 = [Word::MAX; 7]; 176 | let mut ref_s = [0; 15]; 177 | mul(&s1, &s2, &mut ref_s); 178 | 179 | let mut ret_s = [0; 15]; 180 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 181 | 182 | assert!(ret_s == ref_s); 183 | 184 | // d1*d2 185 | let s1 = [1, 2, 3, 4, 5, 6]; 186 | let s2 = [0, 1, 2, 3, 4, 5, 6]; 187 | let mut ref_s = [0; 13]; 188 | mul(&s1, &s2, &mut ref_s); 189 | 190 | let mut ret_s = [0; 13]; 191 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 192 | 193 | assert!(ret_s == ref_s); 194 | 195 | // 0*0 196 | let s1 = [0, 0, 0, 0, 0, 0, 0, 0]; 197 | let s2 = [0, 0, 0, 0, 0, 0, 0]; 198 | let mut ref_s = [0; 15]; 199 | mul(&s1, &s2, &mut ref_s); 200 | 201 | let mut ret_s = [0; 15]; 202 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 203 | 204 | assert!(ret_s == ref_s); 205 | 206 | // 1*1 207 | let s1 = [1, 0, 0, 0, 0, 0, 0, 0]; 208 | let s2 = [1, 0, 0, 0, 0, 0, 0]; 209 | let mut ref_s = [0; 15]; 210 | mul(&s1, &s2, &mut ref_s); 211 | 212 | let mut ret_s = [0; 15]; 213 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 214 | 215 | assert!(ret_s == ref_s); 216 | 217 | // random 218 | for _ in 0..1000 { 219 | let s1 = random_slice(80, 300); 220 | let s2 = random_slice(80, 300); 221 | 222 | //println!("s1 {:?}", s1); 223 | //println!("s2 {:?}", s2); 224 | 225 | let mut ref_s = Vec::new(); 226 | ref_s.resize(s1.len() + s2.len(), 0); 227 | mul(&s1, &s2, &mut ref_s); 228 | 229 | let mut ret_s = Vec::new(); 230 | ret_s.resize(s1.len() + s2.len(), 0); 231 | Mantissa::toom2(&s1, &s2, &mut ret_s).unwrap(); 232 | 233 | assert!(ret_s == ref_s); 234 | } 235 | } 236 | 237 | #[ignore] 238 | #[test] 239 | #[cfg(feature = "std")] 240 | fn toom2_perf() { 241 | for _ in 0..5 { 242 | let sz = 34; 243 | let f = random_slice(sz, sz); 244 | let mut n = vec![]; 245 | let mut ret = vec![]; 246 | ret.resize(sz + f.len(), 0); 247 | let l = 100000; 248 | for _ in 0..l { 249 | let v = random_slice(sz, sz); 250 | n.push(v); 251 | } 252 | 253 | let start_time = std::time::Instant::now(); 254 | for ni in n.iter() { 255 | Mantissa::toom2(ni, &f, &mut ret).unwrap(); 256 | } 257 | let time = start_time.elapsed(); 258 | println!("toom2 {}", time.as_millis()); 259 | 260 | let start_time = std::time::Instant::now(); 261 | for ni in n.iter() { 262 | mul(ni, &f, &mut ret); 263 | } 264 | let time = start_time.elapsed(); 265 | println!("mul {}", time.as_millis()); 266 | } 267 | } 268 | 269 | fn random_slice(min_len: usize, max_len: usize) -> Vec { 270 | let mut s1 = Vec::new(); 271 | let l = if max_len > min_len { 272 | random::() % (max_len - min_len) + min_len 273 | } else { 274 | min_len 275 | }; 276 | for _ in 0..l { 277 | s1.push(random()); 278 | } 279 | s1 280 | } 281 | 282 | fn mul(s1: &[Word], s2: &[Word], ret: &mut [Word]) { 283 | ret.fill(0); 284 | for (i, d1mi) in s1.iter().enumerate() { 285 | let d1mi = *d1mi as DoubleWord; 286 | if d1mi == 0 { 287 | continue; 288 | } 289 | 290 | let mut k = 0; 291 | for (m2j, m3ij) in s2.iter().zip(ret[i..].iter_mut()) { 292 | let m = d1mi * (*m2j as DoubleWord) + *m3ij as DoubleWord + k; 293 | 294 | *m3ij = m as Word; 295 | k = m >> (WORD_BIT_SIZE); 296 | } 297 | ret[i + s2.len()] += k as Word; 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /astro-float-num/src/mantissa/util.rs: -------------------------------------------------------------------------------- 1 | //! Auxiliary structures. 2 | 3 | use crate::{ 4 | common::{buf::WordBuf, util::log2_floor}, 5 | defs::WORD_SIGNIFICANT_BIT, 6 | Error, Word, WORD_BIT_SIZE, 7 | }; 8 | 9 | /// Length of the slice extended by extra size. 10 | pub struct ExtendedSlice 11 | where 12 | V: Iterator, 13 | { 14 | s: V, 15 | extra: usize, 16 | default: T, 17 | } 18 | 19 | impl ExtendedSlice 20 | where 21 | V: Iterator, 22 | { 23 | pub fn new(s: V, extra: usize, default: T) -> Self { 24 | ExtendedSlice { s, extra, default } 25 | } 26 | } 27 | 28 | impl Iterator for ExtendedSlice 29 | where 30 | V: Iterator, 31 | T: Copy + Clone, 32 | { 33 | type Item = T; 34 | 35 | fn next(&mut self) -> Option { 36 | if self.extra > 0 { 37 | self.extra -= 1; 38 | Some(self.default) 39 | } else { 40 | self.s.next() 41 | } 42 | } 43 | } 44 | 45 | /// Slice values shifted to the right by the specified amount of bits. 46 | pub struct RightShiftedSlice<'a, T> { 47 | s: core::slice::Iter<'a, T>, 48 | bits: usize, 49 | prev: Option, 50 | default: T, 51 | extend_sz: usize, 52 | } 53 | 54 | impl<'a, T> RightShiftedSlice<'a, T> 55 | where 56 | T: Copy + Clone, 57 | { 58 | pub fn new(s: &'a [T], shift: usize, default: T, mut extend_sz: usize) -> Self { 59 | let elsz = core::mem::size_of::() * 8; 60 | let mut idx = shift / elsz; 61 | let bits = shift % elsz; 62 | 63 | if extend_sz > idx { 64 | extend_sz -= idx; 65 | idx = 0; 66 | } else { 67 | idx -= extend_sz; 68 | extend_sz = 0; 69 | } 70 | 71 | let mut prev = None; 72 | let iter = if idx < s.len() { 73 | let mut iter = s[idx..].iter(); 74 | prev = iter.next().copied(); 75 | iter 76 | } else { 77 | [].iter() 78 | }; 79 | 80 | RightShiftedSlice { 81 | s: iter, 82 | bits, 83 | prev, 84 | default, 85 | extend_sz, 86 | } 87 | } 88 | } 89 | 90 | impl<'a, T> Iterator for RightShiftedSlice<'a, T> 91 | where 92 | T: core::ops::Shl 93 | + core::ops::Shr 94 | + core::ops::BitOr 95 | + Copy 96 | + Clone, 97 | { 98 | type Item = T; 99 | 100 | fn next(&mut self) -> Option { 101 | if self.extend_sz > 0 { 102 | self.extend_sz -= 1; 103 | if self.extend_sz == 0 && self.bits > 0 { 104 | if let Some(hi) = self.prev { 105 | Some(hi << (core::mem::size_of::() * 8 - self.bits)) 106 | } else { 107 | Some(self.default) 108 | } 109 | } else { 110 | Some(self.default) 111 | } 112 | } else if let Some(lo) = self.prev { 113 | self.prev = self.s.next().copied(); 114 | if self.bits == 0 { 115 | Some(lo) 116 | } else if let Some(hi) = self.prev { 117 | Some((hi << (core::mem::size_of::() * 8 - self.bits)) | (lo >> self.bits)) 118 | } else { 119 | Some(lo >> self.bits) 120 | } 121 | } else { 122 | Some(self.default) 123 | } 124 | } 125 | } 126 | 127 | // Represent subnormal as normalized. 128 | pub struct NormalizedView 129 | where 130 | T: Iterator, 131 | { 132 | iter: T, 133 | shift: usize, 134 | prev: Word, 135 | end: bool, 136 | } 137 | 138 | impl NormalizedView 139 | where 140 | T: Iterator, 141 | { 142 | pub fn new(mut iter: T) -> Self { 143 | let mut shift = 0; 144 | let mut end = true; 145 | let mut prev = 0; 146 | 147 | for mut v in iter.by_ref() { 148 | if v != 0 { 149 | prev = v; 150 | while v < WORD_SIGNIFICANT_BIT { 151 | v <<= 1; 152 | shift += 1; 153 | } 154 | end = false; 155 | break; 156 | } 157 | } 158 | 159 | Self { 160 | iter, 161 | shift, 162 | prev, 163 | end, 164 | } 165 | } 166 | } 167 | 168 | impl Iterator for NormalizedView 169 | where 170 | T: Iterator, 171 | { 172 | type Item = Word; 173 | 174 | fn next(&mut self) -> Option { 175 | if !self.end { 176 | if self.shift == 0 { 177 | let ret = self.prev; 178 | if let Some(v) = self.iter.next() { 179 | self.prev = v; 180 | } else { 181 | self.end = true; 182 | }; 183 | Some(ret) 184 | } else { 185 | let mut ret = self.prev << self.shift; 186 | if let Some(v) = self.iter.next() { 187 | ret |= v >> (WORD_BIT_SIZE - self.shift); 188 | self.prev = v; 189 | } else { 190 | self.end = true; 191 | }; 192 | Some(ret) 193 | } 194 | } else { 195 | None 196 | } 197 | } 198 | } 199 | 200 | /* #[inline] 201 | fn bitle(mut v: Word) -> usize { 202 | let mut bitle = 0; 203 | while v > 0 { 204 | v >>= 1; 205 | bitle += 1; 206 | } 207 | bitle 208 | } 209 | 210 | /// Prepare an initial value for calculating n-root of an argument m. 211 | pub fn root_estimate(m: &[Word], n: usize) -> Result { 212 | debug_assert!(!m.is_empty()); 213 | debug_assert!(*m.last().unwrap() != 0); 214 | 215 | let last = *m.last().unwrap(); 216 | 217 | if m.len() < 2 { 218 | let mut buf = WordBuf::new(1)?; 219 | buf[0] = nroot_int(last, n) + 1; 220 | return Ok(buf); 221 | } 222 | 223 | let last_bits = bitle(last); 224 | let shift_bits = WORD_BIT_SIZE - last_bits; 225 | let all_bits = (m.len() - 1) * WORD_BIT_SIZE + last_bits; 226 | 227 | let mut val = m[m.len() - 1] << shift_bits | if last_bits < WORD_BIT_SIZE { m[m.len() - 2] >> last_bits } else { 0 }; 228 | 229 | let sub_bits = all_bits % n; 230 | let zero_bits = all_bits / n + usize::from(sub_bits != 0); 231 | 232 | val >>= sub_bits; 233 | 234 | let root = if val == WORD_MAX { 235 | 1 << (WORD_BIT_SIZE / 2) 236 | } else { 237 | nroot_int(val + 1, n) 238 | }; 239 | 240 | let root_bits = bitle(root); 241 | let l = root_bits + zero_bits; 242 | 243 | let mut buf = WordBuf::new((l + WORD_BIT_SIZE - 1) / WORD_BIT_SIZE)?; 244 | buf.fill(0); 245 | 246 | let idx = zero_bits / WORD_BIT_SIZE; 247 | let shift_bits = zero_bits % WORD_BIT_SIZE; 248 | 249 | buf[idx] = root << shift_bits; 250 | if root_bits > WORD_BIT_SIZE - shift_bits { 251 | buf[idx + 1] = root >> (WORD_BIT_SIZE - shift_bits); 252 | } 253 | 254 | Ok(buf) 255 | } 256 | */ 257 | 258 | /// Prepare an initial value for calculating n-root of an argument m. 259 | pub fn root_estimate(m: &[Word], n: usize) -> Result { 260 | let mut buf = WordBuf::new(m.len() / n + 1)?; 261 | 262 | if buf.len() > 0 { 263 | buf.fill(0); 264 | 265 | let nbits = log2_floor(*m.last().unwrap() as usize); 266 | 267 | *buf.last_mut().unwrap() = 1 << (nbits / n + 1); // buf.len() > 0 268 | } 269 | Ok(buf) 270 | } 271 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/acos.rs: -------------------------------------------------------------------------------- 1 | //! Arccosine. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::round_p; 5 | use crate::defs::Error; 6 | use crate::defs::RoundingMode; 7 | use crate::num::BigFloatNumber; 8 | use crate::ops::consts::Consts; 9 | use crate::Exponent; 10 | use crate::Sign; 11 | use crate::WORD_BIT_SIZE; 12 | 13 | const ACOS_EXP_THRES: Exponent = -32; 14 | 15 | impl BigFloatNumber { 16 | /// Computes the arccosine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 17 | /// This function requires constants cache `cc` for computing the result. 18 | /// Precision is rounded upwards to the word size. 19 | /// 20 | /// ## Errors 21 | /// 22 | /// - InvalidArgument: argument is greater than 1 or smaller than -1, or the precision is incorrect. 23 | /// - MemoryAllocation: failed to allocate memory. 24 | pub fn acos(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 25 | let p = round_p(p); 26 | 27 | let cmpone = self.abs_cmp(&ONE); 28 | if cmpone == 0 && self.is_positive() { 29 | return Self::new2(p, Sign::Pos, self.inexact()); 30 | } else if cmpone > 0 { 31 | return Err(Error::InvalidArgument); 32 | } 33 | 34 | let mut p_inc = WORD_BIT_SIZE; 35 | let mut p_wrk = p.max(self.mantissa_max_bit_len()) + p_inc; 36 | 37 | let mut add_p = (1 - ACOS_EXP_THRES) as usize; 38 | 39 | let mut x = self.clone()?; 40 | x.set_inexact(false); 41 | 42 | loop { 43 | let p_x = p_wrk + add_p; 44 | x.set_precision(p_x, RoundingMode::None)?; 45 | 46 | let mut ret = x.asin(p_x, RoundingMode::None, cc)?; 47 | 48 | let mut pi = cc.pi_num(p_x, RoundingMode::None)?; 49 | pi.set_exponent(pi.exponent() - 1); 50 | 51 | let ret2 = pi.add(&ret, p_x, RoundingMode::None)?; 52 | ret = pi.sub(&ret, p_x, RoundingMode::None)?; 53 | 54 | let t = ret 55 | .exponent() 56 | .unsigned_abs() 57 | .max(ret2.exponent().unsigned_abs()) as usize 58 | + 1; // ret near pi / 2 gives cancellation 59 | if add_p < t { 60 | add_p = t; 61 | } else { 62 | if ret.try_set_precision(p, rm, p_wrk)? { 63 | ret.set_inexact(ret.inexact() | self.inexact()); 64 | return Ok(ret); 65 | } 66 | 67 | p_wrk += p_inc; 68 | p_inc = round_p(p_wrk / 5); 69 | } 70 | } 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | 77 | use crate::common::{consts::ONE, util::random_subnormal}; 78 | 79 | use super::*; 80 | 81 | #[test] 82 | fn test_arccosine() { 83 | let mut cc = Consts::new().unwrap(); 84 | 85 | let rm = RoundingMode::ToEven; 86 | let p = 64; 87 | let mut n1 = BigFloatNumber::from_word(4294967295, p).unwrap(); 88 | n1.set_exponent(0); 89 | //println!("{}", n1.format(crate::Radix::Dec, RoundingMode::None).unwrap()); 90 | let _n2 = n1.acos(p, rm, &mut cc).unwrap(); 91 | //println!("{:?}", n2.format(crate::Radix::Dec, rm).unwrap()); 92 | 93 | // near 1 94 | let p = 320; 95 | let n1 = BigFloatNumber::parse( 96 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2DC85F7E77EC487_e-1", 97 | crate::Radix::Hex, 98 | p, 99 | RoundingMode::None, 100 | &mut cc, 101 | ) 102 | .unwrap(); 103 | let n2 = n1.acos(p, rm, &mut cc).unwrap(); 104 | let n3 = BigFloatNumber::parse("5.2049C1114CF98E7B6DB49CCF999F4A5E697D73E5DA6BEC6578098357460BAFFB0C25779F1C63E8D8_e-21", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 105 | 106 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 107 | 108 | assert!(n2.cmp(&n3) == 0); 109 | 110 | // near zero 111 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e-10", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 112 | let n2 = n1.acos(p, rm, &mut cc).unwrap(); 113 | let n3 = BigFloatNumber::parse("1.921FB54442D18467F76D0FD2BEE6B538C877D6FA13175F455EB9961B4834A04EF790AE0274E87FF6_e+0", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 114 | 115 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 116 | 117 | assert!(n2.cmp(&n3) == 0); 118 | 119 | let d1 = BigFloatNumber::max_value(p).unwrap(); 120 | let d2 = BigFloatNumber::min_value(p).unwrap(); 121 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 122 | let zero = BigFloatNumber::new(1).unwrap(); 123 | 124 | let mut half_pi = cc.pi_num(p, RoundingMode::ToEven).unwrap(); 125 | half_pi.set_exponent(1); 126 | 127 | assert!(d1.acos(p, rm, &mut cc).is_err()); 128 | assert!(d2.acos(p, rm, &mut cc).is_err()); 129 | assert!(d3.acos(p, rm, &mut cc).unwrap().cmp(&half_pi) == 0); 130 | assert!(zero.acos(p, rm, &mut cc).unwrap().cmp(&half_pi) == 0); 131 | assert!(ONE.acos(p, rm, &mut cc).unwrap().is_zero()); 132 | 133 | // subnormal arg 134 | let n1 = random_subnormal(p); 135 | assert!(n1.acos(p, rm, &mut cc).unwrap().cmp(&half_pi) == 0); 136 | } 137 | 138 | #[ignore] 139 | #[test] 140 | #[cfg(feature = "std")] 141 | fn arccosine_perf() { 142 | let mut cc = Consts::new().unwrap(); 143 | let p = 133; 144 | 145 | let mut n = vec![]; 146 | for _ in 0..10000 { 147 | n.push(BigFloatNumber::random_normal(p, -5, 5).unwrap()); 148 | } 149 | 150 | for _ in 0..5 { 151 | let start_time = std::time::Instant::now(); 152 | for ni in n.iter() { 153 | let _f = ni.acos(p, RoundingMode::ToEven, &mut cc).unwrap(); 154 | } 155 | let time = start_time.elapsed(); 156 | println!("{}", time.as_millis()); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/acosh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic arccosine. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::count_leading_zeroes_skip_first; 5 | use crate::common::util::round_p; 6 | use crate::defs::Error; 7 | use crate::defs::RoundingMode; 8 | use crate::num::BigFloatNumber; 9 | use crate::Consts; 10 | use crate::Sign; 11 | use crate::EXPONENT_MAX; 12 | use crate::WORD_BIT_SIZE; 13 | 14 | impl BigFloatNumber { 15 | /// Computes the hyperbolic arccosine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 16 | /// This function requires constants cache `cc` for computing the result. 17 | /// Precision is rounded upwards to the word size. 18 | /// 19 | /// ## Errors 20 | /// 21 | /// - ExponentOverflow: the result is too large or too small number. 22 | /// - MemoryAllocation: failed to allocate memory. 23 | /// - InvalidArgument: when `self` < 1, or the precision is incorrect. 24 | pub fn acosh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 25 | let p = round_p(p); 26 | 27 | let cmpone = self.cmp(&ONE); 28 | if cmpone == 0 { 29 | return Self::new2(p, Sign::Pos, self.inexact()); 30 | } else if cmpone < 0 { 31 | return Err(Error::InvalidArgument); 32 | } 33 | 34 | let p_max = p.max(self.mantissa_max_bit_len()); 35 | 36 | let mut x = self.clone()?; 37 | x.set_inexact(false); 38 | 39 | let mut p_inc = WORD_BIT_SIZE; 40 | let mut p_wrk = p_max + p_inc; 41 | 42 | loop { 43 | let mut p_x = p_wrk; 44 | 45 | let mut ret = if (self.exponent() as isize - 1) / 2 > p_x as isize + 2 { 46 | // acosh(x) = ln(2*x) 47 | if self.exponent() == EXPONENT_MAX { 48 | // ln(2) + ln(x) 49 | p_x += 2; 50 | 51 | let lnx = x.ln(p_x, RoundingMode::None, cc)?; 52 | 53 | let ln2 = cc.ln_2_num(p_x, RoundingMode::None)?; 54 | 55 | ln2.add(&lnx, p_x, RoundingMode::None)? 56 | } else { 57 | x.set_exponent(self.exponent() + 1); 58 | 59 | let lnx = x.ln(p_x, RoundingMode::None, cc)?; 60 | 61 | x.set_exponent(self.exponent()); 62 | 63 | lnx 64 | } 65 | } else { 66 | // ln(x + sqrt(x*x - 1)) 67 | let mut additional_prec = 0; 68 | if self.exponent() == 1 { 69 | additional_prec = count_leading_zeroes_skip_first(self.mantissa().digits()); 70 | } 71 | 72 | p_x += 6 + additional_prec; 73 | 74 | let xx = x.mul(&x, p_x, RoundingMode::None)?; 75 | 76 | let d1 = xx.sub(&ONE, p_x, RoundingMode::None)?; 77 | 78 | let d2 = d1.sqrt(p_x, RoundingMode::None)?; 79 | 80 | let d3 = d2.add(&x, p_x, RoundingMode::FromZero)?; 81 | 82 | d3.ln(p_x, RoundingMode::None, cc)? 83 | }; 84 | 85 | if ret.try_set_precision(p, rm, p_wrk)? { 86 | ret.set_inexact(ret.inexact() | self.inexact()); 87 | return Ok(ret); 88 | } 89 | 90 | p_wrk += p_inc; 91 | p_inc = round_p(p_wrk / 5); 92 | } 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | 99 | use crate::Exponent; 100 | 101 | use super::*; 102 | 103 | #[test] 104 | fn test_acosh() { 105 | let mut cc = Consts::new().unwrap(); 106 | let rm = RoundingMode::ToEven; 107 | /* let n1 = BigFloatNumber::from_words(&[144, 9223372036854775808], Sign::Pos, 1).unwrap(); 108 | let n2 = n1.acosh(128, RoundingMode::Down, &mut cc).unwrap(); 109 | println!("{:?}", n2); 110 | //println!("{:?}", n2.format(crate::Radix::Bin, rm).unwrap()); 111 | return; */ 112 | 113 | // near 1 114 | let p = 448; 115 | let n1 = BigFloatNumber::parse("1.0000000000000000000000000000000000000000000B56A0EBA6F7D47E21A7B2A7806A698BABAF2F05BC61E2F8FB50FE0B98F55B181AC9C8_e+0", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 116 | let n2 = n1.acosh(p, rm, &mut cc).unwrap(); 117 | let n3 = BigFloatNumber::parse("4.C31368910963B1A1BCFC0EDBD393FB7A5E876F9751D93A20E7E48EC0D16090ADA5F46DF2184D32A19C500088EA09CBD4F23DF713113D8A58_e-16", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 118 | 119 | // println!("{:?}", n1.format(crate::Radix::Bin, rm).unwrap()); 120 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 121 | 122 | assert!(n2.cmp(&n3) == 0); 123 | 124 | // large exp 125 | let p = 320; 126 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e+1000", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 127 | let n2 = n1.acosh(p, rm, &mut cc).unwrap(); 128 | let n3 = BigFloatNumber::parse("2.C5DAB0AF9025886C3364C7B6D6741EB19D4FB009D3F92CA21B77498D9F0666363C665F2F324EAEC8_e+3", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 129 | 130 | //println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 131 | 132 | assert!(n2.cmp(&n3) == 0); 133 | 134 | let d1 = BigFloatNumber::max_value(p).unwrap(); 135 | let d2 = BigFloatNumber::min_value(p).unwrap(); 136 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 137 | 138 | // MAX 139 | let mut d4 = d1.acosh(p, RoundingMode::Down, &mut cc).unwrap(); 140 | 141 | d4.set_exponent(d4.exponent() - 1); 142 | let d5 = d4.cosh(p, rm, &mut cc).unwrap(); 143 | let mut d5 = d5.mul(&d5, p, rm).unwrap(); 144 | d5.set_exponent(d5.exponent() + 1); 145 | 146 | let mut eps = ONE.clone().unwrap(); 147 | eps.set_exponent( 148 | d1.exponent() - p as Exponent + core::mem::size_of::() as Exponent * 8, 149 | ); 150 | 151 | assert!( 152 | d1.sub(&d5, p, RoundingMode::ToEven) 153 | .unwrap() 154 | .abs() 155 | .unwrap() 156 | .cmp(&eps) 157 | < 0 158 | ); 159 | 160 | assert!(d2.acosh(p, rm, &mut cc).unwrap_err() == Error::InvalidArgument); 161 | assert!(d3.acosh(p, rm, &mut cc).unwrap_err() == Error::InvalidArgument); 162 | 163 | assert!(ONE.acosh(p, rm, &mut cc).unwrap().is_zero()); 164 | } 165 | 166 | #[ignore] 167 | #[test] 168 | #[cfg(feature = "std")] 169 | fn acosh_perf() { 170 | let mut cc = Consts::new().unwrap(); 171 | let mut n = vec![]; 172 | let p = 160; 173 | for _ in 0..10000 { 174 | n.push(BigFloatNumber::random_normal(p, 0, 5).unwrap()); 175 | } 176 | 177 | for _ in 0..5 { 178 | let start_time = std::time::Instant::now(); 179 | for ni in n.iter() { 180 | let _f = ni.acosh(p, RoundingMode::ToEven, &mut cc).unwrap(); 181 | } 182 | let time = start_time.elapsed(); 183 | println!("{}", time.as_millis()); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/asin.rs: -------------------------------------------------------------------------------- 1 | //! Arcsine. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::count_leading_ones; 5 | use crate::common::util::invert_rm_for_sign; 6 | use crate::common::util::round_p; 7 | use crate::defs::Error; 8 | use crate::defs::RoundingMode; 9 | use crate::num::BigFloatNumber; 10 | use crate::ops::consts::Consts; 11 | use crate::ops::util::compute_small_exp; 12 | use crate::WORD_BIT_SIZE; 13 | 14 | impl BigFloatNumber { 15 | /// Computes the arcsine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 16 | /// This function requires constants cache `cc` for computing the result. 17 | /// Precision is rounded upwards to the word size. 18 | /// 19 | /// ## Errors 20 | /// 21 | /// - InvalidArgument: argument is greater than 1 or smaller than -1, or the precision is incorrect. 22 | /// - MemoryAllocation: failed to allocate memory. 23 | pub fn asin(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 24 | let p = round_p(p); 25 | 26 | if self.is_zero() { 27 | return Self::new2(p, self.sign(), self.inexact()); 28 | } 29 | 30 | let onecmp = self.abs_cmp(&ONE); 31 | if onecmp > 0 { 32 | return Err(Error::InvalidArgument); 33 | } else if onecmp == 0 { 34 | let rm = if self.is_negative() { invert_rm_for_sign(rm) } else { rm }; 35 | 36 | let mut pi = cc.pi_num(p, rm)?; 37 | 38 | pi.set_exponent(pi.exponent() - 1); 39 | pi.set_sign(self.sign()); 40 | 41 | debug_assert!(pi.inexact()); 42 | 43 | return Ok(pi); 44 | } 45 | 46 | let mut p_inc = WORD_BIT_SIZE; 47 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 48 | 49 | compute_small_exp!(self, self.exponent() as isize * 2 - 2, false, p_wrk, p, rm); 50 | 51 | let mut additional_prec = 8; 52 | if self.exponent() == 0 { 53 | additional_prec += count_leading_ones(self.mantissa().digits()); 54 | } 55 | 56 | p_wrk += p_inc; 57 | 58 | let mut x = self.clone()?; 59 | x.set_inexact(false); 60 | 61 | loop { 62 | let p_x = p_wrk + additional_prec; 63 | x.set_precision(p_x, RoundingMode::None)?; 64 | 65 | // arcsin(x) = arctan(x / sqrt(1 - x^2)) 66 | let xx = x.mul(&x, p_x, RoundingMode::None)?; 67 | let t = ONE.sub(&xx, p_x, RoundingMode::None)?; 68 | let s = t.sqrt(p_x, RoundingMode::None)?; 69 | let d = x.div(&s, p_x, RoundingMode::None)?; 70 | 71 | let mut ret = d.atan(p_x, rm, cc)?; 72 | 73 | if ret.try_set_precision(p, rm, p_wrk)? { 74 | ret.set_inexact(ret.inexact() | self.inexact()); 75 | break Ok(ret); 76 | } 77 | 78 | p_wrk += p_inc; 79 | p_inc = round_p(p_wrk / 5); 80 | } 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | 87 | use crate::common::util::random_subnormal; 88 | 89 | use super::*; 90 | 91 | #[test] 92 | fn test_arcsine() { 93 | let mut cc = Consts::new().unwrap(); 94 | 95 | let rm = RoundingMode::ToEven; 96 | let p = 64; 97 | let mut n1 = BigFloatNumber::from_word(4294967295, p).unwrap(); 98 | n1.set_exponent(0); 99 | //println!("{}", n1.format(crate::Radix::Dec, RoundingMode::None).unwrap()); 100 | let _n2 = n1.asin(p, rm, &mut cc).unwrap(); 101 | //println!("{:?}", n2.format(crate::Radix::Dec, rm).unwrap()); 102 | 103 | // asymptotic & extrema testing 104 | let p = 320; 105 | let n1 = BigFloatNumber::parse( 106 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2DC85F7E77EC487_e-1", 107 | crate::Radix::Hex, 108 | p, 109 | RoundingMode::None, 110 | &mut cc, 111 | ) 112 | .unwrap(); 113 | let n2 = n1.asin(p, rm, &mut cc).unwrap(); 114 | let n3 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e+0", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 115 | 116 | assert!(n2.cmp(&n3) == 0); 117 | 118 | // near zero 119 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e-10", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 120 | let n2 = n1.asin(p, rm, &mut cc).unwrap(); 121 | let n3 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A2A55DE7312DF295F5AB0362F0A77F89C5756A9380CF056D90_e-10", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 122 | 123 | //println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 124 | 125 | assert!(n2.cmp(&n3) == 0); 126 | 127 | let d1 = BigFloatNumber::max_value(p).unwrap(); 128 | let d2 = BigFloatNumber::min_value(p).unwrap(); 129 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 130 | let zero = BigFloatNumber::new(1).unwrap(); 131 | 132 | let mut half_pi = cc.pi_num(p, RoundingMode::ToEven).unwrap(); 133 | half_pi.set_exponent(1); 134 | 135 | assert!(d1.asin(p, rm, &mut cc).is_err()); 136 | assert!(d2.asin(p, rm, &mut cc).is_err()); 137 | assert!(d3.asin(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 138 | assert!(zero.asin(p, rm, &mut cc).unwrap().is_zero()); 139 | assert!(ONE.asin(p, rm, &mut cc).unwrap().cmp(&half_pi) == 0); 140 | assert!( 141 | ONE.neg() 142 | .unwrap() 143 | .asin(p, rm, &mut cc) 144 | .unwrap() 145 | .cmp(&half_pi.neg().unwrap()) 146 | == 0 147 | ); 148 | 149 | // subnormal arg 150 | let n1 = random_subnormal(p); 151 | assert!(n1.asin(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 152 | } 153 | 154 | #[ignore] 155 | #[test] 156 | #[cfg(feature = "std")] 157 | fn arcsine_perf() { 158 | let mut cc = Consts::new().unwrap(); 159 | let p = 160; 160 | 161 | let mut n = vec![]; 162 | for _ in 0..10000 { 163 | n.push(BigFloatNumber::random_normal(p, -5, 0).unwrap()); 164 | } 165 | 166 | for _ in 0..5 { 167 | let start_time = std::time::Instant::now(); 168 | for ni in n.iter() { 169 | let _f = ni.asin(p, RoundingMode::ToEven, &mut cc).unwrap(); 170 | } 171 | let time = start_time.elapsed(); 172 | println!("{}", time.as_millis()); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/asinh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic arcsine. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::invert_rm_for_sign; 5 | use crate::common::util::round_p; 6 | use crate::defs::Error; 7 | use crate::defs::RoundingMode; 8 | use crate::num::BigFloatNumber; 9 | use crate::ops::util::compute_small_exp; 10 | use crate::Consts; 11 | use crate::Sign; 12 | use crate::EXPONENT_MAX; 13 | use crate::WORD_BIT_SIZE; 14 | 15 | impl BigFloatNumber { 16 | /// Computes the hyperbolic arcsine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 17 | /// This function requires constants cache `cc` for computing the result. 18 | /// Precision is rounded upwards to the word size. 19 | /// 20 | /// ## Errors 21 | /// 22 | /// - ExponentOverflow: the result is too large or too small number. 23 | /// - MemoryAllocation: failed to allocate memory. 24 | /// - InvalidArgument: the precision is incorrect. 25 | pub fn asinh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 26 | let p = round_p(p); 27 | 28 | if self.is_zero() { 29 | return Self::new2(p, self.sign(), self.inexact()); 30 | } 31 | 32 | let p_max = p.max(self.mantissa_max_bit_len()); 33 | compute_small_exp!(self, self.exponent() as isize * 2 - 2, true, p_max, p, rm); 34 | 35 | let mut x = self.clone()?; 36 | x.set_sign(Sign::Pos); 37 | x.set_inexact(false); 38 | 39 | let rm = if self.is_negative() { invert_rm_for_sign(rm) } else { rm }; 40 | 41 | let mut p_inc = WORD_BIT_SIZE; 42 | let mut p_wrk = p_max + p_inc; 43 | 44 | let mut val = loop { 45 | let mut p_x = p_wrk; 46 | 47 | let mut ret = if (self.exponent() as isize - 1) / 2 > p_x as isize + 2 { 48 | // asinh(x) = ln(2 * |x|) * signum(x) 49 | 50 | if self.exponent() == EXPONENT_MAX { 51 | // ln(2 * |x|) = ln(2) + ln(|x|) 52 | p_x += 2; 53 | 54 | let lnx = x.ln(p_x, RoundingMode::None, cc)?; 55 | 56 | let ln2 = cc.ln_2_num(p_x, RoundingMode::None)?; 57 | 58 | ln2.add(&lnx, p_x, RoundingMode::None)? 59 | } else { 60 | x.set_exponent(self.exponent() + 1); 61 | 62 | let lnx = x.ln(p_x, RoundingMode::None, cc)?; 63 | 64 | x.set_exponent(self.exponent()); 65 | 66 | lnx 67 | } 68 | } else { 69 | // ln(|x| + sqrt(x*x + 1)) * signum(x) 70 | p_x += self.exponent().unsigned_abs() as usize + 5; 71 | 72 | let xx = x.mul(&x, p_x, RoundingMode::None)?; 73 | 74 | let d1 = xx.add(&ONE, p_x, RoundingMode::None)?; 75 | 76 | let d2 = d1.sqrt(p_x, RoundingMode::None)?; 77 | 78 | let d3 = d2.add(&x, p_x, RoundingMode::FromZero)?; 79 | 80 | d3.ln(p_x, RoundingMode::None, cc)? 81 | }; 82 | 83 | if ret.try_set_precision(p, rm, p_wrk)? { 84 | ret.set_inexact(ret.inexact() | self.inexact()); 85 | break ret; 86 | } 87 | 88 | p_wrk += p_inc; 89 | p_inc = round_p(p_wrk / 5); 90 | }; 91 | 92 | val.set_sign(self.sign()); 93 | 94 | Ok(val) 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | 101 | use crate::{common::util::random_subnormal, Exponent}; 102 | 103 | use super::*; 104 | 105 | #[test] 106 | fn test_asinh() { 107 | let p = 320; 108 | let mut cc = Consts::new().unwrap(); 109 | let rm = RoundingMode::ToEven; 110 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 111 | n1.set_exponent(0); 112 | let _n2 = n1.asinh(p, rm, &mut cc).unwrap(); 113 | //println!("{:?}", n2.format(crate::Radix::Dec, rm).unwrap()); 114 | 115 | // near zero 116 | let n1 = BigFloatNumber::parse("-6.2625AC139402BE3D18693E64BFF93D122A9FB2295D654817665874F984C1D9E32B6C42F068F33020_e-13", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 117 | let n2 = n1.asinh(p, rm, &mut cc).unwrap(); 118 | let n3 = BigFloatNumber::parse("-6.2625AC139402BE3D18693E64BFF93D122A9F8B69858A068F649D78ADAF2C51C59A22D727857055D8_e-13", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 119 | 120 | // println!("{:?}", n1.format(crate::Radix::Hex, rm).unwrap()); 121 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 122 | 123 | assert!(n2.cmp(&n3) == 0); 124 | 125 | // large exp 126 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e+1000", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 127 | let n2 = n1.asinh(p, rm, &mut cc).unwrap(); 128 | let n3 = BigFloatNumber::parse("2.C5DAB0AF9025886C3364C7B6D6741EB19D4FB009D3F92CA21B77498D9F0666363C665F2F324EAEC8_e+3", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 129 | 130 | // println!("{:?}", n1.format(crate::Radix::Bin, rm).unwrap()); 131 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 132 | 133 | assert!(n2.cmp(&n3) == 0); 134 | 135 | let d1 = BigFloatNumber::max_value(p).unwrap(); 136 | let d2 = BigFloatNumber::min_value(p).unwrap(); 137 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 138 | let zero = BigFloatNumber::new(1).unwrap(); 139 | 140 | let mut eps = ONE.clone().unwrap(); 141 | eps.set_exponent( 142 | d1.exponent() - p as Exponent + core::mem::size_of::() as Exponent * 8, 143 | ); 144 | 145 | let mut d4 = d1.asinh(p, rm, &mut cc).unwrap(); 146 | // avoid overflow using sinh(x) = 2 * sinh(x/2)^2 147 | d4.set_exponent(d4.exponent() - 1); 148 | let tmp = d4.sinh(p + 1, RoundingMode::None, &mut cc).unwrap(); 149 | let mut d5 = tmp.mul(&tmp, p, rm).unwrap(); 150 | d5.set_exponent(d5.exponent() + 1); 151 | 152 | assert!( 153 | d1.sub(&d5, p, RoundingMode::ToEven) 154 | .unwrap() 155 | .abs() 156 | .unwrap() 157 | .cmp(&eps) 158 | < 0 159 | ); 160 | 161 | let mut d4 = d2.asinh(p, rm, &mut cc).unwrap(); 162 | // avoid overflow using sinh(x) = 2 * sinh(x/2)^2 163 | d4.set_exponent(d4.exponent() - 1); 164 | let tmp = d4.sinh(p + 1, RoundingMode::None, &mut cc).unwrap(); 165 | let mut d5 = tmp.mul(&tmp, p, rm).unwrap(); 166 | d5.set_exponent(d5.exponent() + 1); 167 | d5.set_sign(Sign::Neg); 168 | 169 | eps.set_exponent( 170 | d2.exponent() - p as Exponent + core::mem::size_of::() as Exponent * 8, 171 | ); 172 | 173 | assert!( 174 | d2.sub(&d5, p, RoundingMode::ToEven) 175 | .unwrap() 176 | .abs() 177 | .unwrap() 178 | .cmp(&eps) 179 | < 0 180 | ); 181 | 182 | assert!(d3.asinh(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 183 | assert!(zero.asinh(p, rm, &mut cc).unwrap().is_zero()); 184 | 185 | // subnormal arg 186 | let n1 = random_subnormal(p); 187 | assert!(n1.asinh(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 188 | } 189 | 190 | #[ignore] 191 | #[test] 192 | #[cfg(feature = "std")] 193 | fn asinh_perf() { 194 | let mut cc = Consts::new().unwrap(); 195 | let mut n = vec![]; 196 | let p = 160; 197 | for _ in 0..10000 { 198 | n.push(BigFloatNumber::random_normal(p, 0, 5).unwrap()); 199 | } 200 | 201 | for _ in 0..5 { 202 | let start_time = std::time::Instant::now(); 203 | for ni in n.iter() { 204 | let _f = ni.asinh(p, RoundingMode::ToEven, &mut cc).unwrap(); 205 | } 206 | let time = start_time.elapsed(); 207 | println!("{}", time.as_millis()); 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/atan.rs: -------------------------------------------------------------------------------- 1 | //! Arctangent. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::consts::TWO; 5 | use crate::common::util::calc_add_cost; 6 | use crate::common::util::calc_mul_cost; 7 | use crate::common::util::calc_sqrt_cost; 8 | use crate::common::util::round_p; 9 | use crate::defs::Error; 10 | use crate::defs::RoundingMode; 11 | use crate::num::BigFloatNumber; 12 | use crate::ops::consts::Consts; 13 | use crate::ops::series::series_cost_optimize; 14 | use crate::ops::series::series_run; 15 | use crate::ops::series::ArgReductionEstimator; 16 | use crate::ops::series::PolycoeffGen; 17 | use crate::ops::util::compute_small_exp; 18 | use crate::Exponent; 19 | use crate::WORD_BIT_SIZE; 20 | 21 | // Polynomial coefficient generator. 22 | struct AtanPolycoeffGen { 23 | f: BigFloatNumber, 24 | iter_cost: usize, 25 | } 26 | 27 | impl AtanPolycoeffGen { 28 | fn new(_p: usize) -> Result { 29 | let f = BigFloatNumber::from_word(1, 1)?; 30 | 31 | let iter_cost = calc_add_cost(f.mantissa_max_bit_len()); 32 | 33 | Ok(AtanPolycoeffGen { f, iter_cost }) 34 | } 35 | } 36 | 37 | impl PolycoeffGen for AtanPolycoeffGen { 38 | fn next(&mut self, rm: RoundingMode) -> Result<&BigFloatNumber, Error> { 39 | let p = self.f.mantissa_max_bit_len(); 40 | if self.f.is_positive() { 41 | self.f = self.f.add(&TWO, p, rm)?; 42 | } else { 43 | self.f = self.f.sub(&TWO, p, rm)?; 44 | } 45 | 46 | self.f.inv_sign(); 47 | 48 | Ok(&self.f) 49 | } 50 | 51 | #[inline] 52 | fn iter_cost(&self) -> usize { 53 | self.iter_cost 54 | } 55 | 56 | #[inline] 57 | fn is_div(&self) -> bool { 58 | true 59 | } 60 | } 61 | 62 | struct AtanArgReductionEstimator {} 63 | 64 | impl ArgReductionEstimator for AtanArgReductionEstimator { 65 | /// Estimates cost of reduction n times for number with precision p. 66 | fn reduction_cost(n: usize, p: usize) -> u64 { 67 | let cost_mul = calc_mul_cost(p); 68 | let cost_add = calc_add_cost(p); 69 | let sqrt_cost = calc_sqrt_cost(p, cost_mul, cost_add); 70 | n as u64 * (2 * (cost_mul + cost_add) + sqrt_cost) as u64 71 | } 72 | 73 | /// Given m, the negative power of 2 of a number, returns the negative power of 2 if reduction is applied n times. 74 | #[inline] 75 | fn reduction_effect(n: usize, m: isize) -> usize { 76 | // for x much smaller than 1, reduction is almost x/2 77 | (n as isize + m) as usize 78 | } 79 | } 80 | 81 | impl BigFloatNumber { 82 | /// Computes the arctangent of a number with precision `p`. The result is rounded using the rounding mode `rm`. 83 | /// This function requires constants cache `cc` for computing the result. 84 | /// Precision is rounded upwards to the word size. 85 | /// 86 | /// ## Errors 87 | /// 88 | /// - MemoryAllocation: failed to allocate memory. 89 | /// - InvalidArgument: the precision is incorrect. 90 | pub fn atan(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 91 | let p = round_p(p); 92 | 93 | if self.is_zero() { 94 | return Self::new2(p, self.sign(), self.inexact()); 95 | } 96 | 97 | let mut p_inc = WORD_BIT_SIZE; 98 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 99 | 100 | compute_small_exp!(self, self.exponent() as isize * 2 - 1, true, p_wrk, p, rm); 101 | 102 | p_wrk += p_inc; 103 | 104 | loop { 105 | let mut x = self.clone()?; 106 | 107 | let p_x = p_wrk + 5; 108 | x.set_precision(p_x, RoundingMode::None)?; 109 | 110 | // if x > 1 then arctan(x) = pi/2 - arctan(1/x) 111 | let mut ret = if x.exponent() > 0 { 112 | x = x.reciprocal(p_x, RoundingMode::None)?; 113 | 114 | let ret = x.atan_series(RoundingMode::None)?; 115 | 116 | let mut pi = cc.pi_num(p_x, RoundingMode::None)?; 117 | pi.set_exponent(1); 118 | pi.set_sign(self.sign()); 119 | 120 | pi.sub(&ret, p_x, RoundingMode::None) 121 | } else { 122 | x.atan_series(RoundingMode::None) 123 | }?; 124 | 125 | if ret.try_set_precision(p, rm, p_wrk)? { 126 | ret.set_inexact(ret.inexact() | self.inexact()); 127 | break Ok(ret); 128 | } 129 | 130 | p_wrk += p_inc; 131 | p_inc = round_p(p_wrk / 5); 132 | } 133 | } 134 | 135 | /// arctan using series 136 | pub(super) fn atan_series(mut self, rm: RoundingMode) -> Result { 137 | // atan: x - x^3/3 + x^5/5 - x^7/7 + ... 138 | 139 | let p = self.mantissa_max_bit_len(); 140 | let mut polycoeff_gen = AtanPolycoeffGen::new(p)?; 141 | let (mut reduction_times, niter, e_eff) = series_cost_optimize::( 142 | p, 143 | &polycoeff_gen, 144 | -(self.exponent() as isize), 145 | 2, 146 | false, 147 | ); 148 | 149 | // to avoid diverging error e_eff must be large enough 150 | if e_eff < 3 { 151 | reduction_times += 3 - e_eff; 152 | } 153 | 154 | // Reduction gives 2^(-p+8) per iteration. 155 | // e_eff compensates error of the series and gives 2^(-p+2). 156 | let add_prec = reduction_times as isize * 8 + 2 - e_eff as isize; 157 | let p_arg = p + if add_prec > 0 { add_prec as usize } else { 0 }; 158 | self.set_precision(p_arg, rm)?; 159 | 160 | let arg = if reduction_times > 0 { self.atan_arg_reduce(reduction_times)? } else { self }; 161 | 162 | let acc = arg.clone()?; // x 163 | let x_step = arg.mul(&arg, p_arg, rm)?; // x^2 164 | let x_first = arg.mul(&x_step, p_arg, rm)?; // x^3 165 | 166 | let mut ret = series_run(acc, x_first, x_step, niter, &mut polycoeff_gen)?; 167 | 168 | if reduction_times > 0 { 169 | ret.set_exponent(ret.exponent() + reduction_times as Exponent); 170 | } 171 | 172 | Ok(ret) 173 | } 174 | 175 | // reduce argument n times. 176 | fn atan_arg_reduce(&self, n: usize) -> Result { 177 | // y = x / (1 + sqrt(1 + x*x)) 178 | let mut ret = self.clone()?; 179 | let p = ret.mantissa_max_bit_len(); 180 | 181 | for _ in 0..n { 182 | let xx = ret.mul(&ret, p, RoundingMode::None)?; 183 | let n0 = xx.add(&ONE, p, RoundingMode::None)?; 184 | let n1 = n0.sqrt(p, RoundingMode::None)?; 185 | let n2 = n1.add(&ONE, p, RoundingMode::None)?; 186 | ret = ret.div(&n2, p, RoundingMode::FromZero)?; 187 | } 188 | 189 | Ok(ret) 190 | } 191 | } 192 | 193 | #[cfg(test)] 194 | mod tests { 195 | 196 | use crate::common::util::random_subnormal; 197 | 198 | use super::*; 199 | 200 | #[test] 201 | fn test_arctan() { 202 | let p = 320; 203 | let mut cc = Consts::new().unwrap(); 204 | let rm = RoundingMode::ToEven; 205 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 206 | n1.set_exponent(1); 207 | let _n2 = n1.atan(p, rm, &mut cc).unwrap(); 208 | //println!("{:?}", n2.format(crate::Radix::Dec, rm).unwrap()); 209 | 210 | // small exp 211 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e-20", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 212 | let n2 = n1.atan(p, rm, &mut cc).unwrap(); 213 | let n3 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419D71406B5262DC1F0C_e-20", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 214 | 215 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 216 | 217 | assert!(n2.cmp(&n3) == 0); 218 | 219 | // large exp 220 | let n1 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A200000000000000004D3C337F7C8D419EBBFC39B4BEC14AF6_e+20", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 221 | let n2 = n1.atan(p, rm, &mut cc).unwrap(); 222 | let n3 = BigFloatNumber::parse("1.921FB54442D18469898CC51701B839A1AF0B18A2C68B83BE07F0257A80F25883A5F3E060CDB82FEE_e+0", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 223 | 224 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 225 | 226 | assert!(n2.cmp(&n3) == 0); 227 | 228 | // near 1 229 | let n1 = BigFloatNumber::parse( 230 | "1.00000000000000000000000000000000000000000000000000000000000000002DC85F7E77EC487C", 231 | crate::Radix::Hex, 232 | p, 233 | RoundingMode::None, 234 | &mut cc, 235 | ) 236 | .unwrap(); 237 | let n2 = n1.atan(p, rm, &mut cc).unwrap(); 238 | let n3 = BigFloatNumber::parse( 239 | "C.90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22682E3838CA2A291C_e-1", 240 | crate::Radix::Hex, 241 | p, 242 | RoundingMode::None, 243 | &mut cc, 244 | ) 245 | .unwrap(); 246 | 247 | // println!("{:?}", n1.format(crate::Radix::Bin, rm).unwrap()); 248 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 249 | 250 | assert!(n2.cmp(&n3) == 0); 251 | 252 | // max, min, subnormal 253 | let d1 = BigFloatNumber::max_value(p).unwrap(); 254 | let d2 = BigFloatNumber::min_value(p).unwrap(); 255 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 256 | let zero = BigFloatNumber::new(1).unwrap(); 257 | 258 | let mut half_pi = cc.pi_num(p, RoundingMode::ToEven).unwrap(); 259 | half_pi.set_exponent(1); 260 | 261 | assert!(d1.atan(p, rm, &mut cc).unwrap().cmp(&half_pi) == 0); 262 | assert!( 263 | d2.atan(p, rm, &mut cc) 264 | .unwrap() 265 | .cmp(&half_pi.neg().unwrap()) 266 | == 0 267 | ); 268 | assert!(d3.atan(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 269 | assert!(zero.atan(p, rm, &mut cc).unwrap().is_zero()); 270 | 271 | // subnormal arg 272 | let n1 = random_subnormal(p); 273 | assert!(n1.atan(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 274 | } 275 | 276 | #[ignore] 277 | #[test] 278 | #[cfg(feature = "std")] 279 | fn arctan_perf() { 280 | let p = 16000; 281 | let mut cc = Consts::new().unwrap(); 282 | let mut n = vec![]; 283 | for _ in 0..10 { 284 | n.push(BigFloatNumber::random_normal(p, -5, 5).unwrap()); 285 | } 286 | 287 | for _ in 0..5 { 288 | let start_time = std::time::Instant::now(); 289 | for ni in n.iter() { 290 | let _f = ni.atan(p, RoundingMode::ToEven, &mut cc).unwrap(); 291 | } 292 | let time = start_time.elapsed(); 293 | println!("{}", time.as_millis()); 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/atanh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic arctangent. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::count_leading_ones; 5 | use crate::common::util::round_p; 6 | use crate::defs::Error; 7 | use crate::defs::RoundingMode; 8 | use crate::num::BigFloatNumber; 9 | use crate::ops::consts::Consts; 10 | use crate::ops::util::compute_small_exp; 11 | use crate::WORD_BIT_SIZE; 12 | 13 | impl BigFloatNumber { 14 | /// Computes the hyperbolic arctangent of a number with precision `p`. The result is rounded using the rounding mode `rm`. 15 | /// This function requires constants cache `cc` for computing the result. 16 | /// Precision is rounded upwards to the word size. 17 | /// 18 | /// ## Errors 19 | /// 20 | /// - ExponentOverflow: the result is too large. 21 | /// - MemoryAllocation: failed to allocate memory. 22 | /// - InvalidArgument: when |`self`| > 1, or the precision is incorrect. 23 | pub fn atanh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 24 | let p = round_p(p); 25 | 26 | if self.is_zero() { 27 | let mut ret = self.clone()?; 28 | ret.set_precision(p, RoundingMode::None)?; 29 | return Ok(ret); 30 | } 31 | 32 | if self.exponent() == 1 { 33 | if self.abs_cmp(&ONE) == 0 { 34 | return Err(Error::ExponentOverflow(self.sign())); 35 | } else { 36 | return Err(Error::InvalidArgument); 37 | } 38 | } else if self.exponent() > 1 { 39 | return Err(Error::InvalidArgument); 40 | } 41 | 42 | let mut p_inc = WORD_BIT_SIZE; 43 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 44 | 45 | compute_small_exp!(self, self.exponent() as isize * 2 - 1, false, p_wrk, p, rm); 46 | 47 | // 0.5 * ln((1 + x) / (1 - x)) 48 | 49 | let mut additional_prec = 4; 50 | if self.exponent() < 0 { 51 | additional_prec += self.exponent().unsigned_abs() as usize; 52 | } else { 53 | additional_prec += count_leading_ones(self.mantissa().digits()); 54 | } 55 | 56 | p_wrk += p_inc; 57 | 58 | let mut x = self.clone()?; 59 | x.set_inexact(false); 60 | 61 | loop { 62 | let p_x = p_wrk + additional_prec; 63 | x.set_precision(p_x, RoundingMode::None)?; 64 | 65 | let d1 = ONE.add(&x, p_x, RoundingMode::None)?; 66 | let d2 = ONE.sub(&x, p_x, RoundingMode::None)?; 67 | 68 | let d3 = d1.div(&d2, p_x, RoundingMode::None)?; 69 | 70 | let mut ret = d3.ln(p_x, RoundingMode::None, cc)?; 71 | 72 | ret.div_by_2(RoundingMode::None); 73 | 74 | if ret.try_set_precision(p, rm, p_wrk)? { 75 | ret.set_inexact(ret.inexact() | self.inexact()); 76 | break Ok(ret); 77 | } 78 | 79 | p_wrk += p_inc; 80 | p_inc = round_p(p_wrk / 5); 81 | } 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | mod tests { 87 | 88 | use crate::{common::util::random_subnormal, Sign}; 89 | 90 | use super::*; 91 | 92 | #[test] 93 | fn test_atanh() { 94 | let p = 320; 95 | let mut cc = Consts::new().unwrap(); 96 | let rm = RoundingMode::ToEven; 97 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 98 | n1.set_exponent(-34); 99 | let _n2 = n1.atanh(p, rm, &mut cc).unwrap(); 100 | //println!("{:?}", n2.format(crate::Radix::Bin, rm).unwrap()); 101 | 102 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 103 | n1.set_exponent(0); 104 | let _n2 = n1.atanh(p, rm, &mut cc).unwrap(); 105 | //println!("{:?}", n2.format(crate::Radix::Bin, rm).unwrap()); 106 | 107 | // asymptotic & extrema testing 108 | let p = 640; 109 | let n1 = BigFloatNumber::parse("F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8EE51946EC87F86A7E6DA4D8C6ED8DFAE4D7B7FF0B8356E63EF277C97F2E2111AECCBE8F2DF4EFE48F618B1E75C7CBBDCFCE32604DE9F240_e-1", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 110 | let n2 = n1.atanh(p, rm, &mut cc).unwrap(); 111 | let n3 = BigFloatNumber::parse("4.34C10E83FA43CA88E0A3A0125990D4B8BC2CF39E0695A6B9F73DE8F43C00767B966992C0A98F96AAC882152114C2FE89AD58DA3BA9E2013CAD88370B80F7D9AD4D9B6494C0591D3CAA382BF6FBD88730_e+1", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 112 | 113 | assert!(n2.cmp(&n3) == 0); 114 | 115 | // small value 116 | let p = 320; 117 | let n1 = BigFloatNumber::parse("7.C3A95633A7BFB754F49F839BCFDED202E43C4EEB4E6CC1292F4751559BBC55E859642CBB19881B10_e-F", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 118 | let n2 = n1.atanh(p, rm, &mut cc).unwrap(); 119 | let n3 = BigFloatNumber::parse("7.C3A95633A7BFB754F49F839BCFDF6E088C51BE9FAF9B30BC9499ABD8AFDA2F9E0F9B97FBDB228480_e-f", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 120 | 121 | // println!("{:?}", n1.format(crate::Radix::Bin, rm).unwrap()); 122 | // println!("{:?}", n2.format(crate::Radix::Hex, rm).unwrap()); 123 | 124 | assert!(n2.cmp(&n3) == 0); 125 | 126 | let d1 = BigFloatNumber::max_value(p).unwrap(); 127 | let d2 = BigFloatNumber::min_value(p).unwrap(); 128 | 129 | assert!(d1.atanh(p, rm, &mut cc).unwrap_err() == Error::InvalidArgument); 130 | assert!(d2.atanh(p, rm, &mut cc).unwrap_err() == Error::InvalidArgument); 131 | 132 | // subnormal 133 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 134 | let zero = BigFloatNumber::new(1).unwrap(); 135 | 136 | assert!(d3.atanh(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 137 | assert!(zero.atanh(p, rm, &mut cc).unwrap().is_zero()); 138 | 139 | assert!(ONE.atanh(p, rm, &mut cc).unwrap_err() == Error::ExponentOverflow(Sign::Pos)); 140 | assert!( 141 | ONE.neg().unwrap().atanh(p, rm, &mut cc).unwrap_err() 142 | == Error::ExponentOverflow(Sign::Neg) 143 | ); 144 | 145 | let n1 = random_subnormal(p); 146 | assert!(n1.atanh(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 147 | } 148 | 149 | #[ignore] 150 | #[test] 151 | #[cfg(feature = "std")] 152 | fn atanh_perf() { 153 | let p = 160; 154 | let mut cc = Consts::new().unwrap(); 155 | let mut n = vec![]; 156 | for _ in 0..10000 { 157 | n.push(BigFloatNumber::random_normal(p, 0, 5).unwrap()); 158 | } 159 | 160 | for _ in 0..5 { 161 | let start_time = std::time::Instant::now(); 162 | for ni in n.iter() { 163 | let _f = ni.atanh(p, RoundingMode::ToEven, &mut cc).unwrap(); 164 | } 165 | let time = start_time.elapsed(); 166 | println!("{}", time.as_millis()); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/cbrt.rs: -------------------------------------------------------------------------------- 1 | //! Cube root computation. 2 | 3 | use crate::{ 4 | common::util::round_p, 5 | defs::{Error, EXPONENT_MIN}, 6 | num::BigFloatNumber, 7 | Exponent, RoundingMode, 8 | }; 9 | 10 | impl BigFloatNumber { 11 | /// Computes the cube root of a number with precision `p`. The result is rounded using the rounding mode `rm`. 12 | /// Precision is rounded upwards to the word size. 13 | /// 14 | /// ## Errors 15 | /// 16 | /// - MemoryAllocation: failed to allocate memory. 17 | /// - InvalidArgument: the precision is incorrect. 18 | pub fn cbrt(&self, p: usize, rm: RoundingMode) -> Result { 19 | let p = round_p(p); 20 | Self::p_assertion(p)?; 21 | 22 | if self.is_zero() { 23 | return Self::new2(p, self.sign(), self.inexact()); 24 | } 25 | 26 | let (e1, m1_opt) = self.normalize()?; 27 | let m1_normalized = m1_opt.as_ref().unwrap_or_else(|| self.mantissa()); 28 | 29 | let exp_add = if e1 < 0 { 30 | -e1 % 3 31 | } else if e1 > 0 { 32 | 3 - e1 % 3 33 | } else { 34 | 0 35 | }; 36 | 37 | let mut inexact = self.inexact(); 38 | 39 | let (e_shift, m3) = m1_normalized.cbrt(p, rm, self.is_positive(), &mut inexact, exp_add)?; 40 | 41 | let e = (e1 + exp_add) / 3 + e_shift; 42 | 43 | if e < EXPONENT_MIN as isize { 44 | let mut ret = 45 | BigFloatNumber::from_raw_unchecked(m3, self.sign(), EXPONENT_MIN, inexact); 46 | 47 | ret.subnormalize(e, rm); 48 | 49 | Ok(ret) 50 | } else { 51 | Ok(BigFloatNumber::from_raw_unchecked( 52 | m3, 53 | self.sign(), 54 | e as Exponent, 55 | inexact, 56 | )) 57 | } 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | 64 | use super::*; 65 | use crate::{ 66 | common::{consts::ONE, util::random_subnormal}, 67 | defs::{EXPONENT_MAX, EXPONENT_MIN, WORD_BIT_SIZE}, 68 | Consts, Exponent, 69 | }; 70 | 71 | #[cfg(feature = "std")] 72 | use crate::Sign; 73 | 74 | #[test] 75 | fn test_cbrt() { 76 | /* let n1 = BigFloatNumber::from_words( 77 | &[1, 0, 1, 0, 0, 0, WORD_SIGNIFICANT_BIT], 78 | Sign::Pos, 1).unwrap(); */ 79 | /* let n1 = BigFloatNumber::from_word(11*11*11, 128).unwrap(); 80 | let n2 = n1.cbrt(128, RoundingMode::FromZero).unwrap(); 81 | println!("{:?}", n1.format(crate::Radix::Bin, RoundingMode::None)); 82 | println!("{:?}", n2.format(crate::Radix::Bin, RoundingMode::None)); 83 | return; */ 84 | 85 | let mut cc = Consts::new().unwrap(); 86 | let mut eps = ONE.clone().unwrap(); 87 | 88 | for _ in 0..1000 { 89 | let prec = (rand::random::() % 5 + 1) * WORD_BIT_SIZE; 90 | let d1 = BigFloatNumber::random_normal(prec, EXPONENT_MIN, EXPONENT_MAX).unwrap(); 91 | let d2 = d1.cbrt(prec, RoundingMode::ToEven).unwrap(); 92 | let d3 = d2 93 | .mul(&d2, prec, RoundingMode::ToEven) 94 | .unwrap() 95 | .mul(&d2, prec, RoundingMode::ToEven) 96 | .unwrap(); 97 | 98 | eps.set_exponent(d1.exponent() - prec as Exponent + 3); 99 | 100 | //println!("d1 {:?}", d1.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 101 | //println!("d2 {:?}", d2.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 102 | //println!("d3 {:?}", d3.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 103 | 104 | assert!( 105 | d1.sub(&d3, prec, RoundingMode::ToEven) 106 | .unwrap() 107 | .abs() 108 | .unwrap() 109 | .cmp(&eps) 110 | < 0 111 | ); 112 | } 113 | 114 | // MAX 115 | let prec = 320; 116 | let d1 = BigFloatNumber::max_value(prec).unwrap(); 117 | let d2 = d1.cbrt(prec, RoundingMode::ToEven).unwrap(); 118 | let d3 = d2 119 | .mul(&d2, prec, RoundingMode::ToEven) 120 | .unwrap() 121 | .mul(&d2, prec, RoundingMode::ToEven) 122 | .unwrap(); 123 | eps.set_exponent(d1.exponent() - prec as Exponent + 3); 124 | assert!( 125 | d1.sub(&d3, prec, RoundingMode::ToEven) 126 | .unwrap() 127 | .abs() 128 | .unwrap() 129 | .cmp(&eps) 130 | < 0 131 | ); 132 | 133 | // MIN 134 | let d1 = BigFloatNumber::min_value(prec).unwrap(); 135 | let d2 = d1.cbrt(prec, RoundingMode::ToEven).unwrap(); 136 | let d3 = d2 137 | .mul(&d2, prec, RoundingMode::ToEven) 138 | .unwrap() 139 | .mul(&d2, prec, RoundingMode::ToEven) 140 | .unwrap(); 141 | eps.set_exponent(d1.exponent() - prec as Exponent + 3); 142 | assert!( 143 | d1.sub(&d3, prec, RoundingMode::ToEven) 144 | .unwrap() 145 | .abs() 146 | .unwrap() 147 | .cmp(&eps) 148 | < 0 149 | ); 150 | 151 | // MIN POSITIVE 152 | let d1 = BigFloatNumber::min_positive(prec).unwrap(); 153 | let d2 = d1.cbrt(prec, RoundingMode::ToEven).unwrap(); 154 | let d3 = d2 155 | .mul(&d2, prec, RoundingMode::ToEven) 156 | .unwrap() 157 | .mul(&d2, prec, RoundingMode::ToEven) 158 | .unwrap(); 159 | let eps = BigFloatNumber::min_positive(prec).unwrap(); 160 | assert!( 161 | d1.sub(&d3, prec, RoundingMode::ToEven) 162 | .unwrap() 163 | .abs() 164 | .unwrap() 165 | .cmp(&eps) 166 | <= 0 167 | ); 168 | 169 | // near 1 170 | let p = 320; 171 | let d1 = BigFloatNumber::parse( 172 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2DC85F7E77EC4872DC85F7E77EC487_e-1", 173 | crate::Radix::Hex, 174 | p, 175 | RoundingMode::None, 176 | &mut cc, 177 | ) 178 | .unwrap(); 179 | let d2 = d1.cbrt(p, RoundingMode::ToEven).unwrap(); 180 | let d3 = BigFloatNumber::parse( 181 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB9ED752A27F96D7B9ED752A27F96D8_e-1", 182 | crate::Radix::Hex, 183 | p, 184 | RoundingMode::None, 185 | &mut cc, 186 | ) 187 | .unwrap(); 188 | 189 | //println!("{:?}", d2.format(crate::Radix::Hex, RoundingMode::None).unwrap()); 190 | 191 | assert!(d2.cmp(&d3) == 0); 192 | 193 | let d1 = BigFloatNumber::parse( 194 | "1.00000000000000000000000000000000000000000000000000000000000000002DC85F7E77EC487C", 195 | crate::Radix::Hex, 196 | p, 197 | RoundingMode::None, 198 | &mut cc, 199 | ) 200 | .unwrap(); 201 | let d2 = d1.cbrt(p, RoundingMode::ToEven).unwrap(); 202 | let d3 = BigFloatNumber::parse( 203 | "1.00000000000000000000000000000000000000000000000000000000000000000F42CA7F7D4EC2D4", 204 | crate::Radix::Hex, 205 | p, 206 | RoundingMode::None, 207 | &mut cc, 208 | ) 209 | .unwrap(); 210 | 211 | // println!("{:?}", d2.format(crate::Radix::Hex, RoundingMode::None).unwrap()); 212 | 213 | assert!(d2.cmp(&d3) == 0); 214 | 215 | // random subnormal arg 216 | for _ in 0..1000 { 217 | let d1 = random_subnormal(prec); 218 | 219 | let d2 = d1.cbrt(prec, RoundingMode::ToEven).unwrap(); 220 | let d3 = d2 221 | .mul(&d2, prec, RoundingMode::ToEven) 222 | .unwrap() 223 | .mul(&d2, prec, RoundingMode::ToEven) 224 | .unwrap(); 225 | 226 | let eps = BigFloatNumber::min_positive(prec).unwrap(); 227 | // eps.set_exponent(eps.get_exponent() + 3); 228 | 229 | // println!("{:?}", d1.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 230 | // println!("{:?}", d3.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 231 | // println!("{:?}", eps.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 232 | 233 | assert!( 234 | d1.sub(&d3, prec, RoundingMode::ToEven) 235 | .unwrap() 236 | .abs() 237 | .unwrap() 238 | .cmp(&eps) 239 | <= 0 240 | ); 241 | } 242 | } 243 | 244 | #[ignore] 245 | #[test] 246 | #[cfg(feature = "std")] 247 | fn cbrt_perf() { 248 | let mut n = vec![]; 249 | let p = 132; 250 | for _ in 0..100000 { 251 | let mut n0 = BigFloatNumber::random_normal(p, -0, 0).unwrap(); 252 | n0.set_sign(Sign::Pos); 253 | n.push(n0); 254 | } 255 | 256 | for _ in 0..5 { 257 | let start_time = std::time::Instant::now(); 258 | for ni in n.iter() { 259 | let _f = ni.cbrt(p, RoundingMode::ToEven).unwrap(); 260 | } 261 | let time = start_time.elapsed(); 262 | println!("{}", time.as_millis()); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/consts/e.rs: -------------------------------------------------------------------------------- 1 | //! Euler's number 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::{log2_floor, round_p}; 5 | use crate::defs::Error; 6 | use crate::num::BigFloatNumber; 7 | use crate::{RoundingMode, WORD_BIT_SIZE}; 8 | 9 | fn pq(a: usize, b: usize) -> Result<(BigFloatNumber, BigFloatNumber), Error> { 10 | if a == b - 1 { 11 | let q = BigFloatNumber::from_usize(b)?; 12 | let p = BigFloatNumber::from_word(1, 1)?; 13 | 14 | Ok((p, q)) 15 | } else { 16 | let m = (a + b) / 2; 17 | 18 | let (pa, qa) = pq(a, m)?; 19 | let (pb, qb) = pq(m, b)?; 20 | 21 | let q = qa.mul_full_prec(&qb)?; 22 | let n0 = pa.mul_full_prec(&qb)?; 23 | let p = n0.add_full_prec(&pb)?; 24 | 25 | Ok((p, q)) 26 | } 27 | } 28 | 29 | fn pqr_inc( 30 | pa: &BigFloatNumber, 31 | qa: &BigFloatNumber, 32 | m: usize, 33 | ) -> Result<(BigFloatNumber, BigFloatNumber, usize), Error> { 34 | let b = m * 2; 35 | 36 | let (pb, qb) = pq(m, b)?; 37 | 38 | let q = qa.mul_full_prec(&qb)?; 39 | let n0 = pa.mul_full_prec(&qb)?; 40 | let p = n0.add_full_prec(&pb)?; 41 | 42 | Ok((p, q, b)) 43 | } 44 | 45 | /// Holds value of currently computed e. 46 | #[derive(Debug)] 47 | pub struct ECache { 48 | b: usize, 49 | pk: BigFloatNumber, 50 | qk: BigFloatNumber, 51 | val: BigFloatNumber, 52 | } 53 | 54 | impl ECache { 55 | fn calc_e(p: &BigFloatNumber, q: &BigFloatNumber) -> Result { 56 | // 1 + pk / qk 57 | let prec = p.mantissa_max_bit_len().max(q.mantissa_max_bit_len()); 58 | let f0 = p.div(q, prec, RoundingMode::None)?; 59 | f0.add(&ONE, prec, RoundingMode::None) 60 | } 61 | 62 | pub fn new() -> Result { 63 | // initial precision is large enough, as b_factor() requires it. 64 | let (p01, q01) = pq(0, 64)?; 65 | 66 | let val = Self::calc_e(&p01, &q01)?; 67 | 68 | Ok(ECache { 69 | b: 64, 70 | pk: p01, 71 | qk: q01, 72 | val, 73 | }) 74 | } 75 | 76 | fn b_factor(x: usize) -> usize { 77 | // If we compute n elements of the series 1 + 1/1! + 1/2! + ... 78 | // then 1/(n!) is greater than the remaining terms: sum(1/(k!)), k = n+1 .. +inf 79 | // (we can see it if we divide sum(1/(k!)) by 1/(n!)). 80 | // So, for n terms the error is less than 1/(n!). 81 | // Let p be the precision we need. 82 | // From Stirling's approximation: 1/(n!) < (e/n)^n. 83 | // From (e/n)^n < 1/(2^p) follows simplified, but more strict, inequality n*(log2(n) - 2) > p. 84 | // Assume n = p / log2(p) - log2(log2(p)), and substitute in the inequality above, 85 | // then for large enough p the inequality is true. 86 | let ln = log2_floor(x); 87 | let lln = log2_floor(ln); 88 | 89 | // to give higher estimate log2_floor is used, 90 | // and 3 is an empirical adjustment. 91 | x / (ln - lln - 3) 92 | } 93 | 94 | /// Return value of e with precision k. 95 | pub(crate) fn for_prec(&mut self, k: usize, rm: RoundingMode) -> Result { 96 | let mut p_inc = WORD_BIT_SIZE; 97 | let mut p_wrk = round_p(k) + p_inc; 98 | 99 | loop { 100 | let kext = Self::b_factor(p_wrk); 101 | 102 | if self.b > kext { 103 | let mut ret = self.val.clone()?; 104 | 105 | if ret.try_set_precision(k, rm, p_wrk)? { 106 | return Ok(ret); 107 | } 108 | 109 | p_wrk += p_inc; 110 | p_inc = round_p(p_wrk / 5); 111 | } 112 | 113 | let mut pk; 114 | let mut qk; 115 | let mut bb; 116 | 117 | (pk, qk, bb) = pqr_inc(&self.pk, &self.qk, self.b)?; 118 | 119 | while bb <= kext { 120 | (pk, qk, bb) = pqr_inc(&pk, &qk, bb)?; 121 | } 122 | 123 | self.val = Self::calc_e(&pk, &qk)?; 124 | 125 | self.pk = pk; 126 | self.qk = qk; 127 | self.b = bb; 128 | } 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | 135 | use super::*; 136 | use crate::{RoundingMode, Sign}; 137 | 138 | #[test] 139 | #[cfg(target_arch = "x86")] 140 | fn test_e_const() { 141 | let mut e = ECache::new().unwrap(); 142 | let c = e.for_prec(320, RoundingMode::ToEven).unwrap(); 143 | //println!("{:?}", c); 144 | let r = BigFloatNumber::from_raw_parts( 145 | &[ 146 | 614153977, 3432226254, 342111227, 2850108993, 3459069589, 3636053379, 658324721, 147 | 2950452768, 2730183322, 2918732888, 148 | ], 149 | 320, 150 | Sign::Pos, 151 | 2, 152 | false, 153 | ) 154 | .unwrap(); 155 | assert!(c.cmp(&r) == 0); 156 | } 157 | 158 | #[test] 159 | #[cfg(not(target_arch = "x86"))] 160 | fn test_e_const() { 161 | let mut e = ECache::new().unwrap(); 162 | let c = e.for_prec(320, RoundingMode::ToEven).unwrap(); 163 | //println!("{:?}", c); 164 | let r = BigFloatNumber::from_raw_parts( 165 | &[ 166 | 14741299514016743161, 167 | 12241124915312604155, 168 | 15616730352774362773, 169 | 12672098147611000049, 170 | 12535862302449814170, 171 | ], 172 | 320, 173 | Sign::Pos, 174 | 2, 175 | false, 176 | ) 177 | .unwrap(); 178 | assert!(c.cmp(&r) == 0); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/consts/ln10.rs: -------------------------------------------------------------------------------- 1 | //! ln(10) 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::round_p; 5 | use crate::defs::Error; 6 | use crate::num::BigFloatNumber; 7 | use crate::{RoundingMode, WORD_BIT_SIZE}; 8 | 9 | fn pqr(a: usize, b: usize) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber), Error> { 10 | if a == b - 1 { 11 | let p = BigFloatNumber::from_word(81, 1)?; 12 | let q = BigFloatNumber::from_usize((2 * b + 1) * 121)?; 13 | let r = BigFloatNumber::from_usize((2 * b + 1) * 81)?; 14 | 15 | Ok((p, q, r)) 16 | } else { 17 | let m = (a + b) / 2; 18 | 19 | let (pa, qa, ra) = pqr(a, m)?; 20 | let (pb, qb, rb) = pqr(m, b)?; 21 | 22 | let pq = pa.mul_full_prec(&qb)?; 23 | let pr = pb.mul_full_prec(&ra)?; 24 | 25 | let p = pq.add_full_prec(&pr)?; 26 | let q = qa.mul_full_prec(&qb)?; 27 | let r = ra.mul_full_prec(&rb)?; 28 | 29 | Ok((p, q, r)) 30 | } 31 | } 32 | 33 | fn pqr_inc( 34 | pa: &BigFloatNumber, 35 | qa: &BigFloatNumber, 36 | ra: &BigFloatNumber, 37 | m: usize, 38 | ) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber, usize), Error> { 39 | let b = m * 2; 40 | 41 | let (pb, qb, rb) = pqr(m, b)?; 42 | 43 | let pq = pa.mul_full_prec(&qb)?; 44 | let pr = pb.mul_full_prec(ra)?; 45 | 46 | let p_ret = pq.add_full_prec(&pr)?; 47 | let q_ret = qa.mul_full_prec(&qb)?; 48 | let r_ret = ra.mul_full_prec(&rb)?; 49 | 50 | Ok((p_ret, q_ret, r_ret, b)) 51 | } 52 | 53 | /// Holds value of currently computed ln(10). 54 | #[derive(Debug)] 55 | pub struct Ln10Cache { 56 | b: usize, 57 | pk: BigFloatNumber, 58 | qk: BigFloatNumber, 59 | rk: BigFloatNumber, 60 | val: BigFloatNumber, 61 | } 62 | 63 | impl Ln10Cache { 64 | pub fn new() -> Result { 65 | let (p01, q01, r01) = pqr(0, 1)?; 66 | 67 | let val = Self::calc_ln10(&p01, &q01)?; 68 | 69 | Ok(Ln10Cache { 70 | b: 1, 71 | pk: p01, 72 | qk: q01, 73 | rk: r01, 74 | val, 75 | }) 76 | } 77 | 78 | fn calc_ln10(p: &BigFloatNumber, q: &BigFloatNumber) -> Result { 79 | // 18 * (1 + p / q) / 11 80 | let prec = p.mantissa_max_bit_len().max(q.mantissa_max_bit_len()); 81 | let mut val = p.div(q, prec, crate::RoundingMode::None)?; 82 | val = val.add(&ONE, prec, crate::RoundingMode::None)?; 83 | let f0 = BigFloatNumber::from_word(18, 1)?; 84 | let f1 = BigFloatNumber::from_word(11, 1)?; 85 | val = val.mul(&f0, prec, crate::RoundingMode::None)?; 86 | val.div(&f1, prec, crate::RoundingMode::None) 87 | } 88 | 89 | /// Return value of ln(10) with precision k (calculate if needed). 90 | pub(crate) fn for_prec(&mut self, k: usize, rm: RoundingMode) -> Result { 91 | let mut p_inc = WORD_BIT_SIZE; 92 | let mut p_wrk = round_p(k) + p_inc; 93 | 94 | loop { 95 | let kext = k * 1728 / 1000 + 4; 96 | 97 | if self.b > kext { 98 | let mut ret = self.val.clone()?; 99 | 100 | if ret.try_set_precision(k, rm, p_wrk)? { 101 | return Ok(ret); 102 | } 103 | 104 | p_wrk += p_inc; 105 | p_inc = round_p(p_wrk / 5); 106 | } 107 | 108 | let mut pk; 109 | let mut qk; 110 | let mut rk; 111 | let mut bb; 112 | 113 | (pk, qk, rk, bb) = pqr_inc(&self.pk, &self.qk, &self.rk, self.b)?; 114 | 115 | while bb <= kext { 116 | (pk, qk, rk, bb) = pqr_inc(&pk, &qk, &rk, bb)?; 117 | } 118 | 119 | self.val = Self::calc_ln10(&pk, &qk)?; 120 | 121 | self.pk = pk; 122 | self.qk = qk; 123 | self.rk = rk; 124 | self.b = bb; 125 | } 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod tests { 131 | 132 | use super::*; 133 | use crate::{RoundingMode, Sign}; 134 | 135 | #[test] 136 | #[cfg(target_arch = "x86")] 137 | fn test_ln10_const() { 138 | let mut ln10 = Ln10Cache::new().unwrap(); 139 | let c = ln10.for_prec(320, RoundingMode::ToEven).unwrap(); 140 | //println!("{:?}", c.fp3(crate::Radix::Dec, RoundingMode::None)); 141 | let r = BigFloatNumber::from_raw_parts( 142 | &[ 143 | 2980581469, 2519663319, 32517490, 2210799234, 3663591694, 3801083129, 2194868776, 144 | 3931559467, 2863180822, 2472381917, 145 | ], 146 | 320, 147 | Sign::Pos, 148 | 2, 149 | false, 150 | ) 151 | .unwrap(); 152 | assert!(c.cmp(&r) == 0); 153 | } 154 | 155 | #[test] 156 | #[cfg(not(target_arch = "x86"))] 157 | fn test_ln10_const() { 158 | let mut ln10 = Ln10Cache::new().unwrap(); 159 | let c = ln10.for_prec(320, RoundingMode::ToEven).unwrap(); 160 | //println!("{:?}", c); 161 | let r = BigFloatNumber::from_raw_parts( 162 | &[ 163 | 10821871555016396893, 164 | 9495310408084368754, 165 | 16325527732095940878, 166 | 16885919335239060008, 167 | 10618799479599967254, 168 | ], 169 | 320, 170 | Sign::Pos, 171 | 2, 172 | false, 173 | ) 174 | .unwrap(); 175 | assert!(c.cmp(&r) == 0); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/consts/ln2.rs: -------------------------------------------------------------------------------- 1 | //! ln(2) 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::consts::THREE; 5 | use crate::common::util::round_p; 6 | use crate::defs::Error; 7 | use crate::num::BigFloatNumber; 8 | use crate::RoundingMode; 9 | use crate::WORD_BIT_SIZE; 10 | 11 | fn pqr(a: usize, b: usize) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber), Error> { 12 | if a == b - 1 { 13 | let p = BigFloatNumber::from_word(1, 1)?; 14 | let q = BigFloatNumber::from_usize((2 * b + 1) * 9)?; 15 | let r = BigFloatNumber::from_usize(2 * b + 1)?; 16 | 17 | Ok((p, q, r)) 18 | } else { 19 | let m = (a + b) / 2; 20 | 21 | let (pa, qa, ra) = pqr(a, m)?; 22 | let (pb, qb, rb) = pqr(m, b)?; 23 | 24 | let pq = pa.mul_full_prec(&qb)?; 25 | let pr = pb.mul_full_prec(&ra)?; 26 | 27 | let p = pq.add_full_prec(&pr)?; 28 | let q = qa.mul_full_prec(&qb)?; 29 | let r = ra.mul_full_prec(&rb)?; 30 | 31 | Ok((p, q, r)) 32 | } 33 | } 34 | 35 | fn pqr_inc( 36 | pa: &BigFloatNumber, 37 | qa: &BigFloatNumber, 38 | ra: &BigFloatNumber, 39 | m: usize, 40 | ) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber, usize), Error> { 41 | let b = m * 2; 42 | 43 | let (pb, qb, rb) = pqr(m, b)?; 44 | 45 | let pq = pa.mul_full_prec(&qb)?; 46 | let pr = pb.mul_full_prec(ra)?; 47 | 48 | let p_ret = pq.add_full_prec(&pr)?; 49 | let q_ret = qa.mul_full_prec(&qb)?; 50 | let r_ret = ra.mul_full_prec(&rb)?; 51 | 52 | Ok((p_ret, q_ret, r_ret, b)) 53 | } 54 | 55 | /// Holds value of currently computed ln(2). 56 | #[derive(Debug)] 57 | pub struct Ln2Cache { 58 | b: usize, 59 | pk: BigFloatNumber, 60 | qk: BigFloatNumber, 61 | rk: BigFloatNumber, 62 | val: BigFloatNumber, 63 | } 64 | 65 | impl Ln2Cache { 66 | pub fn new() -> Result { 67 | let (p01, q01, r01) = pqr(0, 1)?; 68 | 69 | // 2 * (1 + p / q) / 3 70 | let prec = p01.mantissa_max_bit_len().max(q01.mantissa_max_bit_len()); 71 | let mut val = p01.div(&q01, prec, crate::RoundingMode::None)?; 72 | val = val.add(&ONE, prec, crate::RoundingMode::None)?; 73 | val = val.div(&THREE, prec, crate::RoundingMode::None)?; 74 | val.set_exponent(val.exponent() + 1); 75 | 76 | Ok(Ln2Cache { 77 | b: 1, 78 | pk: p01, 79 | qk: q01, 80 | rk: r01, 81 | val, 82 | }) 83 | } 84 | 85 | /// Return value of ln(2) with precision k (calculate if needed). 86 | pub(crate) fn for_prec(&mut self, k: usize, rm: RoundingMode) -> Result { 87 | let mut p_inc = WORD_BIT_SIZE; 88 | let mut p_wrk = round_p(k) + p_inc; 89 | 90 | loop { 91 | let kext = k / 2 + 4; 92 | 93 | if self.b > kext { 94 | let mut ret = self.val.clone()?; 95 | 96 | if ret.try_set_precision(k, rm, p_wrk)? { 97 | return Ok(ret); 98 | } 99 | 100 | p_wrk += p_inc; 101 | p_inc = round_p(p_wrk / 5); 102 | } 103 | 104 | let mut pk; 105 | let mut qk; 106 | let mut rk; 107 | let mut bb; 108 | 109 | (pk, qk, rk, bb) = pqr_inc(&self.pk, &self.qk, &self.rk, self.b)?; 110 | 111 | while bb <= kext { 112 | (pk, qk, rk, bb) = pqr_inc(&pk, &qk, &rk, bb)?; 113 | } 114 | 115 | // 2 * (1 + p / q) / 3 116 | let prec = pk.mantissa_max_bit_len().max(qk.mantissa_max_bit_len()); 117 | let mut ret = pk.div(&qk, prec, crate::RoundingMode::None)?; 118 | ret = ret.add(&ONE, prec, crate::RoundingMode::None)?; 119 | ret = ret.div(&THREE, prec, crate::RoundingMode::None)?; 120 | ret.set_exponent(ret.exponent() + 1); 121 | 122 | self.val = ret; 123 | 124 | self.pk = pk; 125 | self.qk = qk; 126 | self.rk = rk; 127 | self.b = bb; 128 | } 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | 135 | use crate::{RoundingMode, Sign}; 136 | 137 | use super::*; 138 | 139 | #[test] 140 | #[cfg(target_arch = "x86")] 141 | fn test_ln2_const() { 142 | let mut ln2 = Ln2Cache::new().unwrap(); 143 | let c = ln2.for_prec(3200, RoundingMode::ToEven).unwrap(); 144 | //println!("{:?}", c); 145 | let r = BigFloatNumber::from_raw_parts( 146 | &[ 147 | 3303612541, 717600284, 3199892741, 1462999970, 137959948, 6349977, 1794441986, 148 | 1645437721, 1024185081, 96544981, 3471345337, 920660768, 4091103730, 196810490, 149 | 3381928201, 1109492717, 2355549125, 1586635448, 1797039055, 1638923588, 2503522690, 150 | 646601759, 4224649045, 2372882807, 579489529, 2708640721, 1931271310, 1151346004, 151 | 816810139, 2530535123, 2409011252, 1433450182, 451048430, 1972937109, 3009774909, 152 | 1293299123, 1348780427, 1599124760, 455979803, 126841693, 1818258609, 2922462427, 153 | 2984344477, 2505920889, 389352748, 206047828, 1560181411, 122533377, 1578405506, 154 | 1788641162, 895787831, 627481395, 3520465245, 1276780043, 2475905356, 3435941477, 155 | 3027881267, 3376670514, 3683225829, 390465397, 335590294, 2100175838, 4229888533, 156 | 3998653805, 2414170530, 1628254456, 4217109392, 133483025, 255841734, 3660421061, 157 | 790684578, 1700766087, 942682696, 4125075133, 2640660682, 1926137777, 1985476427, 158 | 628072684, 2973130811, 3119160259, 830224510, 449567249, 575334597, 1050069526, 159 | 292141093, 660028201, 3241681220, 3979259445, 1257904912, 1435849467, 1844161688, 160 | 3887625760, 2343238187, 2316113755, 1922610733, 1089684262, 66254511, 3387143064, 161 | 3520035243, 2977044471, 162 | ], 163 | 3200, 164 | Sign::Pos, 165 | 0, 166 | false, 167 | ) 168 | .unwrap(); 169 | assert!(c.cmp(&r) == 0); 170 | } 171 | 172 | #[test] 173 | #[cfg(not(target_arch = "x86"))] 174 | fn test_ln2_const() { 175 | let mut ln2 = Ln2Cache::new().unwrap(); 176 | let c = ln2.for_prec(3200, RoundingMode::ToEven).unwrap(); 177 | //println!("{:?}", c); 178 | let r = BigFloatNumber::from_raw_parts( 179 | &[ 180 | 3082069754683924605, 181 | 6283537028398873861, 182 | 27272943683312140, 183 | 7067101201094214402, 184 | 414657537012126457, 185 | 3954207892741588665, 186 | 845294622150838770, 187 | 4765234938047111433, 188 | 6814547362189857733, 189 | 7039123212900017103, 190 | 2777133410944596354, 191 | 10191454057530328917, 192 | 11633523313888349945, 193 | 4944993435491556494, 194 | 10868565595481147547, 195 | 6156621654544259124, 196 | 8473700360670835694, 197 | 5554677440240256317, 198 | 6868188547772629387, 199 | 544780923660251931, 200 | 12551880549572046001, 201 | 10762848267602590621, 202 | 884968683061185836, 203 | 526276848443620003, 204 | 7682155296647843458, 205 | 2695012071269245751, 206 | 5483728532390938973, 207 | 14757256277160841548, 208 | 14502689430025391411, 209 | 1677036114017882341, 210 | 9020186540394984342, 211 | 17174087324730849813, 212 | 6993299640500441506, 213 | 573305231163259792, 214 | 15721388746840462790, 215 | 7304734722601575330, 216 | 17717062790720533064, 217 | 8272698762445801674, 218 | 2697551639276418891, 219 | 13396691306361020475, 220 | 1930876632637913214, 221 | 4510014273271556293, 222 | 2834799538024855589, 223 | 17090789181815791940, 224 | 6166926504001936144, 225 | 16697225500131306648, 226 | 9947632833883994667, 227 | 4680158270178506285, 228 | 14547668686819489455, 229 | 12786308645202655659, 230 | ], 231 | 3200, 232 | Sign::Pos, 233 | 0, 234 | false, 235 | ) 236 | .unwrap(); 237 | assert!(c.cmp(&r) == 0); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/consts/mod.rs: -------------------------------------------------------------------------------- 1 | mod e; 2 | mod ln10; 3 | mod ln2; 4 | mod pi; 5 | 6 | use crate::common::buf::WordBuf; 7 | use crate::common::util::round_p; 8 | use crate::mantissa::Mantissa; 9 | use crate::num::BigFloatNumber; 10 | use crate::ops::consts::e::ECache; 11 | use crate::ops::consts::ln10::Ln10Cache; 12 | use crate::ops::consts::ln2::Ln2Cache; 13 | use crate::ops::consts::pi::PiCache; 14 | use crate::BigFloat; 15 | use crate::Error; 16 | use crate::RoundingMode; 17 | 18 | #[cfg(not(feature = "std"))] 19 | use alloc::vec::Vec; 20 | 21 | /// Constants cache contains arbitrary-precision mathematical constants. 22 | #[derive(Debug)] 23 | pub struct Consts { 24 | pi: PiCache, 25 | e: ECache, 26 | ln2: Ln2Cache, 27 | ln10: Ln10Cache, 28 | tenpowers: Vec<(WordBuf, WordBuf, usize)>, 29 | } 30 | 31 | /// In an ideal situation, the `Consts` structure is initialized with `Consts::new` only once, 32 | /// and then used where needed. 33 | impl Consts { 34 | /// Initializes the constants cache. 35 | /// 36 | /// ## Errors 37 | /// 38 | /// - MemoryAllocation: failed to allocate memory for mantissa. 39 | pub fn new() -> Result { 40 | Ok(Consts { 41 | pi: PiCache::new()?, 42 | e: ECache::new()?, 43 | ln2: Ln2Cache::new()?, 44 | ln10: Ln10Cache::new()?, 45 | tenpowers: Vec::new(), 46 | }) 47 | } 48 | 49 | /// Returns the value of the pi number with precision `p` using rounding mode `rm`. 50 | /// Precision is rounded upwards to the word size. 51 | /// 52 | /// ## Errors 53 | /// 54 | /// - MemoryAllocation: failed to allocate memory for mantissa. 55 | /// - InvalidArgument: the precision is incorrect. 56 | pub(crate) fn pi_num(&mut self, p: usize, rm: RoundingMode) -> Result { 57 | let p = round_p(p); 58 | self.pi.for_prec(p, rm) 59 | } 60 | 61 | /// Returns the value of the Euler number with precision `p` using rounding mode `rm`. 62 | /// Precision is rounded upwards to the word size. 63 | /// 64 | /// ## Errors 65 | /// 66 | /// - MemoryAllocation: failed to allocate memory for mantissa. 67 | /// - InvalidArgument: the precision is incorrect. 68 | pub(crate) fn e_num(&mut self, p: usize, rm: RoundingMode) -> Result { 69 | let p = round_p(p); 70 | self.e.for_prec(p, rm) 71 | } 72 | 73 | /// Returns the value of the natural logarithm of 2 with precision `p` using rounding mode `rm`. 74 | /// Precision is rounded upwards to the word size. 75 | /// 76 | /// ## Errors 77 | /// 78 | /// - MemoryAllocation: failed to allocate memory for mantissa. 79 | /// - InvalidArgument: the precision is incorrect. 80 | pub(crate) fn ln_2_num(&mut self, p: usize, rm: RoundingMode) -> Result { 81 | let p = round_p(p); 82 | self.ln2.for_prec(p, rm) 83 | } 84 | 85 | /// Returns the value of the natural logarithm of 10 with precision `p` using rounding mode `rm`. 86 | /// Precision is rounded upwards to the word size. 87 | /// 88 | /// ## Errors 89 | /// 90 | /// - MemoryAllocation: failed to allocate memory for mantissa. 91 | /// - InvalidArgument: the precision is incorrect. 92 | pub(crate) fn ln_10_num( 93 | &mut self, 94 | p: usize, 95 | rm: RoundingMode, 96 | ) -> Result { 97 | let p = round_p(p); 98 | self.ln10.for_prec(p, rm) 99 | } 100 | 101 | /// Returns the value of the pi number with precision `p` using rounding mode `rm`. 102 | /// Precision is rounded upwards to the word size. 103 | pub fn pi(&mut self, p: usize, rm: RoundingMode) -> BigFloat { 104 | match self.pi_num(p, rm) { 105 | Ok(v) => v.into(), 106 | Err(e) => BigFloat::nan(Some(e)), 107 | } 108 | } 109 | 110 | /// Returns the value of the Euler number with precision `p` using rounding mode `rm`. 111 | /// Precision is rounded upwards to the word size. 112 | pub fn e(&mut self, p: usize, rm: RoundingMode) -> BigFloat { 113 | match self.e_num(p, rm) { 114 | Ok(v) => v.into(), 115 | Err(e) => BigFloat::nan(Some(e)), 116 | } 117 | } 118 | 119 | /// Returns the value of the natural logarithm of 2 with precision `p` using rounding mode `rm`. 120 | /// Precision is rounded upwards to the word size. 121 | pub fn ln_2(&mut self, p: usize, rm: RoundingMode) -> BigFloat { 122 | match self.ln_2_num(p, rm) { 123 | Ok(v) => v.into(), 124 | Err(e) => BigFloat::nan(Some(e)), 125 | } 126 | } 127 | 128 | /// Returns the value of the natural logarithm of 10 with precision `p` using rounding mode `rm`. 129 | /// Precision is rounded upwards to the word size. 130 | pub fn ln_10(&mut self, p: usize, rm: RoundingMode) -> BigFloat { 131 | match self.ln_10_num(p, rm) { 132 | Ok(v) => v.into(), 133 | Err(e) => BigFloat::nan(Some(e)), 134 | } 135 | } 136 | 137 | /// Return powers of 10: 100, 10000, 100000000, ... 138 | pub(crate) fn tenpowers(&mut self, p: usize) -> Result<&[(WordBuf, WordBuf, usize)], Error> { 139 | if p >= self.tenpowers.len() { 140 | Mantissa::compute_tenpowers(&mut self.tenpowers, p)?; 141 | } 142 | 143 | Ok(&self.tenpowers) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/consts/pi.rs: -------------------------------------------------------------------------------- 1 | //! π number 2 | 3 | use crate::common::util::round_p; 4 | use crate::defs::{Error, WORD_BIT_SIZE}; 5 | use crate::num::BigFloatNumber; 6 | use crate::RoundingMode; 7 | 8 | fn pqr(a: u64, b: u64) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber), Error> { 9 | if a == b - 1 { 10 | let n0 = BigFloatNumber::from_u64(6 * b - 5, 64)?; 11 | let n1 = BigFloatNumber::from_u64(2 * b - 1, 64)?; 12 | let n2 = BigFloatNumber::from_u64(6 * b - 1, 64)?; 13 | 14 | let n3 = n0.mul_full_prec(&n1)?; 15 | let r = n3.mul_full_prec(&n2)?; 16 | 17 | let n0 = BigFloatNumber::from_u64(10939058860032000, 64)?; 18 | let n1 = BigFloatNumber::from_u64(b, 64)?; 19 | let n2 = n1.mul_full_prec(&n1)?; 20 | let n3 = n2.mul_full_prec(&n1)?; 21 | let q = n0.mul_full_prec(&n3)?; 22 | 23 | let n0 = BigFloatNumber::from_u64(13591409 + 545140134 * b, 64)?; 24 | let mut p = r.mul_full_prec(&n0)?; 25 | 26 | if b & 1 != 0 { 27 | p.inv_sign(); 28 | } 29 | 30 | Ok((p, q, r)) 31 | } else { 32 | let m = (a + b) / 2; 33 | 34 | let (pa, qa, ra) = pqr(a, m)?; 35 | let (pb, qb, rb) = pqr(m, b)?; 36 | 37 | let r = ra.mul_full_prec(&rb)?; 38 | let q = qa.mul_full_prec(&qb)?; 39 | let n0 = pa.mul_full_prec(&qb)?; 40 | let n1 = pb.mul_full_prec(&ra)?; 41 | let p = n0.add_full_prec(&n1)?; 42 | 43 | Ok((p, q, r)) 44 | } 45 | } 46 | 47 | fn pqr_inc( 48 | pa: &BigFloatNumber, 49 | qa: &BigFloatNumber, 50 | ra: &BigFloatNumber, 51 | m: u64, 52 | ) -> Result<(BigFloatNumber, BigFloatNumber, BigFloatNumber, u64), Error> { 53 | let b = m * 2; 54 | 55 | let (pb, qb, rb) = pqr(m, b)?; 56 | 57 | let r = ra.mul_full_prec(&rb)?; 58 | let q = qa.mul_full_prec(&qb)?; 59 | let n0 = pa.mul_full_prec(&qb)?; 60 | let n1 = pb.mul_full_prec(ra)?; 61 | let p = n0.add_full_prec(&n1)?; 62 | 63 | Ok((p, q, r, b)) 64 | } 65 | 66 | /// Holds value of currently computed PI. 67 | #[derive(Debug)] 68 | pub struct PiCache { 69 | b: u64, 70 | pk: BigFloatNumber, 71 | qk: BigFloatNumber, 72 | rk: BigFloatNumber, 73 | val: BigFloatNumber, 74 | } 75 | 76 | impl PiCache { 77 | fn calc_pi(p: &BigFloatNumber, q: &BigFloatNumber, k: usize) -> Result { 78 | // q*4270934400 / ((p + q*13591409) * sqrt(10005)) 79 | let n0 = BigFloatNumber::from_word(4270934400, 1)?; 80 | let n1 = BigFloatNumber::from_word(13591409, 1)?; 81 | 82 | let q0 = q.mul_full_prec(&n0)?; 83 | let q1 = q.mul_full_prec(&n1)?; 84 | let p0 = p.add_full_prec(&q1)?; 85 | 86 | let f3 = BigFloatNumber::from_word(10005, 1)?; 87 | let f4 = f3.sqrt(k, RoundingMode::None)?; 88 | let prec = p0.mantissa_max_bit_len().max(f4.mantissa_max_bit_len()); 89 | let f5 = p0.mul(&f4, prec, RoundingMode::None)?; 90 | 91 | let prec = q0.mantissa_max_bit_len().max(f5.mantissa_max_bit_len()); 92 | let ret = q0.div(&f5, prec, RoundingMode::None)?; 93 | 94 | Ok(ret) 95 | } 96 | 97 | pub fn new() -> Result { 98 | let (p01, q01, r01) = pqr(0, 1)?; 99 | 100 | let val = Self::calc_pi(&p01, &q01, 1)?; 101 | Ok(PiCache { 102 | b: 1, 103 | pk: p01, 104 | qk: q01, 105 | rk: r01, 106 | val, 107 | }) 108 | } 109 | 110 | /// Return value of PI with precision `k`. 111 | pub(crate) fn for_prec(&mut self, k: usize, rm: RoundingMode) -> Result { 112 | let mut p_inc = WORD_BIT_SIZE; 113 | let mut p_wrk = round_p(k) + p_inc; 114 | 115 | loop { 116 | let kext = (k + 46 + WORD_BIT_SIZE) / 47; 117 | 118 | if self.b > kext as u64 { 119 | let mut ret = self.val.clone()?; 120 | 121 | if ret.try_set_precision(k, rm, p_wrk)? { 122 | return Ok(ret); 123 | } 124 | 125 | p_wrk += p_inc; 126 | p_inc = round_p(p_wrk / 5); 127 | } 128 | 129 | let mut pk; 130 | let mut qk; 131 | let mut rk; 132 | let mut bb; 133 | 134 | (pk, qk, rk, bb) = pqr_inc(&self.pk, &self.qk, &self.rk, self.b)?; 135 | 136 | while bb <= kext as u64 { 137 | (pk, qk, rk, bb) = pqr_inc(&pk, &qk, &rk, bb)?; 138 | } 139 | 140 | self.val = Self::calc_pi(&pk, &qk, bb as usize * 47)?; 141 | 142 | self.pk = pk; 143 | self.qk = qk; 144 | self.rk = rk; 145 | self.b = bb; 146 | } 147 | } 148 | } 149 | 150 | #[cfg(test)] 151 | mod tests { 152 | 153 | use super::*; 154 | use crate::{RoundingMode, Sign}; 155 | 156 | #[test] 157 | #[cfg(target_arch = "x86")] 158 | fn test_pi_const() { 159 | let mut pi = PiCache::new().unwrap(); 160 | let c = pi.for_prec(320, RoundingMode::ToEven).unwrap(); 161 | //println!("pi {:?}", c); 162 | let r = BigFloatNumber::from_raw_parts( 163 | &[ 164 | 2385773790, 1363806329, 991140642, 34324134, 2322058356, 688016904, 2161908945, 165 | 3301335691, 560513588, 3373259426, 166 | ], 167 | 320, 168 | Sign::Pos, 169 | 2, 170 | false, 171 | ) 172 | .unwrap(); 173 | assert!(c.cmp(&r) == 0); 174 | } 175 | 176 | #[test] 177 | #[cfg(not(target_arch = "x86"))] 178 | fn test_pi_const() { 179 | let mut pi = PiCache::new().unwrap(); 180 | let c = pi.for_prec(320, RoundingMode::ToEven).unwrap(); 181 | //println!("pi {:?}", c); 182 | let r = BigFloatNumber::from_raw_parts( 183 | &[ 184 | 5857503583518590174, 185 | 147421033984662306, 186 | 2955010104097229940, 187 | 14179128828124470481, 188 | 14488038916154245684, 189 | ], 190 | 320, 191 | Sign::Pos, 192 | 2, 193 | false, 194 | ) 195 | .unwrap(); 196 | assert!(c.cmp(&r) == 0); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/cosh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic cocosine. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::round_p; 5 | use crate::defs::Error; 6 | use crate::defs::RoundingMode; 7 | use crate::num::BigFloatNumber; 8 | use crate::ops::util::compute_small_exp; 9 | use crate::Consts; 10 | use crate::Sign; 11 | use crate::WORD_BIT_SIZE; 12 | 13 | impl BigFloatNumber { 14 | /// Computes the hyperbolic cosine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 15 | /// This function requires constants cache cc for computing the result. 16 | /// Precision is rounded upwards to the word size. 17 | /// 18 | /// ## Errors 19 | /// 20 | /// - MemoryAllocation: failed to allocate memory. 21 | /// - InvalidArgument: the precision is incorrect. 22 | pub fn cosh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 23 | let p = round_p(p); 24 | 25 | if self.is_zero() { 26 | let mut ret = Self::from_word(1, p)?; 27 | ret.set_inexact(self.inexact()); 28 | return Ok(ret); 29 | } 30 | 31 | let mut p_inc = WORD_BIT_SIZE; 32 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 33 | 34 | compute_small_exp!(ONE, self.exponent() as isize * 2 - 1, false, p_wrk, p, rm); 35 | 36 | p_wrk += p_inc; 37 | 38 | let mut x = self.clone()?; 39 | x.set_inexact(false); 40 | 41 | loop { 42 | let p_x = p_wrk + 4; 43 | x.set_precision(p_x, RoundingMode::None)?; 44 | 45 | x.set_sign(Sign::Pos); 46 | 47 | let mut ret = if (x.exponent() as isize - 1) * 2 > x.mantissa_max_bit_len() as isize + 2 48 | { 49 | // e^x / 2 50 | 51 | x.exp(p_x, RoundingMode::None, cc) 52 | } else { 53 | // (e^x + e^(-x)) / 2 54 | 55 | let ex = x.exp(p_x, RoundingMode::None, cc)?; 56 | 57 | let xe = ex.reciprocal(p_x, RoundingMode::None)?; 58 | 59 | ex.add(&xe, p_x, RoundingMode::None) 60 | }?; 61 | 62 | ret.div_by_2(RoundingMode::None); 63 | 64 | if ret.try_set_precision(p, rm, p_wrk)? { 65 | ret.set_inexact(ret.inexact() | self.inexact()); 66 | break Ok(ret); 67 | } 68 | 69 | p_wrk += p_inc; 70 | p_inc = round_p(p_wrk / 5); 71 | } 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | 78 | use crate::{common::util::random_subnormal, Sign}; 79 | 80 | use super::*; 81 | 82 | #[test] 83 | fn test_cosh() { 84 | let p = 320; 85 | let mut cc = Consts::new().unwrap(); 86 | let rm = RoundingMode::ToEven; 87 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 88 | n1.set_exponent(0); 89 | let _n2 = n1.cosh(p, rm, &mut cc).unwrap(); 90 | //println!("{:?}", n2.format(crate::Radix::Bin, rm).unwrap()); 91 | 92 | // asymptotic & extrema testing 93 | let p = 640; 94 | let n1 = BigFloatNumber::parse( 95 | "1.0111001e-1000000", 96 | crate::Radix::Bin, 97 | p, 98 | RoundingMode::None, 99 | &mut cc, 100 | ) 101 | .unwrap(); 102 | let n2 = n1.cosh(p, rm, &mut cc).unwrap(); 103 | let n3 = BigFloatNumber::parse("1.000000000000000000000000000000010B6200000000000000000000000000002E8B9840AAAAAAAAAAAAAAAAAAAAAAAAADE85C5950B78E38E38E38E38E38E38E3902814A92D7C21CDB6DB6DB6DB6DB6E_e+0", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 104 | 105 | assert!(n2.cmp(&n3) == 0); 106 | 107 | let d1 = BigFloatNumber::max_value(p).unwrap(); 108 | let d2 = BigFloatNumber::min_value(p).unwrap(); 109 | 110 | assert!(d1.cosh(p, rm, &mut cc).unwrap_err() == Error::ExponentOverflow(Sign::Pos)); 111 | assert!(d2.cosh(p, rm, &mut cc).unwrap_err() == Error::ExponentOverflow(Sign::Pos)); 112 | 113 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 114 | let zero = BigFloatNumber::new(1).unwrap(); 115 | let d4 = random_subnormal(p); 116 | 117 | assert!(d3.cosh(p, rm, &mut cc).unwrap().cmp(&ONE) == 0); 118 | assert!(zero.cosh(p, rm, &mut cc).unwrap().cmp(&ONE) == 0); 119 | assert!(d4.cosh(p, rm, &mut cc).unwrap().cmp(&ONE) == 0); 120 | } 121 | 122 | #[ignore] 123 | #[test] 124 | #[cfg(feature = "std")] 125 | fn cosh_perf() { 126 | let p = 320; 127 | let mut cc = Consts::new().unwrap(); 128 | let mut n = vec![]; 129 | for _ in 0..10000 { 130 | n.push(BigFloatNumber::random_normal(p, -20, 20).unwrap()); 131 | } 132 | 133 | for _ in 0..5 { 134 | let start_time = std::time::Instant::now(); 135 | for ni in n.iter() { 136 | let _f = ni.cosh(p, RoundingMode::ToEven, &mut cc).unwrap(); 137 | } 138 | let time = start_time.elapsed(); 139 | println!("{}", time.as_millis()); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/mod.rs: -------------------------------------------------------------------------------- 1 | //! High-level operations on the numbers. 2 | 3 | mod acos; 4 | mod acosh; 5 | mod asin; 6 | mod asinh; 7 | mod atan; 8 | mod atanh; 9 | mod cbrt; 10 | pub mod consts; 11 | mod cos; 12 | mod cosh; 13 | mod log; 14 | mod pow; 15 | mod series; 16 | mod sin; 17 | mod sinh; 18 | mod sqrt; 19 | mod tan; 20 | mod tanh; 21 | mod util; 22 | 23 | #[cfg(test)] 24 | mod tests; 25 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/sinh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic sine. 2 | 3 | use crate::common::util::round_p; 4 | use crate::defs::Error; 5 | use crate::defs::RoundingMode; 6 | use crate::num::BigFloatNumber; 7 | use crate::ops::util::compute_small_exp; 8 | use crate::Consts; 9 | use crate::Sign; 10 | use crate::WORD_BIT_SIZE; 11 | 12 | impl BigFloatNumber { 13 | /// Computes the hyperbolic sine of a number with precision `p`. The result is rounded using the rounding mode `rm`. 14 | /// This function requires constants cache cc for computing the result. 15 | /// Precision is rounded upwards to the word size. 16 | /// 17 | /// ## Errors 18 | /// 19 | /// - MemoryAllocation: failed to allocate memory. 20 | /// - InvalidArgument: the precision is incorrect. 21 | pub fn sinh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 22 | let p = round_p(p); 23 | 24 | if self.is_zero() { 25 | return Self::new2(p, self.sign(), self.inexact()); 26 | } 27 | 28 | let mut p_inc = WORD_BIT_SIZE; 29 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 30 | 31 | compute_small_exp!(self, self.exponent() as isize * 2 - 2, false, p_wrk, p, rm); 32 | 33 | p_wrk += p_inc; 34 | 35 | let mut x = self.clone()?; 36 | x.set_inexact(false); 37 | x.set_sign(Sign::Pos); 38 | 39 | let ethres = (x.exponent().unsigned_abs().max(1) as usize - 1) * 2; 40 | 41 | loop { 42 | let p_x = p_wrk + 4; 43 | x.set_precision(p_x, RoundingMode::None)?; 44 | 45 | let mut ret = if x.exponent() <= 0 { 46 | Self::sinh_series(x.clone()?, p_x, RoundingMode::None)? 47 | } else { 48 | let mut val = if ethres > x.mantissa_max_bit_len() + 2 { 49 | // e^|x| / 2 * signum(self) 50 | 51 | x.exp(p_x, RoundingMode::None, cc) 52 | } else { 53 | // (e^x - e^(-x)) / 2 54 | 55 | let ex = match x.exp(p_x, RoundingMode::None, cc) { 56 | Ok(val) => val, 57 | Err(Error::ExponentOverflow(_)) => { 58 | return Err(Error::ExponentOverflow(self.sign())); 59 | } 60 | Err(err) => return Err(err), 61 | }; 62 | 63 | let xe = ex.reciprocal(p_x, RoundingMode::None)?; 64 | 65 | ex.sub(&xe, p_x, RoundingMode::None) 66 | } 67 | .map_err(|e| -> Error { 68 | if let Error::ExponentOverflow(_) = e { 69 | Error::ExponentOverflow(self.sign()) 70 | } else { 71 | e 72 | } 73 | })?; 74 | 75 | val.div_by_2(RoundingMode::None); 76 | 77 | val 78 | }; 79 | 80 | ret.set_sign(self.sign()); 81 | 82 | if ret.try_set_precision(p, rm, p_wrk)? { 83 | ret.set_inexact(ret.inexact() | self.inexact()); 84 | break Ok(ret); 85 | } 86 | 87 | p_wrk += p_inc; 88 | p_inc = round_p(p_wrk / 5); 89 | } 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod tests { 95 | 96 | use crate::{common::util::random_subnormal, Sign}; 97 | 98 | use super::*; 99 | 100 | #[test] 101 | fn test_sinh() { 102 | let p = 32000; 103 | let mut cc = Consts::new().unwrap(); 104 | let rm = RoundingMode::ToEven; 105 | let mut n1 = BigFloatNumber::from_word(1, 1).unwrap(); 106 | n1.set_exponent(0); 107 | let _n2 = n1.sinh(p, rm, &mut cc).unwrap(); 108 | //println!("{:?}", n2.fp3(crate::Radix::Dec, rm).unwrap()); 109 | 110 | let d1 = BigFloatNumber::max_value(p).unwrap(); 111 | let d2 = BigFloatNumber::min_value(p).unwrap(); 112 | 113 | assert!(d1.sinh(p, rm, &mut cc).unwrap_err() == Error::ExponentOverflow(Sign::Pos)); 114 | assert!(d2.sinh(p, rm, &mut cc).unwrap_err() == Error::ExponentOverflow(Sign::Neg)); 115 | 116 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 117 | let zero = BigFloatNumber::new(1).unwrap(); 118 | let n1 = random_subnormal(p); 119 | 120 | assert!(d3.sinh(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 121 | assert!(zero.sinh(p, rm, &mut cc).unwrap().is_zero()); 122 | assert!(n1.sinh(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 123 | 124 | let mut large_pos = BigFloatNumber::from_word(1, 256).unwrap(); 125 | large_pos.set_exponent(150); 126 | assert_eq!( 127 | large_pos.sinh(p, rm, &mut cc).unwrap_err(), 128 | Error::ExponentOverflow(Sign::Pos) 129 | ); 130 | let mut large_neg = BigFloatNumber::from_word(1, 256).unwrap(); 131 | large_neg.set_exponent(150); 132 | large_neg.set_sign(Sign::Neg); 133 | assert_eq!( 134 | large_neg.sinh(p, rm, &mut cc).unwrap_err(), 135 | Error::ExponentOverflow(Sign::Neg) 136 | ); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/sqrt.rs: -------------------------------------------------------------------------------- 1 | //! Sqrt computation. 2 | 3 | use crate::common::util::round_p; 4 | use crate::Sign; 5 | use crate::{ 6 | defs::{Error, EXPONENT_MIN}, 7 | num::BigFloatNumber, 8 | Exponent, RoundingMode, 9 | }; 10 | 11 | impl BigFloatNumber { 12 | /// Computes the square root of a number with precision `p`. The result is rounded using the rounding mode `rm`. 13 | /// Precision is rounded upwards to the word size. 14 | /// 15 | /// ## Errors 16 | /// 17 | /// - InvalidArgument: argument is negative, or the precision is incorrect. 18 | /// - MemoryAllocation: failed to allocate memory. 19 | pub fn sqrt(&self, p: usize, rm: RoundingMode) -> Result { 20 | let p = round_p(p); 21 | Self::p_assertion(p)?; 22 | 23 | if self.is_zero() { 24 | return Self::new2(p, self.sign(), self.inexact()); 25 | } 26 | 27 | if self.is_negative() { 28 | return Err(Error::InvalidArgument); 29 | } 30 | 31 | let (e1, m1_opt) = self.normalize()?; 32 | let m1_normalized = m1_opt.as_ref().unwrap_or_else(|| self.mantissa()); 33 | 34 | let mut inexact = self.inexact(); 35 | 36 | let (e_shift, m3) = m1_normalized.sqrt(p, rm, true, &mut inexact, e1 & 1 == 1)?; 37 | 38 | let e = (e1 + (e1 & 1)) / 2 + e_shift; 39 | 40 | if e < EXPONENT_MIN as isize { 41 | let mut ret = BigFloatNumber::from_raw_unchecked(m3, Sign::Pos, EXPONENT_MIN, inexact); 42 | 43 | ret.subnormalize(e, rm); 44 | 45 | Ok(ret) 46 | } else { 47 | Ok(BigFloatNumber::from_raw_unchecked( 48 | m3, 49 | Sign::Pos, 50 | e as Exponent, 51 | inexact, 52 | )) 53 | } 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | mod tests { 59 | 60 | use super::*; 61 | use crate::common::consts::ONE; 62 | use crate::{common::util::random_subnormal, defs::WORD_BIT_SIZE, Exponent}; 63 | use crate::{Consts, Sign, EXPONENT_MAX}; 64 | 65 | #[test] 66 | fn test_sqrt() { 67 | let mut cc = Consts::new().unwrap(); 68 | 69 | /* let n1 = BigFloatNumber::from_words( 70 | &[18446744073709551614, 18446744073709551615], 71 | Sign::Pos, 0).unwrap(); 72 | let n2 = n1.sqrt(384, RoundingMode::ToEven).unwrap(); 73 | println!("{:?}", n2.format(crate::Radix::Bin, RoundingMode::None).unwrap()); */ 74 | 75 | // near 1 76 | let p = 320; 77 | let d1 = BigFloatNumber::parse( 78 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2DC85F7E77EC4872DC85F7E77EC487_e-1", 79 | crate::Radix::Hex, 80 | p, 81 | RoundingMode::None, 82 | &mut cc, 83 | ) 84 | .unwrap(); 85 | let d2 = d1.sqrt(p, RoundingMode::ToEven).unwrap(); 86 | let d3 = BigFloatNumber::parse( 87 | "F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF96E42FBF3BF624396E42FBF3BF6243_e-1", 88 | crate::Radix::Hex, 89 | p, 90 | RoundingMode::None, 91 | &mut cc, 92 | ) 93 | .unwrap(); 94 | 95 | //println!("{:?}", d2.format(crate::Radix::Hex, RoundingMode::None).unwrap()); 96 | 97 | assert!(d2.cmp(&d3) == 0); 98 | 99 | let d1 = BigFloatNumber::parse( 100 | "1.00000000000000000000000000000000000000000000000000000000000000002DC85F7E77EC487C", 101 | crate::Radix::Hex, 102 | p, 103 | RoundingMode::None, 104 | &mut cc, 105 | ) 106 | .unwrap(); 107 | let d2 = d1.sqrt(p, RoundingMode::ToEven).unwrap(); 108 | let d3 = BigFloatNumber::parse( 109 | "1.000000000000000000000000000000000000000000000000000000000000000016E42FBF3BF6243E", 110 | crate::Radix::Hex, 111 | p, 112 | RoundingMode::None, 113 | &mut cc, 114 | ) 115 | .unwrap(); 116 | 117 | //println!("{:?}", d2.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 118 | 119 | assert!(d2.cmp(&d3) == 0); 120 | 121 | // MAX 122 | let prec = 3200; 123 | let mut eps = ONE.clone().unwrap(); 124 | 125 | let d1 = BigFloatNumber::max_value(prec).unwrap(); 126 | let d2 = d1.sqrt(prec, RoundingMode::ToEven).unwrap(); 127 | let d3 = d2.mul(&d2, prec, RoundingMode::ToEven).unwrap(); 128 | eps.set_exponent(d1.exponent() - prec as Exponent + 2); 129 | assert!( 130 | d1.sub(&d3, prec, RoundingMode::ToEven) 131 | .unwrap() 132 | .abs() 133 | .unwrap() 134 | .cmp(&eps) 135 | < 0 136 | ); 137 | 138 | // MIN 139 | let d1 = BigFloatNumber::min_positive(prec).unwrap(); 140 | let d2 = d1.sqrt(prec, RoundingMode::ToEven).unwrap(); 141 | let d3 = d2.mul(&d2, prec, RoundingMode::ToEven).unwrap(); 142 | let eps = BigFloatNumber::min_positive(prec).unwrap(); 143 | assert!( 144 | d1.sub(&d3, prec, RoundingMode::ToEven) 145 | .unwrap() 146 | .abs() 147 | .unwrap() 148 | .cmp(&eps) 149 | <= 0 150 | ); 151 | 152 | // random 153 | let mut eps = ONE.clone().unwrap(); 154 | 155 | for _ in 0..1000 { 156 | let prec = (rand::random::() % 32 + 1) * WORD_BIT_SIZE; 157 | 158 | let mut d1 = BigFloatNumber::random_normal(prec, EXPONENT_MIN, EXPONENT_MAX).unwrap(); 159 | if d1.is_negative() { 160 | d1.inv_sign(); 161 | } 162 | 163 | let d2 = d1.sqrt(prec, RoundingMode::ToEven).unwrap(); 164 | let d3 = d2.mul(&d2, prec, RoundingMode::ToEven).unwrap(); 165 | 166 | eps.set_exponent(d1.exponent() - prec as Exponent + 2); 167 | 168 | //println!("d1 {:?}\nd3 {:?}", d1, d3); 169 | 170 | assert!( 171 | d1.sub(&d3, prec, RoundingMode::ToEven) 172 | .unwrap() 173 | .abs() 174 | .unwrap() 175 | .cmp(&eps) 176 | < 0 177 | ); 178 | } 179 | 180 | // random subnormal arg 181 | for _ in 0..1000 { 182 | let mut d1 = random_subnormal(prec); 183 | d1.set_sign(Sign::Pos); 184 | 185 | let d2 = d1.sqrt(prec, RoundingMode::ToEven).unwrap(); 186 | let d3 = d2.mul(&d2, prec, RoundingMode::ToEven).unwrap(); 187 | 188 | let eps = BigFloatNumber::min_positive(prec).unwrap(); 189 | // eps.set_exponent(eps.get_exponent() + 2); 190 | 191 | // println!("{:?}", d1.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 192 | // println!("{:?}", d3.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 193 | // println!("{:?}", eps.format(crate::Radix::Bin, RoundingMode::None).unwrap()); 194 | 195 | assert!( 196 | d1.sub(&d3, prec, RoundingMode::ToEven) 197 | .unwrap() 198 | .abs() 199 | .unwrap() 200 | .cmp(&eps) 201 | <= 0 202 | ); 203 | } 204 | } 205 | 206 | #[ignore] 207 | #[test] 208 | #[cfg(feature = "std")] 209 | fn sqrt_perf() { 210 | let mut n = vec![]; 211 | let p = 132; 212 | for _ in 0..100000 { 213 | let mut n0 = BigFloatNumber::random_normal(p, -0, 0).unwrap(); 214 | n0.set_sign(Sign::Pos); 215 | n.push(n0); 216 | } 217 | 218 | for _ in 0..5 { 219 | let start_time = std::time::Instant::now(); 220 | for ni in n.iter() { 221 | let _f = ni.sqrt(p, RoundingMode::ToEven).unwrap(); 222 | } 223 | let time = start_time.elapsed(); 224 | println!("{}", time.as_millis()); 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/tanh.rs: -------------------------------------------------------------------------------- 1 | //! Hyperbolic tangent. 2 | 3 | use crate::common::consts::ONE; 4 | use crate::common::util::round_p; 5 | use crate::defs::Error; 6 | use crate::defs::RoundingMode; 7 | use crate::defs::EXPONENT_MAX; 8 | use crate::num::BigFloatNumber; 9 | use crate::ops::util::compute_small_exp; 10 | use crate::Consts; 11 | use crate::Sign; 12 | use crate::WORD_BIT_SIZE; 13 | 14 | impl BigFloatNumber { 15 | /// Computes the hyperbolic tangent of a number with precision `p`. The result is rounded using the rounding mode `rm`. 16 | /// This function requires constants cache `cc` for computing the result. 17 | /// Precision is rounded upwards to the word size. 18 | /// 19 | /// ## Errors 20 | /// 21 | /// - ExponentOverflow: the result is too large or too small number. 22 | /// - MemoryAllocation: failed to allocate memory. 23 | /// - InvalidArgument: the precision is incorrect. 24 | pub fn tanh(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result { 25 | let p = round_p(p); 26 | 27 | if self.is_zero() { 28 | return Self::new2(p, self.sign(), self.inexact()); 29 | } 30 | 31 | // prevent overflow 32 | if self.exponent() == EXPONENT_MAX { 33 | let mut ret = Self::from_i8(self.sign().to_int(), p)?; 34 | ret = ret.add_correction(true)?; 35 | ret.set_precision(p, rm)?; 36 | return Ok(ret); 37 | } 38 | 39 | let mut p_inc = WORD_BIT_SIZE; 40 | let mut p_wrk = p.max(self.mantissa_max_bit_len()); 41 | 42 | compute_small_exp!(self, self.exponent() as isize * 2 - 1, true, p_wrk, p, rm); 43 | 44 | // (e^(2*x) - 1) / (e^(2*x) + 1) 45 | 46 | let mut additional_prec = 4; 47 | if self.exponent() < 0 { 48 | additional_prec += self.exponent().unsigned_abs() as usize; 49 | } 50 | 51 | p_wrk += p_inc; 52 | 53 | let mut x = self.clone()?; 54 | x.set_inexact(false); 55 | x.set_sign(Sign::Pos); 56 | x.set_exponent(x.exponent() + 1); 57 | 58 | loop { 59 | let p_x = p_wrk + additional_prec; 60 | x.set_precision(p_x, RoundingMode::None)?; 61 | 62 | let xexp = match x.exp(p_x, RoundingMode::FromZero, cc) { 63 | Ok(v) => Ok(v), 64 | Err(e) => match e { 65 | Error::ExponentOverflow(_) => { 66 | return self.process_large_exp(p, rm); 67 | } 68 | Error::DivisionByZero => Err(Error::DivisionByZero), 69 | Error::InvalidArgument => Err(Error::InvalidArgument), 70 | Error::MemoryAllocation => Err(Error::MemoryAllocation), 71 | }, 72 | }?; 73 | 74 | if xexp.exponent() as isize > p_x as isize { 75 | return self.process_large_exp(p, rm); 76 | } 77 | 78 | let d1 = xexp.sub(&ONE, p_x, RoundingMode::None)?; 79 | let d2 = xexp.add(&ONE, p_x, RoundingMode::None)?; 80 | 81 | let mut ret = d1.div(&d2, p_x, RoundingMode::None)?; 82 | 83 | ret.set_sign(self.sign()); 84 | 85 | if ret.try_set_precision(p, rm, p_wrk)? { 86 | ret.set_inexact(ret.inexact() | self.inexact()); 87 | break Ok(ret); 88 | } 89 | 90 | p_wrk += p_inc; 91 | p_inc = round_p(p_wrk / 5); 92 | } 93 | } 94 | 95 | fn process_large_exp(&self, p: usize, rm: RoundingMode) -> Result { 96 | let rm_mask = if self.is_positive() { 97 | 0b1100 // rounding down or to zero 98 | } else { 99 | 0b1010 // rounding up or to zero 100 | }; 101 | 102 | let mut ret = if rm as u32 & rm_mask != 0 { 103 | let mut ret = BigFloatNumber::max_value(p)?; 104 | ret.set_sign(self.sign()); 105 | ret.set_exponent(0); 106 | ret 107 | } else { 108 | Self::from_i8(self.sign().to_int(), p)? 109 | }; 110 | 111 | ret.set_inexact(true); 112 | 113 | Ok(ret) 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | 120 | use crate::common::util::random_subnormal; 121 | 122 | use super::*; 123 | 124 | #[test] 125 | fn test_tanh() { 126 | let p = 320; 127 | let mut cc = Consts::new().unwrap(); 128 | let rm = RoundingMode::ToEven; 129 | let mut n1 = BigFloatNumber::from_word(1, p).unwrap(); 130 | n1.set_exponent(0); 131 | let _n2 = n1.tanh(p, rm, &mut cc).unwrap(); 132 | //println!("{:?}", n2.format(crate::Radix::Dec, rm).unwrap()); 133 | 134 | // asymptotic & extrema testing 135 | let p = 640; 136 | let n1 = BigFloatNumber::parse("8.00000000000000100000000000000010B6200000000000000000000000000002E8B9840AAAAAAAAAAAAAAAAAAAAAAAAADE85C5950B78E38E38E38E38E38E38E3902814A92D7C21CDB6DB6DB6DB6DB6E_e+1", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 137 | let n2 = n1.tanh(p, rm, &mut cc).unwrap(); 138 | let n3 = BigFloatNumber::parse("F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3455354A958B21BA74F856FDC3BA2D793AEBE0E1D1ADF118BD9D0B592FF14C815D2C_e-1", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 139 | 140 | assert!(n2.cmp(&n3) == 0); 141 | 142 | // near zero 143 | let p = 448; 144 | let n1 = BigFloatNumber::parse("-4.029AC2B966FC8CD896222A09593F0751477AD7BA9C969E57290BDE947A2E1514DF2901A1C5624013106A5197035DCA72C221BA963E190A20_e-13", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 145 | let n2 = n1.tanh(p, rm, &mut cc).unwrap(); 146 | let n3 = BigFloatNumber::parse("-4.029AC2B966FC8CD896222A09593F0751477AC23B7FED67E140D1714FFCEC504D75DF70C388B621AF2FBC77C90C98C50F852D65BEDA273128_e-13", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap(); 147 | 148 | //println!("{:?}", n1.format(crate::Radix::Hex, rm, &mut cc).unwrap()); 149 | //println!("{:?}", n2.format(crate::Radix::Hex, rm, &mut cc).unwrap()); 150 | 151 | assert!(n2.cmp(&n3) == 0); 152 | 153 | let d1 = BigFloatNumber::max_value(p).unwrap(); 154 | let d2 = BigFloatNumber::min_value(p).unwrap(); 155 | 156 | assert!(d1.tanh(p, rm, &mut cc).unwrap().cmp(&ONE) == 0); 157 | assert!(d2.tanh(p, rm, &mut cc).unwrap().cmp(&ONE.neg().unwrap()) == 0); 158 | 159 | let d3 = BigFloatNumber::min_positive(p).unwrap(); 160 | let zero = BigFloatNumber::new(1).unwrap(); 161 | let n1 = random_subnormal(p); 162 | 163 | assert!(d3.tanh(p, rm, &mut cc).unwrap().cmp(&d3) == 0); 164 | assert!(zero.tanh(p, rm, &mut cc).unwrap().is_zero()); 165 | assert!(n1.tanh(p, rm, &mut cc).unwrap().cmp(&n1) == 0); 166 | } 167 | 168 | #[ignore] 169 | #[test] 170 | #[cfg(feature = "std")] 171 | fn tanh_perf() { 172 | let p = 160; 173 | let mut cc = Consts::new().unwrap(); 174 | let mut n = vec![]; 175 | for _ in 0..10000 { 176 | n.push(BigFloatNumber::random_normal(p, 0, 5).unwrap()); 177 | } 178 | 179 | for _ in 0..5 { 180 | let start_time = std::time::Instant::now(); 181 | for ni in n.iter() { 182 | let _f = ni.tanh(p, RoundingMode::ToEven, &mut cc).unwrap(); 183 | } 184 | let time = start_time.elapsed(); 185 | println!("{}", time.as_millis()); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /astro-float-num/src/ops/util.rs: -------------------------------------------------------------------------------- 1 | //! Auxiliary items. 2 | 3 | use crate::{num::BigFloatNumber, Consts, Error, RoundingMode}; 4 | 5 | impl BigFloatNumber { 6 | /// Reduce `self` to interval (-2*pi; 2*pi) 7 | pub(crate) fn reduce_trig_arg(self, cc: &mut Consts, rm: RoundingMode) -> Result { 8 | if self.exponent() > 2 { 9 | let mut pi = cc.pi_num(self.mantissa_max_bit_len() + self.exponent() as usize, rm)?; 10 | 11 | pi.set_exponent(pi.exponent() + 1); 12 | 13 | self.rem(&pi) 14 | } else { 15 | Ok(self) 16 | } 17 | } 18 | 19 | /// Determine how close `self` to pi or pi/2 20 | pub(crate) fn trig_arg_pi_proximity( 21 | &self, 22 | cc: &mut Consts, 23 | rm: RoundingMode, 24 | ) -> Result<(usize, usize), Error> { 25 | let mut q = 0; 26 | 27 | if self.exponent() > 0 { 28 | let mut pi = cc.pi_num(self.mantissa_max_bit_len() + self.exponent() as usize, rm)?; 29 | pi.set_sign(self.sign()); 30 | 31 | if self.exponent() < 2 { 32 | pi.set_exponent(1); 33 | } 34 | 35 | let mut ret = self.sub(&pi, self.mantissa_max_bit_len(), rm)?; 36 | q += pi.exponent() as usize; 37 | 38 | if ret.exponent() > 0 && self.sign() == ret.sign() { 39 | if ret.exponent() < 2 { 40 | pi.set_exponent(1); 41 | } 42 | 43 | ret = ret.sub(&pi, self.mantissa_max_bit_len(), rm)?; 44 | q += pi.exponent() as usize; 45 | } 46 | 47 | Ok((ret.exponent().unsigned_abs() as usize, q)) 48 | } else { 49 | Ok((self.exponent().unsigned_abs() as usize, q)) 50 | } 51 | } 52 | } 53 | 54 | /// Compute result for argument with small exponent. 55 | macro_rules! compute_small_exp { 56 | ($arg:ident, $exp:expr, $sign_inverse:expr, $p_wrk:ident, $p:ident, $rm:ident) => { 57 | if $p_wrk as isize + 1 < -($exp) { 58 | let mut x = $arg.clone()?; 59 | if $p > x.mantissa_max_bit_len() { 60 | x.set_precision($p, RoundingMode::None)?; 61 | } 62 | let mut ret = x.add_correction($sign_inverse)?; 63 | ret.set_precision($p, $rm)?; 64 | return Ok(ret); 65 | } 66 | }; 67 | } 68 | 69 | pub(super) use compute_small_exp; 70 | -------------------------------------------------------------------------------- /astro-float-num/src/strop.rs: -------------------------------------------------------------------------------- 1 | //! BigFloatNumber formatting. 2 | 3 | use crate::defs::Error; 4 | use crate::defs::Radix; 5 | use crate::defs::RoundingMode; 6 | use crate::num::BigFloatNumber; 7 | use crate::Consts; 8 | use crate::Exponent; 9 | use crate::Sign; 10 | 11 | #[cfg(feature = "std")] 12 | use std::fmt::Write; 13 | 14 | #[cfg(not(feature = "std"))] 15 | use {alloc::string::String, core::fmt::Write}; 16 | 17 | const DIGIT_CHARS: [char; 16] = 18 | ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; 19 | 20 | impl BigFloatNumber { 21 | /// Parses the number from the string `s` using radix `rdx`, precision `p`, and rounding mode `rm`. 22 | /// Note, since hexadecimal digits include the character "e", the exponent part is separated 23 | /// from the mantissa by "_". 24 | /// For example, a number with mantissa `123abcdef` and exponent `123` would be formatted as `123abcdef_e+123`. 25 | /// 26 | /// ## Errors 27 | /// 28 | /// - InvalidArgument: failed to parse input or precision is incorrect. 29 | /// - MemoryAllocation: failed to allocate memory for mantissa. 30 | /// - ExponentOverflow: the resulting exponent becomes greater than the maximum allowed value for the exponent. 31 | #[cfg(test)] 32 | pub fn parse( 33 | s: &str, 34 | rdx: Radix, 35 | p: usize, 36 | rm: RoundingMode, 37 | cc: &mut Consts, 38 | ) -> Result { 39 | let ps = crate::parser::parse(s, rdx)?; 40 | 41 | if ps.is_nan() || ps.is_inf() { 42 | Err(Error::InvalidArgument) 43 | } else { 44 | let (m, s, e) = ps.raw_parts(); 45 | BigFloatNumber::convert_from_radix(s, m, e, rdx, p, rm, cc) 46 | } 47 | } 48 | 49 | /// Formats the number using radix `rdx` and rounding mode `rm`. 50 | /// Note, since hexadecimal digits include the character "e", the exponent part is separated 51 | /// from the mantissa by "_". 52 | /// For example, a number with mantissa `123abcdef` and exponent `123` would be formatted as `123abcdef_e+123`. 53 | /// 54 | /// ## Errors 55 | /// 56 | /// - MemoryAllocation: failed to allocate memory for mantissa. 57 | /// - ExponentOverflow: the resulting exponent becomes greater than the maximum allowed value for the exponent. 58 | pub fn format(&self, rdx: Radix, rm: RoundingMode, cc: &mut Consts) -> Result { 59 | let (s, m, e) = self.convert_to_radix(rdx, rm, cc)?; 60 | 61 | let mut mstr = String::new(); 62 | let mstr_sz = 8 63 | + (self.mantissa_max_bit_len() + core::mem::size_of::() * 8) 64 | / match rdx { 65 | Radix::Bin => 1, 66 | Radix::Oct => 3, 67 | Radix::Dec => 3, 68 | Radix::Hex => 4, 69 | }; 70 | 71 | mstr.try_reserve_exact(mstr_sz)?; 72 | 73 | if s == Sign::Neg { 74 | mstr.push('-'); 75 | } 76 | 77 | if m.is_empty() { 78 | mstr.push_str("0.0"); 79 | } else { 80 | let mut iter = m.iter(); 81 | 82 | if self.is_subnormal() { 83 | mstr.push('0'); 84 | } else { 85 | mstr.push(DIGIT_CHARS[*iter.next().unwrap() as usize]); // m is not empty as checked above, hence unwrap 86 | } 87 | 88 | mstr.push('.'); 89 | 90 | iter.map(|&d| DIGIT_CHARS[d as usize]) 91 | .for_each(|v| mstr.push(v)); 92 | 93 | if rdx == Radix::Hex { 94 | let _ = write!(mstr, "_"); 95 | } 96 | 97 | if e < 1 { 98 | let val = if self.is_subnormal() { 99 | e.unsigned_abs() as usize 100 | } else { 101 | (e as isize - 1).unsigned_abs() 102 | }; 103 | 104 | let _ = match rdx { 105 | Radix::Bin => write!(mstr, "e-{:b}", val), 106 | Radix::Oct => write!(mstr, "e-{:o}", val), 107 | Radix::Dec => write!(mstr, "e-{}", val), 108 | Radix::Hex => write!(mstr, "e-{:x}", val), 109 | }; 110 | } else { 111 | let _ = match rdx { 112 | Radix::Bin => write!(mstr, "e+{:b}", e as isize - 1), 113 | Radix::Oct => write!(mstr, "e+{:o}", e as isize - 1), 114 | Radix::Dec => write!(mstr, "e+{}", e as isize - 1), 115 | Radix::Hex => write!(mstr, "e+{:x}", e as isize - 1), 116 | }; 117 | }; 118 | } 119 | 120 | Ok(mstr) 121 | } 122 | } 123 | 124 | #[cfg(test)] 125 | mod tests { 126 | 127 | use rand::random; 128 | 129 | use crate::{ 130 | common::util::random_subnormal, Exponent, EXPONENT_MAX, EXPONENT_MIN, WORD_BIT_SIZE, 131 | }; 132 | 133 | use super::*; 134 | 135 | #[test] 136 | fn test_strop() { 137 | let mut eps = BigFloatNumber::from_word(1, 192).unwrap(); 138 | let mut cc = Consts::new().unwrap(); 139 | 140 | for i in 0..1000 { 141 | let p1 = (random::() % 32 + 3) * WORD_BIT_SIZE; 142 | let p2 = (random::() % 32 + 3) * WORD_BIT_SIZE; 143 | let p = p1.min(p2); 144 | 145 | let n = if i & 1 == 0 { 146 | BigFloatNumber::random_normal(p1, EXPONENT_MIN + p as Exponent, EXPONENT_MAX) 147 | .unwrap() 148 | } else { 149 | random_subnormal(p1) 150 | }; 151 | 152 | for rdx in [Radix::Bin, Radix::Oct, Radix::Hex] { 153 | let rm = match random::() % 7 { 154 | 0 => RoundingMode::ToEven, 155 | 1 => RoundingMode::Up, 156 | 2 => RoundingMode::Down, 157 | 3 => RoundingMode::FromZero, 158 | 4 => RoundingMode::ToZero, 159 | 5 => RoundingMode::ToOdd, 160 | 6 => RoundingMode::None, 161 | _ => unreachable!(), 162 | }; 163 | 164 | let s = n.format(rdx, rm, &mut cc).unwrap(); 165 | let d = BigFloatNumber::parse(&s, rdx, p2, rm, &mut cc).unwrap(); 166 | 167 | if p2 < p1 { 168 | let mut x = n.clone().unwrap(); 169 | x.set_precision(p, rm).unwrap(); 170 | assert!(x.cmp(&d) == 0); 171 | } else { 172 | assert!(n.cmp(&d) == 0); 173 | } 174 | } 175 | 176 | let s = n.format(Radix::Dec, RoundingMode::ToEven, &mut cc).unwrap(); 177 | let d = 178 | BigFloatNumber::parse(&s, Radix::Dec, p2, RoundingMode::ToEven, &mut cc).unwrap(); 179 | 180 | if i & 1 == 0 { 181 | eps.set_exponent(n.exponent() - p as Exponent + 4); 182 | assert!( 183 | d.sub(&n, p, RoundingMode::None) 184 | .unwrap() 185 | .abs() 186 | .unwrap() 187 | .cmp(&eps) 188 | < 0 189 | ); 190 | } else { 191 | let mut eps2 = BigFloatNumber::min_positive(p).unwrap(); 192 | eps2.set_exponent(eps2.exponent() + 2); 193 | assert!( 194 | d.sub(&n, p, RoundingMode::None) 195 | .unwrap() 196 | .abs() 197 | .unwrap() 198 | .cmp(&eps2) 199 | < 0 200 | ); 201 | } 202 | } 203 | 204 | // MIN, MAX, min_positive 205 | let p1 = (random::() % 32 + 1) * WORD_BIT_SIZE; 206 | let p2 = (random::() % 32 + 1) * WORD_BIT_SIZE; 207 | let p = p1.min(p2); 208 | let rm = RoundingMode::None; 209 | 210 | for rdx in [Radix::Bin, Radix::Oct, Radix::Dec, Radix::Hex] { 211 | // min, max 212 | // for p2 < p1 rounding will cause overflow, for p2 >= p1 no rounding is needed. 213 | for mut n in 214 | [BigFloatNumber::max_value(p1).unwrap(), BigFloatNumber::min_value(p1).unwrap()] 215 | { 216 | //println!("\n{:?} {} {}", rdx, p1, p2); 217 | //println!("{:?}", n); 218 | 219 | let s = n.format(rdx, rm, &mut cc).unwrap(); 220 | let mut g = BigFloatNumber::parse(&s, rdx, p2, rm, &mut cc).unwrap(); 221 | 222 | //println!("{:?}", g); 223 | 224 | if rdx == Radix::Dec { 225 | eps.set_exponent(n.exponent() - p as Exponent + 4); // 4, since rm is none 226 | assert!(n.sub(&g, p, rm).unwrap().abs().unwrap().cmp(&eps) <= 0); 227 | } else { 228 | if p2 < p1 { 229 | n.set_precision(p, rm).unwrap(); 230 | } else if p2 > p1 { 231 | g.set_precision(p, rm).unwrap(); 232 | } 233 | 234 | assert!(n.cmp(&g) == 0); 235 | } 236 | } 237 | 238 | // min subnormal 239 | let mut n = BigFloatNumber::min_positive(p1).unwrap(); 240 | let s = n.format(rdx, rm, &mut cc).unwrap(); 241 | let mut g = BigFloatNumber::parse(&s, rdx, p2, rm, &mut cc).unwrap(); 242 | 243 | if rdx == Radix::Dec { 244 | let mut eps = BigFloatNumber::min_positive(p).unwrap(); 245 | eps.set_exponent(eps.exponent() + 2); 246 | assert!(n.sub(&g, p, rm).unwrap().abs().unwrap().cmp(&eps) < 0); 247 | } else { 248 | if p2 < p1 { 249 | n.set_precision(p, rm).unwrap(); 250 | } else if p2 > p1 { 251 | g.set_precision(p, rm).unwrap(); 252 | } 253 | assert!(n.cmp(&g) == 0); 254 | } 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /astro-float-num/tests/README.md: -------------------------------------------------------------------------------- 1 | ## Integration tests 2 | 3 | The `mpfr` directory contains tests that verify that the results of arithmetic operations, mathematical functions, and other operations produced by `astro-float` are identical to the results of the same operations produced by `mpfr` at the bit level. -------------------------------------------------------------------------------- /astro-float-num/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | //! Integration tests. 2 | 3 | #[cfg(test)] 4 | #[cfg(target_arch = "x86_64")] 5 | mod mpfr; 6 | -------------------------------------------------------------------------------- /astro-float-num/tests/mpfr/compare_const_test.rs: -------------------------------------------------------------------------------- 1 | //! This test suite performs comparison of mpfr constants and astro-float constants at bit level. 2 | 3 | use crate::mpfr::common::get_prec_rng; 4 | use crate::mpfr::common::test_astro_const; 5 | use crate::mpfr::common::{assert_float_close, get_random_rnd_pair}; 6 | use astro_float_num::RoundingMode; 7 | use astro_float_num::{BigFloat, Consts, EXPONENT_MAX, EXPONENT_MIN, WORD_BIT_SIZE}; 8 | use gmp_mpfr_sys::{ 9 | gmp::exp_t, 10 | mpfr::{self, rnd_t}, 11 | }; 12 | use rand::random; 13 | use rug::{ 14 | float::{exp_max, exp_min}, 15 | Float, 16 | }; 17 | 18 | // constants computation 19 | #[test] 20 | fn mpfr_compare_const() { 21 | let repeat_cnt = 100; 22 | let run_cnt = 500; 23 | 24 | let p_rng = get_prec_rng(); 25 | 26 | let p_min = 1; 27 | 28 | let p_max = (p_rng + p_min) * WORD_BIT_SIZE; 29 | 30 | unsafe { 31 | mpfr::set_emin(EXPONENT_MIN as exp_t); 32 | mpfr::set_emax(EXPONENT_MAX as exp_t); 33 | } 34 | 35 | assert_eq!(EXPONENT_MIN, exp_min()); 36 | assert_eq!(EXPONENT_MAX, exp_max()); 37 | 38 | let mut mpfr_e = Float::with_val((p_max + WORD_BIT_SIZE) as u32, 1); 39 | unsafe { 40 | mpfr::exp( 41 | mpfr_e.as_raw_mut(), 42 | Float::with_val(1, 1).as_raw(), 43 | rnd_t::RNDN, 44 | ); 45 | } 46 | 47 | let mut mpfr_ln10 = Float::with_val((p_max + WORD_BIT_SIZE) as u32, 1); 48 | unsafe { 49 | mpfr::log( 50 | mpfr_ln10.as_raw_mut(), 51 | Float::with_val(32, 10).as_raw(), 52 | rnd_t::RNDN, 53 | ); 54 | } 55 | 56 | for _ in 0..repeat_cnt { 57 | let mut cc = Consts::new().unwrap(); 58 | 59 | for _ in 0..run_cnt { 60 | let p = (random::() % p_rng + p_min) * WORD_BIT_SIZE; 61 | 62 | let (rm, rnd) = get_random_rnd_pair(); 63 | 64 | // pi, ln(2) 65 | test_astro_const!(pi, const_pi, p, rm, rnd, (p, rm, "const pi"), cc); 66 | test_astro_const!(ln_2, const_log2, p, rm, rnd, (p, rm, "const ln(2)"), cc); 67 | 68 | // e 69 | let n1 = cc.e(p, rm); 70 | let mut f1 = mpfr_e.clone(); 71 | unsafe { 72 | mpfr::prec_round(f1.as_raw_mut(), p as mpfr::prec_t, rnd); 73 | } 74 | 75 | assert_float_close( 76 | n1, 77 | f1, 78 | p, 79 | &format!("{:?}", (p, rm, "const e")), 80 | true, 81 | &mut cc, 82 | ); 83 | 84 | // ln(10) 85 | let n1 = cc.ln_10(p, rm); 86 | f1 = mpfr_ln10.clone(); 87 | unsafe { 88 | mpfr::prec_round(f1.as_raw_mut(), p as mpfr::prec_t, rnd); 89 | } 90 | 91 | assert_float_close( 92 | n1, 93 | f1, 94 | p, 95 | &format!("{:?}", (p, rm, "const ln(10)")), 96 | true, 97 | &mut cc, 98 | ); 99 | } 100 | } 101 | 102 | // large prec 103 | let p; 104 | #[cfg(not(debug_assertions))] 105 | { 106 | p = 1000000; 107 | } 108 | 109 | #[cfg(debug_assertions)] 110 | { 111 | p = 10048; 112 | } 113 | 114 | let mut cc = Consts::new().unwrap(); 115 | let rm = RoundingMode::ToEven; 116 | let rnd = rnd_t::RNDN; 117 | 118 | let mut mpfr_e = Float::with_val(p as u32, 1); 119 | unsafe { 120 | mpfr::exp( 121 | mpfr_e.as_raw_mut(), 122 | Float::with_val(1, 1).as_raw(), 123 | rnd_t::RNDN, 124 | ); 125 | } 126 | 127 | let mut mpfr_ln10 = Float::with_val(p as u32, 1); 128 | unsafe { 129 | mpfr::log( 130 | mpfr_ln10.as_raw_mut(), 131 | Float::with_val(32, 10).as_raw(), 132 | rnd_t::RNDN, 133 | ); 134 | } 135 | 136 | // pi, ln(2) 137 | test_astro_const!(pi, const_pi, p, rm, rnd, (p, rm, "const pi"), cc); 138 | test_astro_const!(ln_2, const_log2, p, rm, rnd, (p, rm, "const ln(2)"), cc); 139 | 140 | // e 141 | let n1 = cc.e(p, rm); 142 | let mut f1 = mpfr_e.clone(); 143 | unsafe { 144 | mpfr::prec_round(f1.as_raw_mut(), p as mpfr::prec_t, rnd); 145 | } 146 | 147 | assert_float_close( 148 | n1, 149 | f1, 150 | p, 151 | &format!("{:?}", (p, rm, "const e")), 152 | true, 153 | &mut cc, 154 | ); 155 | 156 | // ln(10) 157 | let n1 = cc.ln_10(p, rm); 158 | f1 = mpfr_ln10.clone(); 159 | unsafe { 160 | mpfr::prec_round(f1.as_raw_mut(), p as mpfr::prec_t, rnd); 161 | } 162 | 163 | assert_float_close( 164 | n1, 165 | f1, 166 | p, 167 | &format!("{:?}", (p, rm, "const ln(10)")), 168 | true, 169 | &mut cc, 170 | ); 171 | } 172 | -------------------------------------------------------------------------------- /astro-float-num/tests/mpfr/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | pub mod compare_const_test; 3 | pub mod compare_ops_test; 4 | pub mod compare_special_test; 5 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Errors 2 | 3 | This document describes how error is estimated. Given an operation and arguments containing certain error the goal it to find error bound of the result of the operation. When the error of the result is known, operations can be applied sequentially to estimate error of a more complex construct. 4 | 5 | Arguments are represented as $m2^{e_1}$, $n2^{e_2}$, where $m$, $n$ denote the mantissa of a floating point number with $0.5 <= |n| < 1, 0.5 <= |m| < 1$, and $e_1$, $e_2$ denote exponent of a floating point number. 6 | 7 | $p$ denotes precision of a floating point number. 8 | 9 | $m 2^{e_1} \pm 2^{e_1 - p}$ denotes an argument with absolute error of at most $2^{e_1-p}$, i.e. $\pm1$ ulp. 10 | 11 | ## Error of multiplication 12 | 13 | Absolute error $err_a$ of multiplying numbers containing relative error less than $2^{-p}$: 14 | 15 | $$\displaylines{err_a < |(m 2^{e_1} \pm 2^{e_1 - p}) (n 2^{e_2} \pm 2^{e_2 - p}) - m 2^{e_1} n 2^{e_2}| = \\\\ = |(m \pm n) 2^{e_1 + e_2 - p} \pm 2^{e_1 + e_2 - 2 p}| < 2 ^ {e_1 + e_2 - p + 2}}$$ 16 | 17 | Relative errror: 18 | 19 | $$\displaylines{err_r < |\frac{err_a}{mn 2^{e_1 + e_2}}| = |\frac{(m \pm n) 2^{e_1 + e_2 - p} \pm 2^{e_1 + e_2 - 2 p}}{mn 2^{e_1 + e_2}}| = \\\\ = |\frac{(m \pm n) 2^{- p}}{mn} \pm \frac{2^{ - 2 p}}{mn}| <= 2^{-p+2} \pm 2^{-2p+2} < 2^{-p + 3}}$$ 20 | 21 | Similarly for $k > 0$ and $p > 1$: 22 | 23 | $$\displaylines{err_a < |(m 2^{e_1} \pm 2^{e_1 - p + k}) (n 2^{e_2} \pm 2^{e_2 - p}) - m 2^{e_1} n 2^{e_2}| =\\\\= |(m \pm n 2^k) 2^{e_1 + e_2 - p} \pm 2^{e_1 + e_2 - 2 p + k}| =\\\\= |(m 2^{-k - 1} \pm n 2^{-1} \pm 2^{-p-1}) 2^{e_1+e_2-p+k+1}| < 2 ^ {e_1 + e_2 - p + k + 1}}$$ 24 | 25 | $$\displaylines{err_r < |\frac{(m 2^{-k - 1} \pm n 2^{-1} \pm 2^{-p-1})}{mn} 2^{-p+k+1}| =\\\\= |\left(\frac{1}{n2^{k + 1}} \pm \frac{1}{2m} \pm \frac{1}{mn2^{p+1}}\right) 2^{-p+k+1}| < 2^{-p + k + 2}}$$ 26 | 27 | 28 | ## Error of division 29 | 30 | Absolute error of dividing numbers with error and $p > 3$: 31 | 32 | $$\displaylines{err_a < |\frac {m 2^{e_1} \pm 2^{e_1 - p}} {n 2^{e_2} \pm 2^{e_2 - p}} - \frac{m 2^{e_1}}{n 2^{e_2}}| =\\\\= |\frac{(n \pm m) 2^{-p}}{n^2 \pm n2^{-p}} 2^{e_1 - e_2}| < |\left(\frac{2}{1 - 2^{-p+1}} + \frac{4}{1 - 2^{-p+1}}\right) 2^{e_1 - e_2 - p}| < 2 ^ {e_1 - e_2 - p + 3}}$$ 33 | 34 | Relative error: 35 | 36 | $$\displaylines{err_r < |\frac{(n \pm m) 2^{-p}}{mn \pm m2^{-p}}| =\\\\= |\left(\frac{n}{m(n \pm 2^{-p})} \pm \frac{1}{n \pm 2^{-p}}\right) 2^{-p}| < \frac{2n + 1}{n - 2^{-p}} 2^{-p} < 2 ^ {- p + 3}}$$ 37 | 38 | 39 | For $k > 0$ and $p > 4$: 40 | 41 | $$\displaylines{err_a < |\frac {m 2^{e_1} \pm 2^{e_1 - p + k}} {n 2^{e_2} \pm 2^{e_2 - p}} - \frac{m 2^{e_1}}{n 2^{e_2}}| =\\\\= |\frac{(n \pm m2^{-k}) 2^{-p + k}}{n^2 \pm n2^{-p}} 2^{e_1 - e_2}| < \left(\frac{2}{1 - 2^{-p+1}} + \frac{4 2^{-k}}{1 - 2^{-p+1}}\right) 2^{e_1 - e_2 - p + k} < 2 ^ {e_1 - e_2 - p + k + 3}}$$ 42 | 43 | $$\displaylines{err_r < \frac{2n + 2^{-k}}{n - 2^{-p}} 2^{-p+k} < 2 ^ {- p + k + 3}}$$ 44 | 45 | and $k > 0$ and $p > 2 + k$: 46 | 47 | $$\displaylines{err_a < |\frac {m 2^{e_1} \pm 2^{e_1 - p}} {n 2^{e_2} \pm 2^{e_2 - p + k}} - \frac{m 2^{e_1}}{n 2^{e_2}}| =\\\\= |\frac{(n2^{-k} \pm m) 2^{-p + k}}{n^2 \pm n2^{-p+k}} 2^{e_1 - e_2}| < \left(\frac{2^{-k+1}}{1 - 2^{-p+1+k}} + \frac{4}{1 - 2^{-p+1+k}}\right) 2^{e_1 - e_2 - p + k} < 2 ^ {e_1 - e_2 - p + k + 3}}$$ 48 | 49 | $$\displaylines{err_r < \frac{n2^{-k+1} + 1}{n - 2^{-p+k}} 2^{-p+k} < 2 ^ {- p + k + 3}}$$ 50 | 51 | ## Error of subtraction 52 | 53 | Absolute error of subtraction of numbers with the same sign: 54 | 55 | $$\displaylines{err_a < |(m 2^{e_1} \pm 2^{e_1 - p}) - (n 2^{e_2} \pm 2^{e_2 - p}) - (m 2^{e_1} - n 2^{e_2})| < 2 ^ {max(e_1, e_2) - p + 1}}$$ 56 | 57 | Relative error: 58 | 59 | $$\displaylines{err_r < 2^{-p+2}}$$ 60 | 61 | Note: subtraction can cause borrow which increases relative error. 62 | 63 | For $k > 0$: 64 | 65 | $$\displaylines{err_a = |(m 2^{e_1} \pm 2^{e_1 - p + k}) - (n 2^{e_2} \pm 2^{e_2 - p}) - (m 2^{e_1} - n 2^{e_2})| < 2 ^ {max(e_1,e_2) - p + k + 1}}$$ 66 | 67 | $$\displaylines{err_r < 2^{-p+k+2}}$$ 68 | 69 | ## Error of addition 70 | 71 | Absolute error of addition of numbers with the same sign: 72 | 73 | $$\displaylines{err_a < |(m 2^{e_1} \pm 2^{e_1 - p}) + (n 2^{e_2} \pm 2^{e_2 - p}) - (m 2^{e_1} + n 2^{e_2})| <= 2 ^ {max(e_1, e_2) - p + 1}}$$ 74 | 75 | Relative error: 76 | 77 | $$\displaylines{err_r <= 2^{-p+1}}$$ 78 | 79 | For $k > 0$: 80 | 81 | $$\displaylines{err_a = |(m 2^{e_1} \pm 2^{e_1 - p + k}) + (n 2^{e_2} \pm 2^{e_2 - p}) - (m 2^{e_1} + n 2^{e_2})| < 2 ^ {max(e_1,e_2) - p + k + 1}}$$ 82 | 83 | $$\displaylines{err_r < 2^{-p+k+1}}$$ 84 | 85 | 86 | ## Error of square root 87 | 88 | Absolute error of the square root of a number with error: 89 | 90 | $$\displaylines{err_a < |\sqrt{m 2^e \pm 2^{e - p}} - \sqrt{m 2^e}| =\\\\= \sqrt{m 2^e}|\sqrt{\frac{m2^e \pm 2^{e-p}}{m2^e}} - 1| =\\\\= \sqrt{m 2^e}|\sqrt{1 \pm \frac{2^{-p}}{m}} - 1| < 2^{- p} \sqrt{m2^e} <= 2 ^ {\lceil{e/2}\rceil - p}}$$ 91 | 92 | (because no solution exists for $|\sqrt{1 \pm \frac{2^{-p}}{m}} - 1| >= 2^{- p}$). 93 | 94 | Relative error: 95 | 96 | $$\displaylines{err_r < 2^{-p}}$$ 97 | 98 | ## Error of series of $\sin$, $\cos$ and $\sinh$ 99 | 100 | Error of Maclaurin series $M(x)$ of a function $f(x)$ for $x < 1$ in which absolute value of the function derivatives near 0 never exceeds 1 need to be estimated only for several first terms of the series. 101 | 102 | Proof. 103 | 104 | $${err < |M(m 2^e \pm 2^{e-p}) - M(m 2^e)| = 2^e |M(m \pm 2^p) - M(m)}|$$ 105 | 106 | $0.5 <= m < 1$ and $e <= 0$. 107 | 108 | For simplisity assume $e = 0$ and the absolute value of n'th derivative is 1. 109 | 110 | Then series look like: 111 | 112 | $$M(x) = f(0) + x + \frac{x^2}{2!} + \frac{x^3}{3!} + ... + \frac{x^n}{n!}$$ 113 | 114 | From binomial formula $(1 + x)^n = \displaystyle\sum_{k=0}^{n}{B_k x^k}$ follows if $x = 1$: 115 | 116 | $$2^n = \displaystyle\sum_{k=0}^{n}{B_k}\tag{1}$$ 117 | 118 | Then $(m \pm 2^{-p})^n < (1 + 2^{-p})^n = \sum{B_k 2^{-p k}}$. 119 | 120 | Since we subtract $m^n$ from $(m \pm 2^{-p})^n$ to compute the absolute error, $k > 0$. 121 | 122 | Then using (1) we get: 123 | 124 | $$\sum{B_k 2^{-p k}} < \sum{B_k 2^{-p}} = 2^{n - p}$$ 125 | 126 | From Stirling's approximation $n! > (n/e) ^ n$ 127 | follows: 128 | 129 | $$\frac{2^{n - p}}{n!} < 2^{n - p} \left(\frac{e}{n}\right)^n = 2^{-p} \left(\frac{2 e}{n}\right)^n$$ 130 | 131 | The residual error of the series can be received from Lagrange's error bound, 132 | and it is smaller than $2^{-p} \left(\frac{2 e}{n + 1}\right)^{n + 1}$. 133 | 134 | For $n$ terms of the series $err < 2^{-p} \displaystyle\sum_{k=1}^{n+1}{\left(\frac{2 e}{k}\right)^k}$. 135 | 136 | Starting from $k = 6$ we have: $\displaystyle\sum_{k=6}^{n+1}{\left(\frac{2 e}{k}\right)^k} < 1$ and $err < 2^{-p}$. 137 | 138 | ## Error of $\arctan$, $\mathop{\text{arctanh}}$ series. 139 | 140 | Series: 141 | 142 | $$\arctan(x) = x - \frac{x^3}{3} + \frac{x^5}{5} - ...$$ 143 | 144 | $$\mathop{\text{arctanh}}(x) = x + \frac{x^3}{3} + \frac{x^5}{5} + ...$$ 145 | 146 | Assume, the series $x + a_3 x^3 + a_5 x^5 + ...$ is computed directly, and $x$ contains relative error less than $2^{-p}$: 147 | 148 | $a_3$, $a_5$,... have relative error less than $2^{-p}$ since they are the result of division of 1 by the exact number 3, 5, 7,... 149 | and their value is smaller than 0.5 by definition. 150 | 151 | If $x = m 2^e$, where $0.5 <= m < 1$ and $e = -3$, then absolute error relative to 1 for $\mathop{\text{arctanh}}$: 152 | 153 | $$\displaylines{err_a < 2^{-p-3} + 2^{-p-9+4} + 2^{-p-15+6} + ... < 2^{-p-2}}$$ 154 | 155 | or less than $2^{-p+1}$ relative to $x$. The same is true for $e < -3$. 156 | 157 | For $\arctan$ error is $2^{-p+2}$, since the first subtraction can cause borrow. 158 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | array_width = 80 2 | single_line_if_else_max_width = 80 -------------------------------------------------------------------------------- /tests/tests/expr.rs: -------------------------------------------------------------------------------- 1 | use astro_float::BigFloat; 2 | use astro_float::Consts; 3 | use astro_float::RoundingMode; 4 | use astro_float_macro::expr; 5 | 6 | fn main() { 7 | let rm = RoundingMode::None; 8 | let mut cc = Consts::new().unwrap(); 9 | let _res: BigFloat = expr!(-6 * atan(1.0 / sqrt(3)), (256, rm, &mut cc)); 10 | } 11 | --------------------------------------------------------------------------------