Skip to content
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

Accuracy of bessel functions for larger arguments #4

Open
heltonmc opened this issue Apr 8, 2023 · 4 comments
Open

Accuracy of bessel functions for larger arguments #4

heltonmc opened this issue Apr 8, 2023 · 4 comments

Comments

@heltonmc
Copy link

heltonmc commented Apr 8, 2023

I was looking through the code and was curious how

b = cos(ax+xn)
this line held up? I don't know how to exactly test this accuracy 😃 but I found that this simple summation is prone to some huge cancellation. Especially because the two values vary so much in magnitude.

It's much better to do something like...

s_x, c_x = sincos(x)
s_xn, c_xn = sincos(xn)
  (c_x * c_xn - s_x * s_xn)

because it essentially acts as a range reduction putting the two values at similar magnitude. Therefore, when summed they lose less precision. We are working on a slightly different approach but if Fortran has a good function to give you both sin and cos this could significantly improve the accuracy! It should only cost a few cycles but will be able to provide much better relative tolerances.

@perazz
Copy link
Owner

perazz commented May 1, 2023

Very good point, thank you! My Fortran implementation is not as polished as yours, though it gets pretty good performance already. I'll definitely put this improvement in the pipeline.

BTW: Fortran has no sincos function but all compilers will detect when both sin and cos are requested, and use a sincos function if it's available. An argument-sum wrapper would look like:

elemental real(BK) function cos_sum(a,b)
   real(BK), intent(in) :: a, b
   real(BK) :: ca,sa,cb,sb
   ca = cos(a)
   sa = sin(a)
   cb = cos(b)
   sb = sin(b)
   cos_sum = (ca*cb-sa*sb)
end function 

@heltonmc
Copy link
Author

heltonmc commented May 1, 2023

Ya there’s a couple of ways to do it but it pops up frequent enough that having a function for it will be helpful. It will be slightly slower of course but is much more accurate even near zeros.

You could also reduce each argument within the sin first before adding them together. That will also significantly improve the accuracy. In general only one argument needs to be reduced as the other one is usually <pi. So that will be faster and Definitely more accurate than the naive case but unsure if it’s as accurate as approach above. Probably be similar depending on how good the argument reduction is done!

@perazz
Copy link
Owner

perazz commented May 1, 2023

It will be slightly slower of course but is much more accurate even near zeros.

That's indeed one of the reasons I try to keep whole packages within a single module file. I know many people don't like the approach and split packages into many files. But link time optimization in Fortran is usually pretty bad, and if your whole package is contained within the same file, any compilers will inline all these small functions, so, you get more readable code with no performance loss.

@heltonmc
Copy link
Author

heltonmc commented May 1, 2023

Sounds good! I think just inserting your above code is fine. My comment was more so that this type of function pops up very frequently in all the Bessel function implementations so it's good to have a routine you like and can change easily.

I don't think there's actually going to be much link-time optimization besides if your compiler will inline or not as this would be a strictly accuracy improvement at the cost of several nanoseconds. So that tradeoff is definitely up to you as this will contain more arithmetic operations 😄

This is essentially the outcome of delaying any floating point issues until the very end. All the arithmetic up to this point should be accurate to less than <1.5 ULP. So any numerical errors are mostly contained on this single line. Unfortunately, this line is very expensive even in the naive case compared to rest of routine so even a small change in this will be felt in the total routine.

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

No branches or pull requests

2 participants