[PATCH 5/5] x86 long double: Add tests for pseudo normal numbers

Adhemerval Zanella adhemerval.zanella@linaro.org
Tue Dec 22 21:48:42 GMT 2020



On 15/12/2020 11:13, Siddhesh Poyarekar via Libc-alpha wrote:
> Add some tests for fpclassify, isnanl, isinfl and issignaling.
> ---
>  sysdeps/x86/fpu/Makefile        |   3 +-
>  sysdeps/x86/fpu/test-unnormal.c | 196 ++++++++++++++++++++++++++++++++
>  2 files changed, 198 insertions(+), 1 deletion(-)
>  create mode 100644 sysdeps/x86/fpu/test-unnormal.c
> 
> diff --git a/sysdeps/x86/fpu/Makefile b/sysdeps/x86/fpu/Makefile
> index 600e42c3db..e77de56d14 100644
> --- a/sysdeps/x86/fpu/Makefile
> +++ b/sysdeps/x86/fpu/Makefile
> @@ -4,11 +4,12 @@ CPPFLAGS += -I../soft-fp
>  
>  libm-support += powl_helper
>  tests += test-fenv-sse test-fenv-clear-sse test-fenv-x87 test-fenv-sse-2 \
> -	 test-flt-eval-method-387 test-flt-eval-method-sse
> +	 test-flt-eval-method-387 test-flt-eval-method-sse test-unnormal
>  CFLAGS-test-fenv-sse.c += -msse2 -mfpmath=sse
>  CFLAGS-test-fenv-clear-sse.c += -msse2 -mfpmath=sse
>  CFLAGS-test-fenv-sse-2.c += -msse2 -mfpmath=sse
>  CFLAGS-test-flt-eval-method-387.c += -fexcess-precision=standard -mfpmath=387
>  CFLAGS-test-flt-eval-method-sse.c += -fexcess-precision=standard -msse2 \
>  				     -mfpmath=sse
> +CFLAGS-test-unnormal.c += -fsignaling-nans -std=c2x
>  endif

A possibility is to hookup this tests on 
math/libm-test-{fpclassify,isnan,isinf,issignaling}.inc using the new define
I suggested on the 4/5 part [1] so you can also check if no exceptions are being
generated and errno is not set.

It increases the tests coverage and avoid a arch-specific tests.

[1] https://sourceware.org/pipermail/libc-alpha/2020-December/121004.html

> diff --git a/sysdeps/x86/fpu/test-unnormal.c b/sysdeps/x86/fpu/test-unnormal.c
> new file mode 100644
> index 0000000000..fc65d9290f
> --- /dev/null
> +++ b/sysdeps/x86/fpu/test-unnormal.c
> @@ -0,0 +1,196 @@
> +/* Test long double classification with x86 pseudo normal numbers.
> +   Copyright (C) 2020 Free Software Foundation, Inc.
> +
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <math.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +struct tests
> +{
> +  const char *val;
> +  int class;
> +} inputs[] = {
> +      /* Normal.  */
> +      {"\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04", FP_NAN},
> +      {"\x00\x04\x00\x00\x00\x00\x00\xf0\x00\x04", FP_NORMAL},
> +      /* Pseudo-infinite.  */
> +      {"\x00\x00\x00\x00\x00\x00\x00\x00\xff\x7f", FP_NAN},
> +      {"\x00\x00\x00\x00\x00\x00\x00\x80\xff\x7f", FP_INFINITE},
> +      /* Pseudo-zero.  */
> +      {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", FP_NAN},
> +      {"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", FP_ZERO},
> +};
> +

I find this quite confusing to parse the value represented. I think
it would be way more readable to include <math_ldbl.h> and define the
values using the ieee_long_double_shape_type 'parts' member.

If the idea is also to check snprintf, I think it would be better to
the tests to a different test.

Also make the inputs a 'static' variable.

