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;
 }




Reply via email to