Perfect "roundtrip" for f64 and RBig
lskyum opened this issue · 5 comments
Hi, I'm new to dashu (and Rust) but thanks for a very nice looking library!
I'm testing to see if Rust and dashu can be used to optimize a C# application that relies on big rationals.
But a very important feature is that any f64 can be converted to RBig and back to that exact f64 number.
My initial investigation showed that this didn't work with simplest_from_f64:
use dashu;
use rand::prelude::*;
fn main() {
let mut rng = rand::thread_rng();
for _i in 0..10000000 {
let a: f64 = rng.gen::<f64>() * 1000000.0;
let b = dashu::Rational::simplest_from_f64(a).unwrap();
let c : f64 = b.to_f64().value();
if a != c {
// for example a = 4142.7423687749879 and c = 4142.742368774987 ends up here...
println!("{} != {}", a, c);
}
}
}
Does dashu have functions to make this "perfect roundtrip"?
Hi, thanks for your interest. The method simplest_from_fxx
is not meant for exact conversion, it rather produces a readable output for a float number. For exact conversion, you can try RBig::TryFrom<fxx>
, that conversion is intended to be exact (and basically the denominator will always be a power of two). Could you please try RBig::try_from
and tell me if there are any failures in terms of roundtrip?
@cmpute Thanks for the quick response.
RBig::try_from seems to work very well. I've testet some edge cases along with a lot of random cases.
fn test_roundtrip(a : f64) {
let b = Rational::try_from(a).unwrap();
let c = b.to_f64().value();
if a != c {
println!("{} != {}", a, c);
}
}
fn main() {
let mut rng = rand::thread_rng();
for _i in 0..10000000 {
let random_bits = rng.gen::<u64>();
let a = f64::from_bits(random_bits);
if a.is_nan() || a.is_infinite() { continue; }
test_roundtrip(a);
}
// Edge cases
test_roundtrip(0.0);
test_roundtrip(-0.0);
test_roundtrip(f64::MAX);
test_roundtrip(f64::MIN);
test_roundtrip(f64::MIN_POSITIVE);
test_roundtrip(f64::EPSILON);
test_roundtrip(-f64::EPSILON);
// test_roundtrip(f64::INFINITY); // try_from doesn't work for infinity
// test_roundtrip(f64::NEG_INFINITY);
println!("Finished!");
}
It only fails on INFINITY, but I'm not sure it's suppose to work?
The C# implementation I'm using uses 1/0 and -1/0 for +/- infinity, but that is not a critical feature.
So far Rust and dashu is about 3x faster, so this looks very promising :-)
Glad that dashu
brings some speed up :)
Regarding infinities, RBig
does not support infinities (nor nans) by design, maybe you can support that by adding some checks? dashu_float::FBig
does support infinities, and that's why infinities won't be supported by RBig
@cmpute Yes, it's not a big issue with nan and infinity, because the code usually just needs checks for div-by-zero.
Great. Then I will close the issue by now, feel free to reach out if you have other questions.