> +const char *classes[5];
> +#define stringify(N) #N
> +
> +static void
> +initialize (void)
> +{
> +  classes[FP_NAN] = stringify(FP_NAN);
> +  classes[FP_INFINITE] = stringify(FP_INFINITY);
> +  classes[FP_ZERO] = stringify(FP_ZERO);
> +  classes[FP_SUBNORMAL] = stringify(FP_SUBNORMAL);
> +  classes[FP_NORMAL] = stringify(FP_NORMAL);
> +}
> +
> +static void
> +unnormal_str (const char *val, char *ret)
> +{
> +  for (int i = 9; i >= 0; i--)
> +    {
> +      if (i == 7 || i == 3)
> +	*ret++ = ' ';
> +      snprintf(ret, 3, "%02x", (unsigned char) val[i]);
> +      ret += 2;
> +    }
> +}
> +
> +static int
> +test_fpclassify (void)
> +{
> +  int ret = 0;
> +
> +  printf ("* fpclassify tests:\n");

Maybe add the verbose output only when tests is invoke with --debug
(same for other cases).

> +  for (int i = 0; i < sizeof (inputs)/sizeof (struct tests); i++)
> +    {
> +      long double value;
> +      char buf[22];
> +
> +      memcpy (&value, inputs[i].val, 10);
> +      unnormal_str(inputs[i].val, buf);
> +      int class = fpclassify(value);
> +
> +      if (class != inputs[i].class)

Use TEST_COMPARE.

> +	{
> +	  printf ("0x%s: got %s, expected %s\n", buf,
> +		  classes[fpclassify(value)],
> +		  classes[inputs[i].class]);
> +	  ret |= 1;
> +	}
> +      else
> +	printf ("0x%s: OK\n", buf);
> +    }
> +  return ret;
> +}
> +
> +static int
> +test_isinf (void)
> +{
> +  int ret = 0;
> +
> +  printf ("* isinfl tests:\n");
> +  for (int i = 0; i < sizeof (inputs)/sizeof (struct tests); i++)
> +    {
> +      long double value;
> +      char buf[22];
> +
> +      memcpy (&value, inputs[i].val, 10);
> +      unnormal_str(inputs[i].val, buf);
> +      int inf = isinf (value);
> +
> +      if ((inputs[i].class == FP_INFINITE && inf)
> +	  || (inputs[i].class != FP_INFINITE && !inf))
> +	printf ("0x%s: OK\n", buf);
> +      else
> +	{
> +	  printf ("0x%s: got %s, expected %s\n", buf,
> +		  inf ? "INFINITE" : "NOT INFINITE",
> +		  classes[inputs[i].class]);
> +	  ret |= 1;
> +	}
> +    }
> +
> +  return ret;
> +}
> +
> +static int
> +test_isnan (void)
> +{
> +  int ret = 0;
> +
> +  printf ("* isnanl tests:\n");
> +  for (int i = 0; i < sizeof (inputs)/sizeof (struct tests); i++)
> +    {
> +      long double value;
> +      char buf[22];
> +
> +      memcpy (&value, inputs[i].val, 10);
> +      unnormal_str(inputs[i].val, buf);
> +      int nan = isnan (value);
> +
> +      if ((inputs[i].class == FP_NAN && nan)
> +	  || (inputs[i].class != FP_NAN && !nan))
> +	printf ("0x%s: OK\n", buf);
> +      else
> +	{
> +	  printf ("0x%s: got %s, expected %s\n", buf,
> +		  nan ? "NAN" : "NOT NAN",
> +		  classes[inputs[i].class]);
> +	  ret |= 1;
> +	}
> +    }
> +  return ret;
> +}
> +
> +static int
> +test_issignaling (void)
> +{
> +  int ret = 0;
> +
> +  printf ("* issignaling tests:\n");
> +  for (int i = 0; i < sizeof (inputs)/sizeof (struct tests); i++)
> +    {
> +      long double value;
> +      char buf[22];
> +
> +      memcpy (&value, inputs[i].val, 10);
> +      unnormal_str(inputs[i].val, buf);
> +      int signaling = issignaling (value);
> +
> +      if ((inputs[i].class == FP_NAN && signaling)
> +	  || (inputs[i].class != FP_NAN && !signaling))
> +	printf ("0x%s: OK\n", buf);
> +      else
> +	{
> +	  printf ("0x%s: got %s, expected %s\n", buf,
> +		  signaling ? "SIGNALING" : "NOT SIGNALING",
> +		  classes[inputs[i].class]);
> +	  ret |= 1;
> +	}
> +    }
> +  return ret;
> +}
> +
> +int
> +do_test (void)
> +{
> +  int ret = 0;
> +
> +  initialize ();
> +
> +  ret |= test_fpclassify ();
> +  ret |= test_isinf ();
> +  ret |= test_isnan ();
> +  ret |= test_issignaling ();
> +
> +  return ret;
> +}
> +
> +#include <support/test-driver.c>
> 


More information about the Libc-alpha mailing list