Overflow: When x is verymodestly negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
// float example
float yn = sinhf(q) / q;
// If a non-finite value resulted, try a slower, less precise, yet wider range approach.
if (!isfinite(yn)) {
yn = expf(q - logf(q) - logf(2));
}
float yn = sinhf(q) / q;
if (!isfinite(yn)) {
// I expect this to work for all `x` where the math `sinhf(q) / q <= FLT_MAX`.
yn = expf(q/2);
yn *= yn/(2*q);
}
Overflow: When x is very negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
// float example
float yn = sinhf(q) / q;
if (!isfinite(yn)) {
yn = expf(q - logf(q) - logf(2));
}
float yn = sinhf(q) / q;
if (!isfinite(yn)) {
yn = expf(q/2);
yn *= yn/(2*q);
}
Overflow: When x is modestly negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
// float example
float yn = sinhf(q) / q;
// If a non-finite value resulted, try a slower, less precise, yet wider range approach.
if (!isfinite(yn)) {
yn = expf(q - logf(q) - logf(2));
}
float yn = sinhf(q) / q;
if (!isfinite(yn)) {
// I expect this to work for all `x` where the math `sinhf(q) / q <= FLT_MAX`.
yn = expf(q/2);
yn *= yn/(2*q);
}
Trig errors
q = sqrtf(x) has up to 0.5 ULP error in the result. When q < 99.99%*π, sinf(q) is well behaved. Yet at/near every following multiple of π, the ULP error of sinf(q) can be terrible, even if the absolution value is very reasonable. As q grows and its least significant significant digit nears 1.0, sign and magnitude of sinf(q) becomes meaningless.
Additional code is needed to compensate for square root errors. Considerations start at x > 1.57 for ULP errors.
There are no for absolute errors concerns for finite x.
Trig errors
q = sqrtf(x) has up to 0.5 ULP error in the result. When q < 99.99%*π, sinf(q) is well behaved. Yet at/near every following multiple of π, the ULP error of sinf(q) can be terrible, even if the absolution value is very reasonable. As q grows and its least significant significant digit nears 1.0, sign and magnitude of sinf(q) becomes meaningless.
Additional code is needed to compensate for square root errors. Considerations start at x > 1.57 for ULP errors.
There are no for absolute errors concerns for finite x.
Infinities: When x is +infinity, the expected math result is 0.0, yet OP's code may return Not-a-number due to sin(infinity).
Overflow: When x is very negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
Modest magnitude x: I found that, using float, q = sqrtf(fabsf(x)); return sinhf(q) / q; loses about 6 bits of correctness before sinhf(q) hits overflow. This is because the error is forming the square root q and then q applied to sinhf(g) incurs an error proportional to q.
To really reduce this function's error, a better, TBD, approach is needed as x grows negative (about x < -4).
When x is +infinity, the expected math result is 0.0, yet OP's code may return Not-a-number due to sin(infinity).
When x is very negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
Infinities: When x is +infinity, the expected math result is 0.0, yet OP's code may return Not-a-number due to sin(infinity).
Overflow: When x is very negative, sinh(q) may overflow into infinity, yet the extend math quotient sinh(q)/q is still finite.
Modest magnitude x: I found that, using float, q = sqrtf(fabsf(x)); return sinhf(q) / q; loses about 6 bits of correctness before sinhf(q) hits overflow. This is because the error is forming the square root q and then q applied to sinhf(g) incurs an error proportional to q.
To really reduce this function's error, a better, TBD, approach is needed as x grows negative (about x < -4).