-
Notifications
You must be signed in to change notification settings - Fork 221
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
cyl_bessel_j_zero does not respect policies::promote_double<false> #398
Comments
The Lanczos one I recognize: It's required. There is an ill-conditioned filter computation that really does need that extra precision; see here. Looks like its also responsible for the call to |
I am porting code to a platform without native 80-bit or 128-bit (i.e. 'long double' is equivalent to 'double'). Will the Lanczos code fail to converge on such a platform? |
@evanmiller : It won't fail to converge, the filter coefficients will just not be as accurate. For digital filters, we generally want half-ulp accuracy precomputed once, under the assumption that applying the filter to data will dominate the execution time. |
@NAThompson Is it reasonable to ask that this coefficient computation respect the |
There's almost zero hope you'll get the same result on all platforms, even if it does respect The semantics of In the Lanczos filters, we're using argument promotion just to achieve accuracy, because the filter computation is very ill-conditioned. |
Well, I don't need bit-for-bit compatibility. But if there are indeed significant accuracy issues introduced without 80-bit long double, I would like to have control over it for the sake of general cross-platform consistency. It seems to me that because In any event, if the current algorithm only guarantees accuracy when 80-bit registers are present, then it appears I will need to find another algorithm. |
I did a very deep lit search for this algorithm, and I couldn't find another one that had the same performance and capabilities. So if you do go find another where you can get the filter coeffs without an ill-conditioned step, I'd be very happy about it. |
Well, I'm just looking for a pure 64-bit Bessel algorithm - I'm not trying to reinvent the Lanczos wheel. It could be something as simple as replacing Boost's lgamma with the system's lgamma. I will need to investigate more to see exactly where the long double dependencies are for the functions that I'm calling. |
@evanmiller : Actually I think we're talking about a different Lanczos algorithm. I'm pretty sure there are no filter coefficients needed there. Probably @jzmaddock will know what the Bessel function is doing. |
This does look like a bug to me, @NAThompson the code in question is my old lanczos gamma function approximation, and it should be using a double precision approximation in this case IMO. @evanmiller can you get a call stack for where the long double calls occur? |
@jzmaddock I don't have a stack trace handy; I'm just disassembling a static binary right now. I think I hit some Lanczos code through this code path earlier today though: I can post suspicious disassembly here as I come across it. It looks like there is a similar issue affecting the |
A long double of
|
I'm making good progress eliminating the long doubles by passing policies around in more places - will open a PR later today. |
The math/include/boost/math/special_functions/detail/unchecked_factorial.hpp Lines 489 to 500 in 6bb0e8d
|
A small demonstration program is actually just: #include <boost/math/special_functions/beta.hpp>
int main() { } Based on the disassembly, it looks like an 80-bit Lanczos approximator is getting force-instantiated before any user code is called at all: ___cxx_global_var_init:
0000000100002dd0 pushq %rbp
0000000100002dd1 movq %rsp, %rbp
0000000100002dd4 movq 0x1225(%rip), %rax ## literal pool symbol address: guard variable for boost::math::lanczos::lanczos_initializer<boost::math::lanczos::lanczos17m64, long double>::initializer
0000000100002ddb cmpb $0x0, (%rax)
0000000100002dde jne 0x100002dfe
0000000100002de4 movq 0x123d(%rip), %rdi ## literal pool symbol address: boost::math::lanczos::lanczos_initializer<boost::math::lanczos::lanczos17m64, long double>::initializer
0000000100002deb callq boost::math::lanczos::lanczos_initializer<boost::math::lanczos::lanczos17m64, long double>::init::init() ## boost::math::lanczos::lanczos_initializer<boost::math::lanczos::lanczos17m64, long double>::init::init()
0000000100002df0 movq 0x1209(%rip), %rax ## literal pool symbol address: guard variable for boost::math::lanczos::lanczos_initializer<boost::math::lanczos::lanczos17m64, long double>::initializer
0000000100002df7 movq $0x1, (%rax)
0000000100002dfe popq %rbp
0000000100002dff retq So it's possible that this Lanczos code is never actually hit in my test program. |
I see this, here's a test program for you (requires current develop):
The instantiation call stack gives the source of the error(s), the first of which is caused by:
Which should be using Many thanks for looking into this!! |
Clever solution! Just so you know: A complete audit is outside the scope of what I'm doing right now, but I'll send along pull requests for any more of those double-long weeds that I come across. The first batch of proposed changes is in #399. |
Changing template <>
inline float binomial_coefficient<float, policies::policy<> >(unsigned n, unsigned k, const policies::policy<>& pol)
{
typedef policies::normalise<
policies::policy<>,
policies::promote_float<true>,
policies::promote_double<false>,
policies::discrete_quantile<>,
policies::assert_undefined<> >::type forwarding_policy;
return policies::checked_narrowing_cast<float, forwarding_policy>(binomial_coefficient<double>(n, k, forwarding_policy()), "boost::math::binomial_coefficient<%1%>(unsigned,unsigned)");
} If so, I will add it to my pull request. |
Yes exactly so! Once your PR is in I'll try and do a thorough trawl for other issues. |
Ok! I've committed the The last function affecting my own code base is |
With the PR merged, I am now down to two sources of
double const boost::math::tools::make_big_value<double>(long double, char const*, boost::integral_constant<bool, true> const&, boost::integral_constant<bool, false> const&) I can't yet tell if I can expect identical behavior on 64-bit and 80-bit platforms. For example, math/include/boost/math/special_functions/detail/bessel_j0.hpp Lines 67 to 75 in d4d0297
I think that because a |
This seems to eliminate the
|
Before this change, any function calling unchecked_factorial<double> would generate long double code. This created a headache for users attempting to eliminate long doubles from their compiled code. A more elegant solution might use templates to create a single lookup array for all major floating-point types, but the present solution (adding an explicit double array) works just fine. See #398.
I'm auditing a code base to eliminate uses of
long double
on x86 (i.e. eliminate all x87 instructions). After compiling, I dump the disassembly and look for forbidden instructions and function calls.I found that the following small test program - which I am led to believe by Boost documentation will not promote doubles to long doubles - includes calls to a number of
long double
functions when compiled.A sampling of disassembly:
Is this expected behavior, or a bug?
I realize that I can disable x87 with a compiler flag - but my compiler is actually crashing when I try that. So I am hoping to resolve the issue within Boost.
The text was updated successfully, but these errors were encountered: