stencillogic / astro-float Goto Github PK
View Code? Open in Web Editor NEWArbitrary precision floating point numbers library
License: MIT License
Arbitrary precision floating point numbers library
License: MIT License
Is there ever a need to resize the mantissa vector? A possible optimization could be to store Box<[Word]>
rather than Vec<Word>
, which reduces the size of BigFloat
from 40 bytes to 32.
Current implementation of short multiplication is only 10% faster than full multiplication. Improve the performance of the short multiplication.
A lot of the time, needed precision might be known ahead of runtime. Const generics might be able to make use of something like this:
struct KnownBigFloat<const WORDS: usize = 2> {
inner: KnownFlavor<WORDS>,
}
#[derive(Debug)]
enum KnownFlavor<const WORDS: usize> {
Value(KnownBigFloatNumber<WORDS>),
NaN(Option<Error>),
Inf(Sign), // signed Inf
}
pub(crate) struct KnownBigFloatNumber<const WORDS: usize> {
e: Exponent,
s: Sign,
m: KnownMantissa<WORDS>,
inexact: bool,
}
pub(crate) struct KnownMantissa<const WORDS: usize> {
m: [Word; WORDS],
}
I expect this would have a noticeable performance increase as well - mantissa is smaller for <= 128 bits of precision, allocation-free, no indirection, and probably some nicer optimizations.
A downside is this isn't as elegant as it could be with the unstbale generic_const_exprs
- ideally you could specify BITS
in the const generic and store m: [Word; BITS / WORD_BIT_SIZE]
, but that won't be stable for a while.
Thanks for the lib! It has been immensely helpful.
I am running into issues converting a BigFloat instance to BigUint (from num_bigint). My naive try is
let mut consts = Consts::new().unwrap();
let mut value = BigFloat::from(1.2);
let (sign, radix_repr, ex) = value
.round(0, ROUNDING_MODE)
.convert_to_radix(astro_float::Radix::Dec, ROUNDING_MODE, &mut consts)
.unwrap();
// Assume sign is always positive
let value_biguint = BigUint::from_radix_be(&radix_repr, 10).unwrap();
I though that setting to n to 0 (i.e. fractional digits are decimal) will round the value to 1, hence mantissa will 1 and exp will be 0. But this isn't the case. Probably I am misreding the docs. Can you point me to a function using which I can round the value towards 0 and mantissa will drop rounded-off digits?
SmallVec does not show performance benefits compared to Vec, so, the dependency on SmallVec should be excluded.
Implement binary operator ^ which given n^m
computes n to the power of m. Consider using BigFloatNumber::powi for unsigned integer m, BigFloatNumber::powsi for signed integer m, and BigFloatNumber::pow in other cases.
Review unsafe code sections and try replacing unsafe with safe analogs having the same or better performance. If the performance drops unsafe should stay.
Hello! I really like this package and I've been working with it a lot, however I have found a bug :(
Reproducible example in astro-float 0.9.1
use astro_float
#[test]
fn test_sinh() {
let prec = 256;
let mut cc = astro_float::Consts::new().unwrap();
let sinh_neg = astro_float::BigFloat::from_f64(-1e50, prec).sinh(
prec,
astro_float::RoundingMode::None,
&mut cc,
);
// Passes :(
assert!(sinh_neg.is_inf_pos());
// Fails :(
assert!(sinh_neg.is_inf_neg());
}
Basically the sinh of any really negative number will return positive infinity when it should return negative infinity. However the sinh of negative infinity is correct. I believe this is because the exponent overflow error from 1/(e^x)
does not swap signs. Basically if x is really negative, the reciprocal operation on positive zero will overflow to positive infinity and the offending line of code (below) will pass on the positive overflow onto the return value even though it is being subtracted from the final result later.
let xe = ex.reciprocal(p_x, RoundingMode::None)?;
Link to line of code that's broken
It was likely intended that the question mark operator would pass the error into the map_err function but it actually just returns it.
I think the best solution would be to replace the question mark operator with a match statement that just sets the overflow sign to negative (this is probably the fastest because it is doing the match statement anyway).
I am willing to submit a pull request to fix this or if you want to fix it that would be cool too.
Current implementation of short division does not show performance benefit over full division. Improve the performance of the short division.
Consider adding conversion functions to primitive integer types.
Possible signatures (<int_type>
is i8, i16, i32, i64, i128, u8, u16,u32, u64, u128):
BigFloat::try_to_<int_type>(&self, RoundingMode) -> Result<<int_type>, Error>
: returns Error
if BigFloat's absolute values is larger than the the value <int_type>
can hold.BigFloat::to_<int_type>_saturating(&self, RoundingMode) -> <int_type>
: if the value of BigFloat is larger than the value that <int_type>
can hold, clamp the value to the maximum value of <int_type>
.AGM is more performant compared to Taylor series for large precision. Implement AGM algorithm for the logarithm. Test the performance improvement.
The code
astro-float/astro-float-num/src/defs.rs
Lines 11 to 33 in 891843d
x86
or not(x86)
. Is there a reason that x86 is special, or could this instead use #[cfg(target_pointer_width = 64)]
/#[cfg(target_pointer_width = 32)]
?Implement divide and conquer algorithm for conversion functions to convert from and to decimal base.
While happily using this library, we noticed a program would hang in certain conditions and narrowed it down to this snippet:
use astro_float::{ctx::Context, expr, BigFloat, Consts, RoundingMode, Sign};
fn main() {
let mut precision_context = Context::new(
128,
RoundingMode::ToEven,
Consts::new().expect("Constants cache initialized"),
);
let x = BigFloat::from_raw_parts(
&[9441246966859219331u64, 13943995520284385473u64],
128,
Sign::Pos,
-5,
true,
);
let y = BigFloat::from_raw_parts(
&[145249953336295741u64, 9295997013522923649u64],
128,
Sign::Neg,
-3,
true,
);
println!("{}", expr!(y + x, &mut precision_context));
}
Version of astro-float:
astro-float = "0.7.1"
Platform is windows. Here are the compiler versions I can reproduce it with:
rustc 1.75.0-nightly (a2f5f9691 2023-11-02)
rustc 1.73.0 (cc66ad468 2023-10-03)
I have a use case where I need to go back and forth between BigFloat
and f64
. fn to_f64(&self) -> f64
would be perfect.
For context there is an existing pub(crate) implementation.
astro-float/astro-float-num/src/num.rs
Line 795 in 00d5150
I would like to propose cleaning up this method and tweak it to follow Rust's convention with casting between f32
and f64
. According to the nomicon
- Casting from an
f32
to anf64
is perfect and lossless- Casting from an
f64
to anf32
will produce the closest possiblef32
- if necessary, rounding is according to roundTiesToEven mode ***
- on overflow, infinity (of the same sign as the input) is produced
*** as defined in IEEE 754-2008 §4.3.1: pick the nearest floating point number, preferring the one with an even least significant digit if exactly halfway between two floating point numbers.
Here the same rules can be followed.
f64
(includes subnormals)
I am open to working on this contribution.
I built on/for an M1 Mac system:
rustup target add aarch64-apple-darwin
cargo check --target=aarch64-apple-darwin
(check won't require a local toolchain, linker, SDKs, etc)
I see errors like this, followed by cascading issues which I think are related:
error[E0432]: unresolved import `crate::defs::Word`
--> astro-float-num/src/common/buf.rs:4:5
|
4 | use crate::defs::Word;
| ^^^^^^^^^^^^^^^^^ no `Word` in `defs`
I think the crux is snippets like this:
/// A word.
#[cfg(target_arch = "x86_64")]
pub type Word = u64;
// ...
/// A word.
#[cfg(target_arch = "x86")]
pub type Word = u32;
There's no base case, so as soon as you compile for not-x86 everything falls apart. You could add more targets to the list, but you could also use target_pointer_width
instead of target_arch
to make these decisions.
See: https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width
astro_float_num::mantissa::mantissa::Mantissa::pow_mod
calls mul_mod
function in a cycle with the same argument n
. Each mul_mod
performs division by n
. It may be more performant to replace the division with the multiplication by 1/n
. I.e. consider implementing Barret reduction.
I'm surprised by the rounding behaviour used in BigFloat.round(...). rounding 0.99 -> 0 seems very unnatural to me.
use astro_float::{BigFloat, RoundingMode};
fn main() {
let result = BigFloat::from_f64(0.99, 64).round(0, RoundingMode::ToEven);
println!("{result}");
}
Output:
0.0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.