Skip to content

Improve Precision and Performance for All Functions#1

Open
mark-schultz-wu wants to merge 6 commits intomichaelciraci:masterfrom
mark-schultz-wu:master
Open

Improve Precision and Performance for All Functions#1
mark-schultz-wu wants to merge 6 commits intomichaelciraci:masterfrom
mark-schultz-wu:master

Conversation

@mark-schultz-wu
Copy link

Hey, I saw that atan and ln were using very large Taylor series and that some tests were running at a reduced precision. I wanted to see if I could get everything to pass the brute-force test suite at a high precision.

Here are the main changes:

  • atan: I implemented a range reduction using the identity atan(x) = 2 * atan(x / (1 + sqrt(1+x^2))). This repeatedly shrinks the input x to a small interval (e.g., [0, 1/8]). By reducing the interval, the Taylor series converges much faster and now passes the high-precision tests.

  • ln: I used the identity ln(x) = 2 * atanh((x-1)/(x+1)). I then added a similar range reduction for atanh and approximated it with a small Taylor series.

  • Other Fixes: I rewrote the exp implementation using the identity exp(x) = 2^k * exp(r) to improve its precision. I also rewrote sin(x) and cos(x) with more numerically stable argument reduction to resolve the test failures in csc and cot.

  • Testing & Performance: I added criterion as a dev dependency to benchmark the changes. The atan function saw a ~1500x speedup, and all functions now pass the brute-force tests with an absolute tolerance of 1e-10. There was a minor, acceptable performance regression in cos and sin due to the more complex, correct logic.

This introduces a new benchmark suite using the `criterion` crate to establish a performance baseline for the library's core functions.  A `benches/performance.rs` file is added to benchmark a representative set of functions, including `cos`, `sin`, `atan`, `ln`, and `asin`, across various inputs.  The `ln` function has been made public to allow it to be accessed by the benchmark runner.
The previous `atan` implementation used a high-iteration Taylor series that was slow and only provided ~5-6 decimal places of precision.  This refactors the function to use iterative argument reduction. The input is now mapped to a small value (`< 0.125`) before applying a much shorter, more efficient Taylor series.  This new approach achieves full f64 machine precision while also providing a significant performance improvement.
Add a taylor approximation for atanh in preparation for a reimplementation of `ln`.
This fixes major precision and performance issues, allowing all functions to pass a full brute-force test suite at machine precision.  - Rewrites `sin` and `cos` to use robust argument reduction. This fixes precision bugs for large inputs and resolves the persistent test failures in `cot` and `csc`.  - Rewrites `exp` to use range reduction, fixing bugs that caused failures in `sinh` and `cosh`.  - Adds brute-force tests for all remaining public functions and makes the `cot` and `csc` tests more robust to avoid false negatives near poles.
@michaelciraci
Copy link
Owner

Thanks so much for taking the time to contribute. Sorry I'm just seeing this now (I need to check my GitHub notifications).

A few days I found that there are several functions from Rust's libm port that require minimal work to make it const: https://github.com/rust-lang/compiler-builtins/tree/master/libm

Here's an example of a function (atan) from libm that required minimal work to put into this crate: https://github.com/michaelciraci/Trig-Const/blob/cd046bc4256b7662b5e36e4528bea38f78b660b5/src/atan.rs

I'll go through this MR and compare precision to std trig functions. There were some functions from libm that couldn't be made const, so it may be cherry picking different functions from different sources. My ultimate goal is highest precision as possible.

@michaelciraci
Copy link
Owner

You made significant improvements over the algorithms in this crate from a week ago. Although using a port from libm still shows a pretty substantial improvement over using a series. This is what I get for benchmarking:

Function       | Total Tests| Diff Count|       Max Diff
Current atan   |      502655|      32840|    2.22045e-16
Mark    atan   |      502655|     129988|    5.55112e-16
Current atanh  |       19999|       6706|    1.95399e-13
Mark    atanh  |       19999|      13062|    1.74083e-13
Current ln     |       99999|       3502|    8.88178e-16
Mark    ln     |       99999|      79094|    2.13163e-14
Current exp    |       20001|       2028|    3.63798e-12
Mark    exp    |       20001|      15434|    1.81899e-11
Current sin    |      502655|      22582|    1.11022e-16
Mark    sin    |      502655|     449397|    1.11022e-15
Current cos    |      502655|      21844|    1.11022e-16
Mark    cos    |      502655|     440723|    1.11022e-15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants