Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <3805114.vVB5bnNuiK@omega>
Date: Sun, 31 Jan 2021 13:36:59 +0100
From: Bruno Haible <bruno@...sp.org>
To: Rich Felker <dalias@...c.org>
Cc: musl@...ts.openwall.com
Subject: expm1l, logl, log1pl, log2l, log10l, remainderl functions imprecise on arm64 and s390x

Rich Felker wrote:
> Thanks. I suspect you may find others; it's a known issue that not all
> of the long double functions are appropriately accurate for archs
> where long double is quad. ...
> Hopefully we'll get the rest in better shape soon.

Indeed. Here are more test cases. Hope you can use them. I computed the
"correct values" using the arbitrary-precision arithmetic of GNU clisp.


Test case for expm1l:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef expm1l
extern
#ifdef __cplusplus
"C"
#endif
long double expm1l (long double);
static long double dummy (long double x) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_expm1l) (long double) = argc ? expm1l : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 11.358L;
    long double y = my_expm1l (x);
    long double z = my_expm1l (- x);
    volatile long double t = (1.0L + y) * z;
    long double err = (y + t) * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\ny = %.*Lg\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), y,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -100.0L && err <= 100.0L))
      result |= 1;
  }
  return result;
}
===============================================================================
Output on musl/arm64 and musl/s390x:

LDBL_MANT_DIG = 113
y = 85646.90127933183975983411073684692
z = -0.9999883242906707492281270788225811
err = -1.11952e+22

Correct values:
y = 85646.9012793317906249282906550266078499527412871
z = -0.999988324290670724047210016651430966314251470277


Test case for logl:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef logl
extern
#ifdef __cplusplus
"C"
#endif
long double logl (long double);
static long double dummy (long double x) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_logl) (long double) = argc ? logl : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 16.981137113807045L;
    long double y = my_logl (x);
    long double z = my_logl (1.0L / x);
    long double err = (y + z) * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\ny = %.*Lg\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), y,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -100.0L && err <= 100.0L))
      result |= 1;
  }

  return result;
}
===============================================================================
Output on musl/arm64 and musl/s390x:
LDBL_MANT_DIG = 113
y = 2.832103146474115096253854062524624
z = -2.83210314647411554034306391258724
err = -2.30584e+18

Correct values:
y = 2.83210314647411532843886058554627196598416729434
z = -2.83210314647411532843886058554627196598416729434


Test case for log1pl:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef log1pl
extern
#ifdef __cplusplus
"C"
#endif
long double log1pl (long double);
static long double dummy (long double x) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_log1pl) (long double) = argc ? log1pl : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 11.358L;
    long double y = my_log1pl (x);
    long double z = my_log1pl (- x / (1.0L + x));
    long double err = (y + z) * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\ny = %.*Lg\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), y,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -900.0L && err <= 900.0L))
      result |= 1;
  }

  return result;
}
===============================================================================
Output on musl/arm64 and musl/s390x:
LDBL_MANT_DIG = 113
y = 2.514303626638787925173801340861246
z = -2.51430362663878748108459149079863
err = 2.30584e+18

Correct values:
y = 2.514303626638787808991104205181310474969595858254
z = -2.514303626638787808991104205181310474969595858254


Test case for log2l:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef log2l
extern
#ifdef __cplusplus
"C"
#endif
long double log2l (long double);
static long double dummy (long double x) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_log2l) (long double) = argc ? log2l : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 11.358L;
    long double y = my_log2l (x);
    long double z = my_log2l (1.0L / x);
    long double err = (y + z) * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\ny = %.*Lg\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), y,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -10000.0L && err <= 10000.0L))
      result |= 1;
  }
  if (DBL_MAX_EXP < LDBL_MAX_EXP)
    {
      long double x = ldexpl (1.0L, DBL_MAX_EXP);
      long double y = my_log2l (x);
      if (y > 0 && y + y == y) /* infinite? */
        result |= 2;
    }

  return result;
}
===============================================================================
Output and exit code on musl/arm64 and musl/s390x:
LDBL_MANT_DIG = 113
y = 3.50563691176277458794174890499562
z = -3.505636911762774143852539054933004
err = 2.30584e+18
3

Correct values:
y = 3.505636911762774324741549890160677088025417867858
z = -3.505636911762774324741549890160677088025417867858


Test case for log10l:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef log10l
extern
#ifdef __cplusplus
"C"
#endif
long double log10l (long double);
static long double dummy (long double x) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_log10l) (long double) = argc ? log10l : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 7.90097792256024576L;
    long double y = my_log10l (x);
    long double z = my_log10l (1.0L / x);
    long double err = (y + z) * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\ny = %.*Lg\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), y,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -100.0L && err <= 100.0L))
      result |= 1;
  }

  return result;
}
===============================================================================
Output on musl/arm64 and musl/s390x:
LDBL_MANT_DIG = 113
y = 0.8976808482634930363985859003150836
z = -0.8976808482634929253762834377994295
err = 5.76461e+17

Correct values:
y = 0.897680848263492989548770123475961808070106674916
z = -0.897680848263492989548770123475961808070106674916


Test case for remainderl:
===============================================================================
#ifndef __NO_MATH_INLINES
# define __NO_MATH_INLINES 1 /* for glibc */
#endif
#include <float.h>
#include <math.h>
#include <stdio.h>
#undef remainderl
extern
#ifdef __cplusplus
"C"
#endif
long double remainderl (long double, long double);
static long double dummy (long double x, long double y) { return 0; }
int main (int argc, char *argv[])
{
  long double (* volatile my_remainderl) (long double, long double) = argc ? remainderl : dummy;
  int result = 0;
  {
    const long double TWO_LDBL_MANT_DIG = /* 2^LDBL_MANT_DIG */
      (long double) (1U << ((LDBL_MANT_DIG - 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 1) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 2) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 3) / 5))
      * (long double) (1U << ((LDBL_MANT_DIG - 1 + 4) / 5));
    long double x = 11.357996098166760874793827872740874983168L;
    long double y = 0.486497838502717923110029188864352615388L;
    long double z = my_remainderl (x, y) - (x - 23 * y);
    long double err = z * TWO_LDBL_MANT_DIG;
    printf ("LDBL_MANT_DIG = %d\nz = %.*Lg\nerr = %g\n",
            LDBL_MANT_DIG,
            (int)(LDBL_MANT_DIG * 0.30103), z,
            (double)err);
    if (!(err >= -32.0L && err <= 32.0L))
      result |= 1;
  }

  return result;
}
===============================================================================
Output on musl/arm64 and musl/s390x:
LDBL_MANT_DIG = 113
z = -6.067178037652900601980525052256813e-15
err = -3.15026e+19

Correct value: z = 0


Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.