-
-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Floating point precision for log10 #250
Comments
Hi Jegge! tl;dr: Short of implementing a new log function, implementing symbolic algebra optimizations, or switching to a new floating/fixed point type, the root problem cannot be fixed. A practical solution might be to round displayed outputs to an agreeable decimal place, and have slightly more flexible floating point equality checking behind the scenes. I've done some investigating, and it looks like the default number type is As referenced in the rust version of this issue, the inaccuracy boils down to the reality that IEEE doesn't have a standard for this because there are many trade-offs when designing intrinsic math functions. Rust's implementation used I think a practical solution to this would be rounding floats in the output to a reasonable decimal place (and adding a feature/flag to disable this behavior). Python took this approach, and standardized rounding displayed output as of v3.1 according to the docs. They also provide a A Partial Solution based on Your SuggestionI expanded the macro you referenced and added My implementation of the pub mod log {
use super::*;
#[cfg(feature = "bytes")]
pub fn byte_byte(a: u8, b: u8) -> f64 {
b.ilog(a) as f64
}
#[cfg(feature = "bytes")]
pub fn byte_num(a: u8, b: f64) -> f64 {
let a = f64::from(a);
b.log(a)
}
#[cfg(feature = "bytes")]
pub fn num_byte(a: f64, b: u8) -> f64 {
let b = f64::from(b);
b.log(a)
}
pub fn num_num(a: f64, b: f64) -> f64 {
b.log(a)
}
#[cfg(feature = "complex")]
pub fn com_x(a: Complex, b: impl Into<Complex>) -> Complex {
let b = b.into();
b.log(a)
}
#[cfg(feature = "complex")]
pub fn x_com(a: impl Into<Complex>, b: Complex) -> Complex {
let a = a.into();
b.log(a)
}
pub fn error<T: Display>(a: T, b: T, env: &Uiua) -> UiuaError {
env.error(format!("Cannot get the log base {0} of {1}", b, a))
}
} |
Hi SybeBlue, thank you for your reply. Is The first variant checks for an integral pub fn num_num(a: f64, b: f64) -> f64 {
if (a.fract() > 0) {
a.log(b)
} else if (b == 2) {
(a as i64).ilog2() as f64
} else if (b == 10) {
(a as i64).ilog10() as f64
} else {
(a as i64).ilog(b as i64) as f64
}
} The second variant should be slightly faster than the first. According to the docs f64::log2 and f64::log10 improve the precision for these special bases and should be used instead of log:
This variant might be a reasonable middle ground: pub fn num_num(a: f64, b: f64) -> f64 {
if (b == 2) {
a.log2()
} else if (b == 10) {
a.log10()
} else {
a.log(b)
}
} |
One thing I would be worried about with solutions that do checks like this is speed. |
Well I think that the speed penalty should not be an issue, since it's a simple test, especially in variant two. If I were to choose, I would prefer correctness over speed. |
I've noticed another problem with some of the implementations above. |
Hi there,
while experimenting with Uiua, I found that e.g. ₙ10 1000 gives the incorrect result of 2.9999999999999996 instead of 3, caused by a rounding error:
The following test case should uncover the problem:
This error can be reproduced in the pad as well as in a locally installed interpreter.
It seems that there is an Issue with Rust's underlying f64/f32 log and that there exists an integer variant that circumvents/fixes the problem. However,
uiua/src/algorithm/pervade.rs
Line 884 in f19ec8b
Jegge
The text was updated successfully, but these errors were encountered: