New module 'isnanl-nolibm'.
[pspp] / lib / isnanl.c
1 /* Test for NaN that does not need libm.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 /* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
19
20 #include <config.h>
21
22 #include <float.h>
23 #include <string.h>
24
25 #define LDBL_EXP_MASK ((LDBL_MAX_EXP - LDBL_MIN_EXP) | 7)
26
27 #define NWORDS \
28   ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
29 typedef union { long double value; unsigned int word[NWORDS]; }
30         memory_long_double;
31
32 int
33 rpl_isnanl (long double x)
34 {
35 #if defined LDBL_EXPBIT0_WORD && defined LDBL_EXPBIT0_BIT
36   /* Be careful to not do any floating-point operation on x, such as x == x,
37      because x may be a signalling NaN.  */
38   static memory_long_double nan = { 0.0L / 0.0L };
39   static long double plus_inf = 1.0L / 0.0L;
40   static long double minus_inf = -1.0L / 0.0L;
41   memory_long_double m;
42
43   /* A NaN can be recognized through its exponent.  But exclude +Infinity and
44      -Infinity, which have the same exponent.  */
45   m.value = x;
46   if ((((m.word[LDBL_EXPBIT0_WORD] >> LDBL_EXPBIT0_BIT)
47         ^ (nan.word[LDBL_EXPBIT0_WORD] >> LDBL_EXPBIT0_BIT))
48        & LDBL_EXP_MASK)
49       == 0)
50     return (memcmp (&m.value, &plus_inf, sizeof (long double)) != 0
51             && memcmp (&m.value, &minus_inf, sizeof (long double)) != 0);
52   else
53     return 0;
54 #else
55   /* The configuration did not find sufficient information.  Give up about
56      the signaling NaNs, handle only the quiet NaNs.  */
57   if (x == x)
58     return 0;
59   else
60     return 1;
61 #endif
62 }