Gnulib's replacement for the sinl() function was buggy for 22 years.
2025-12-03 Bruno Haible <[email protected]> sinl: Fix result for small negative values. * lib/sincosl.c (kernel_sinl): For small |x|, multiply the result with the sign of x. * tests/test-sinl.c (main): Test two small negative values. diff --git a/lib/sincosl.c b/lib/sincosl.c index c693ad4db7..fa2c973f28 100644 --- a/lib/sincosl.c +++ b/lib/sincosl.c @@ -125,17 +125,17 @@ kernel_sinl (long double x, long double y, int iy) polynomial of degree 17. */ if (x < 0.000000000000000006938893903907228377647697925567626953125L) /* |x| < 2^-57 */ if (!((int) x)) - return x; /* generate inexact */ + return x * sign; /* generate inexact */ z = x * x; - return x + (x * (z * (SIN1 + z * (SIN2 + z * (SIN3 + z * (SIN4 + - z * (SIN5 + - z * - (SIN6 + - z * - (SIN7 + - z * - SIN8))))))))); + return (x + x * (z * (SIN1 + z * + (SIN2 + z * + (SIN3 + z * + (SIN4 + z * + (SIN5 + z * + (SIN6 + z * + (SIN7 + z * + SIN8))))))))) * sign; } else { diff --git a/tests/test-sinl.c b/tests/test-sinl.c index 47f0afafd0..52cd5aeb8e 100644 --- a/tests/test-sinl.c +++ b/tests/test-sinl.c @@ -41,5 +41,16 @@ main () y = sinl (x); ASSERT (y >= 0.5646424733L && y <= 0.5646424734L); + /* A small negative value. */ + x = -0.1L; + y = sinl (x); + ASSERT (y >= -0.09983341665L && y <= -0.09983341664L); + + /* A very small negative value. */ + x = -0.000000000000000004L; + y = sinl (x); + ASSERT (y >= -0.000000000000000004000000000L + && y <= -0.000000000000000003999999999L); + return test_exit_status; }
