weird behavior with casinh()

Paul Zimmermann Paul.Zimmermann@inria.fr
Mon Oct 7 07:42:55 GMT 2024


       Hi,

indeed, with the test program we used for our comparison [1],
I obtain with Newlib 4.4.0:

casinh 0 -1 0x0p+0,0x1.0000002d413cdp+26 [inf] inf inf
libm gives (inf,-nan)
mpc gives (0x1.2b7088750621fp+4,0x1.921fb54442d18p+0)

i.e., for z=(0x0p+0,0x1.0000002d413cdp+26), the casinh() function from
Newlib gives (Inf,NaN) instead of (0x1.2b7088750621fp+4,0x1.921fb54442d18p+0).

Paul

[1] https://inria.hal.science/hal-04714173

> Date: Mon,  7 Oct 2024 00:29:24 +0000
> From: Leo Filippini <leo.filippini@posteo.org>
> 
> Hello,
> 
> 
> I noticed while writing code for am ARM project that the casinh() 
> function in newlib behaves oddly for real numbers larger than about 10.
> 
> 
> I don't have a working newlib for my desktop machine so I copied the 
> code from the source, but I see the same behavior on my ARM project. 
> This is a minimum working example:
> 
> 
> #include <stdio.h>
> #include <complex.h>
> #include <math.h>
> 
> void printComplex(char * name, double complex num) {
>      printf("%s: ", name);
>      printf("%.16f %.16f * j\n", creal(num), cimag(num));
> }
> 
> double complex newlib_casin(double complex z)
> {
>      double complex w;
>      double complex ca, ct, zz, z2;
>      double x, y;
> 
>      x = creal(z);
>      y = cimag(z);
> 
> #if 0
>      if (y == 0.0) {
>          if (fabs(x) > 1.0) {
>              w = M_PI_2 + 0.0 * I;
> #if 0
>              mtherr ("casin", DOMAIN);
> #endif
>          } else {
>              w = asin(x) + 0.0 * I;
>          }
>          return w;
>      }
> #endif
> 
> /* Power series expansion */
> /*
> b = cabs(z);
> if( b < 0.125 )
> {
> z2.r = (x - y) * (x + y);
> z2.i = 2.0 * x * y;
> 
> cn = 1.0;
> n = 1.0;
> ca.r = x;
> ca.i = y;
> sum.r = x;
> sum.i = y;
> do
>      {
>      ct.r = z2.r * ca.r  -  z2.i * ca.i;
>      ct.i = z2.r * ca.i  +  z2.i * ca.r;
>      ca.r = ct.r;
>      ca.i = ct.i;
> 
>      cn *= n;
>      n += 1.0;
>      cn /= n;
>      n += 1.0;
>      b = cn/n;
> 
>      ct.r *= b;
>      ct.i *= b;
>      sum.r += ct.r;
>      sum.i += ct.i;
>      b = fabs(ct.r) + fabs(ct.i);
>      }
> while( b > MACHEP );
> w->r = sum.r;
> w->i = sum.i;
> return;
> }
> */
> 
> 
>      ca = x + y * I;
>      ct = ca * I;
>      /* sqrt( 1 - z*z) */
>      /* cmul( &ca, &ca, &zz ) */
>      /*x * x  -  y * y */
>      zz = (x - y) * (x + y) + (2.0 * x * y) * I;
> 
>      zz = 1.0 - creal(zz) - cimag(zz) * I;
>      z2 = csqrt(zz);
>      //printComplex("\tz2", z2);
> 
>      //printComplex("\tct", ct);
>      zz = ct + z2;
>      //printComplex("\tzz", zz);
>      zz = clog(zz);
>      //printComplex("\tzz", zz);
>      /* multiply by 1/i = -i */
>      w = zz * (-1.0 * I);
>      return w;
> }
> 
> double complex newlib_casinh(double complex z) {
>      double complex w;
> 
>      w = -1.0 * I * newlib_casin(z * I);
>      return w;
> }
> 
> double complex other_casinh(double complex z) {
>      return clog(z + csqrt(z * z + 1));
> }
> 
> int main(void) {
> 
>      double complex yc;
>      int x;
>      for (x = 15; x < 21; x++) {
> 
>          printComplex("x: ", x);
> 
>          yc = csinh(x);
>          printComplex("yc = csinh(x)", yc);
>          printf("\n");
> 
>          printComplex("glibc casinh(yc)", casinh(yc));
>          printComplex("newlib casinh(yc)", newlib_casinh(yc));
>          printComplex("other casinh(yc)", other_casinh(yc));
>          printf("\n\n");
>      }
> 
>      }
> 
> 
> When I compile this on my linux machine I get:
> 
> x: : 15.0000000000000000 0.0000000000000000 * j
> yc = csinh(x): 1634508.6862359023652971 0.0000000000000000 * j
> 
> glibc casinh(yc): 15.0000000000000000 0.0000000000000000 * j
> newlib casinh(yc): 14.9998785788736946 -0.0000000000000000 * j
> other casinh(yc): 15.0000000000000000 0.0000000000000000 * j
> 
> ...
> 
> a few more lines edited away for brevity
> 
> ...
> 
> 
> x: : 19.0000000000000000 0.0000000000000000 * j
> yc = csinh(x): 89241150.4815936386585236 0.0000000000000000 * j
> 
> glibc casinh(yc): 19.0000000000000000 0.0000000000000000 * j
> newlib casinh(yc): 18.0218266945585768 -0.0000000000000000 * j
> other casinh(yc): 19.0000000000000000 0.0000000000000000 * j
> 
> 
> x: : 20.0000000000000000 0.0000000000000000 * j
> yc = csinh(x): 242582597.7048951387405396 0.0000000000000000 * j
> 
> glibc casinh(yc): 20.0000000000000000 0.0000000000000000 * j
> newlib casinh(yc): inf -nan * j
> other casinh(yc): 20.0000000000000000 0.0000000000000000 * j
> 
> 
> It seems with the input getting larger and larger casinh() looses 
> precision until it stops working completely.
> 
> Glibc's version of casinh() doesn't misbehave like that, and also a 
> simple function other_casinh() that I created with an expression for the 
> inverse hyperbolic sine behaves as expected.
> 
> 
> Am I doing something wrong with this? are there disadvantages to 
> other_casinh() with respect to newlib's implementation of it that I 
> don't know about? other_casinh() also seems to result in slightly 
> smaller code size when I compile it for ARM cortex M0.
> 
> 
> Thank you,
> 
> Leo
> 
> 
> 


More information about the Newlib mailing list