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

Message ID 20201215141339.2684384-6-siddhesh@sourceware.org
State Superseded
Headers
Series x86 pseudo-normal numbers |

Commit Message

Siddhesh Poyarekar Dec. 15, 2020, 2:13 p.m. UTC
  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
  

Comments

Adhemerval Zanella Netto Dec. 22, 2020, 9:48 p.m. UTC | #1
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>
>
  
Siddhesh Poyarekar Dec. 23, 2020, 1:58 a.m. UTC | #2
On 12/23/20 3:18 AM, Adhemerval Zanella via Libc-alpha wrote:
> 
> 
> 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

OK, it will need changes to the driver.

>> 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.

That's a good idea.

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

The *printf already has its own test.

> Also make the inputs a 'static' variable.

OK.

>> +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).

OK.

>> +  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.

OK.

Thanks,
Siddhesh
  
Siddhesh Poyarekar Dec. 23, 2020, 10:20 a.m. UTC | #3
On 12/23/20 7:28 AM, Siddhesh Poyarekar via Libc-alpha wrote:
> On 12/23/20 3:18 AM, Adhemerval Zanella via Libc-alpha wrote:
>>
>>
>> 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
> 
> OK, it will need changes to the driver.
> 

I took a look at this and it looks like it will need a significant 
amount of changes to the driver and test generation scripts, since these 
numbers are not generated by the CPU.  Would it be OK if I defer this to 
later if we ever feel the need to add tests beyond test-unnormal?

Siddhesh
  
Adhemerval Zanella Netto Dec. 23, 2020, 5:44 p.m. UTC | #4
On 23/12/2020 07:20, Siddhesh Poyarekar wrote:
> On 12/23/20 7:28 AM, Siddhesh Poyarekar via Libc-alpha wrote:
>> On 12/23/20 3:18 AM, Adhemerval Zanella via Libc-alpha wrote:
>>>
>>>
>>> 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
>>
>> OK, it will need changes to the driver.
>>
> 
> I took a look at this and it looks like it will need a significant amount of changes to the driver and test generation scripts, since these numbers are not generated by the CPU.  Would it be OK if I defer this to later if we ever feel the need to add tests beyond test-unnormal?
> 
> Siddhesh

I don't have a strong opinion, although below you can check it does not
really require too much tinkering to add a new tests for pseudo-normal zero. 
The idea is to add the generic constants pseudo_xxx, define a new list using 
a different struct, and issue ALL_RM_TESTS with RUN_TEST_LOOP_f_i_tg_u.

It does add a *lot* more coverage (errno, rounding modes, exceptions).

---

diff --git a/math/libm-test-driver.c b/math/libm-test-driver.c
index 11b541b2e7..ddfd16cf91 100644
--- a/math/libm-test-driver.c
+++ b/math/libm-test-driver.c
@@ -19,6 +19,7 @@
 #include "libm-test-support.h"
 
 #include <math-tests-arch.h>
+#include <nan-pseudo-number.h>
 
 /* Flags set by the including file.  */
 const int flag_test_errno = TEST_ERRNO;
@@ -122,6 +123,11 @@ const char qtype_str[] = TYPE_STR;
 /* For nexttoward tests.  */
 #define snan_value_ld	__builtin_nansl ("")
 
+#if HANDLE_PSEUDO_NUMBERS
+# include <math_ldbl.h>
+#define pseudo_inf { .parts = { 0x00000000, 0x00000000, 0x7fff }}
+#endif
+
 /* Structures for each kind of test.  */
 /* Used for both RUN_TEST_LOOP_f_f and RUN_TEST_LOOP_fp_f.  */
 struct test_f_f_data
@@ -316,6 +322,18 @@ struct test_f_i_data
     int exceptions;
   } rd, rn, rz, ru;
 };
+#if HANDLE_PSEUDO_NUMBERS
+struct test_f_i_data_u
+{
+  const char *arg_str;
+  ieee_long_double_shape_type arg;
+  struct
+  {
+    int expected;
+    int exceptions;
+  } rd, rn, rz, ru;
+};
+#endif
 /* Used for RUN_TEST_LOOP_ff_b, RUN_TEST_LOOP_fpfp_b and
    RUN_TEST_LOOP_ff_i_tg.  */
 struct test_ff_i_data
@@ -832,6 +850,13 @@ struct test_Ff_b1_data
 		       (ARRAY)[i].RM_##ROUNDING_MODE.expected,		\
 		       (ARRAY)[i].RM_##ROUNDING_MODE.exceptions);	\
   ROUND_RESTORE_ ## ROUNDING_MODE
+#define RUN_TEST_LOOP_f_i_tg_u(FUNC_NAME, ARRAY, ROUNDING_MODE)		\
+  IF_ROUND_INIT_ ## ROUNDING_MODE					\
+    for (size_t i = 0; i < sizeof (ARRAY) / sizeof (ARRAY)[0]; i++)	\
+      RUN_TEST_f_i_tg ((ARRAY)[i].arg_str, FUNC_NAME, (ARRAY)[i].arg.value,\
+		       (ARRAY)[i].RM_##ROUNDING_MODE.expected,		\
+		       (ARRAY)[i].RM_##ROUNDING_MODE.exceptions);	\
+  ROUND_RESTORE_ ## ROUNDING_MODE
 #define RUN_TEST_ff_b(ARG_STR, FUNC_NAME, ARG1, ARG2, EXPECTED,		\
 		      EXCEPTIONS)					\
   do									\
diff --git a/math/libm-test-fpclassify.inc b/math/libm-test-fpclassify.inc
index 96b557ecb4..0e2e00a09d 100644
--- a/math/libm-test-fpclassify.inc
+++ b/math/libm-test-fpclassify.inc
@@ -37,10 +37,20 @@ static const struct test_f_i_data fpclassify_test_data[] =
     TEST_f_i (fpclassify, -min_subnorm_value, FP_SUBNORMAL, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
   };
 
+#if HANDLE_PSEUDO_NUMBERS
+static const struct test_f_i_data_u fpclassify_test_data_u[] =
+  {
+    TEST_f_i (fpclassify, pseudo_inf, FP_NAN, NO_INEXACT_EXCEPTION|ERRNO_UNCHANGED),
+  };
+#endif
+
 static void
 fpclassify_test (void)
 {
   ALL_RM_TEST (fpclassify, 1, fpclassify_test_data, RUN_TEST_LOOP_f_i_tg, END);
+#if HANDLE_PSEUDO_NUMBERS
+  ALL_RM_TEST (fpclassify, 1, fpclassify_test_data_u, RUN_TEST_LOOP_f_i_tg_u, END);
+#endif
 }
 
 static void
  
Siddhesh Poyarekar Dec. 24, 2020, 12:48 a.m. UTC | #5
On 12/23/20 11:14 PM, Adhemerval Zanella wrote:
> I don't have a strong opinion, although below you can check it does not
> really require too much tinkering to add a new tests for pseudo-normal zero.
> The idea is to add the generic constants pseudo_xxx, define a new list using
> a different struct, and issue ALL_RM_TESTS with RUN_TEST_LOOP_f_i_tg_u.

Thanks for the example, I got caught up in the TEST_* macros and their 
conversion in python and took an unnecessary detour.  I'll give this 
another shot.

Thanks,
Siddhesh
  

Patch

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
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},
+};
+
+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");
+  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)
+	{
+	  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>