Have you ever needed trigonometric functions, say, sin(x)
for arguments x
larger (in absolute value) than, say, 1e16? If so, what was your application?
I am asking from the perspective of their implementations in a compiler, as well as in stdlib
for non-intrinsic functions. This is a common issue, see, e.g.:
- math: trigonometric argument reduction fails for huge arguments · Issue #6794 · golang/go · GitHub
- Accurate trigonometric functions for large arguments | Numerical .NET
The hard part is in argument reduction, which for values x > 1e16 requires more than quadruple precision pi
value (typically pi/4
, but also 2*pi
can be used). I am happy to explain why and how it works, but that’s a separate issue. Here I am interested in the user experience. For |x| < 1e16, there exist quite efficient reduction techniques.
From a compiler or stdlib
's perspective, we have 4 options for x > 1e16:
- Give an accurate answer
- Return an increasingly incorrect result (such as half or more of the digits are incorrect, so worse than 1e-8 accuracy for double precision)
- Give an error message
- Return NaN (which can trigger a NaN trap exception with the proper compiler option if the user wants, effectively becoming 3.)
From a performance perspective, it seems 1., 3. and 4. all require some kind of branching (if statement for x > something
). In that case, it seems if branching is required anyway, then let’s do 1. to return the correct answer and be done with it. There is no additional performance penalty (I think), and the function will work for all double precision numbers. And this should be the default.
The option 2. might have a possible advantage for optimizations if it allows branchless computation that can always be efficiently vectorized. If that’s not the case, then I don’t think there is any advantage. But if it is the case, then one practical solution might be to enable this implementation with a compiler option (or as a special name in stdlib
), and if enabled the compiler can also insert runtime checks (in Debug mode) to ensure the argument is always smaller than 1e16 (or whatever the threshold).
Do you think that’s a reasonable approach?
But I would still be interested in actual applications. It would greatly help me form a better opinion.