1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2020 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <fenv.h>
37 #include <math.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41
42 #ifdef __STDC_IEC_559__
43 #define HAVE_HW_DOUBLE
44 #endif
45
46 #ifdef HAVE_HW_DOUBLE
47 typedef double test_t;
48 #define test_sqrt(x) sqrt(x)
49 #define test_pow(x,y) pow(x,y)
50 #define test_remainder(x,y) remainder(x,y)
51 #define test_log(x) log(x)
52 #define tiny_val 1e-300
53 #define huge_val 1e300;
54 #else
55 typedef float test_t;
56 #define test_sqrt(x) sqrtf(x)
57 #define test_pow(x,y) powf(x,y)
58 #define test_remainder(x,y) remainderf(x,y)
59 #define test_log(x) logf(x)
60 #define tiny_val 1e-30f
61 #define huge_val 1e30f
62 #endif
63
64 volatile test_t one = 1.0;
65 volatile test_t zero = 0.0;
66 volatile test_t two = 2.0;
67 volatile test_t huge = huge_val;
68 volatile test_t tiny = tiny_val;
69 volatile test_t inf = INFINITY;
70
71 #define lowbit(x) ((x) & -(x))
72 #define ispoweroftwo(x) (((x) & ((x) - 1)) == 0)
73
74 #ifdef FE_DIVBYZERO
75 #define my_divbyzero FE_DIVBYZERO
76 #else
77 #define my_divbyzero 0
78 #endif
79
80 #ifdef FE_OVERFLOW
81 #define my_overflow FE_OVERFLOW
82 #else
83 #define my_overflow 0
84 #endif
85
86 #ifdef FE_UNDERFLOW
87 #define my_underflow FE_UNDERFLOW
88 #else
89 #define my_underflow 0
90 #endif
91
92 #ifdef FE_INEXACT
93 #define my_inexact FE_INEXACT
94 #else
95 #define my_inexact 0
96 #endif
97
98 #ifdef FE_INVALID
99 #define my_invalid FE_INVALID
100 #else
101 #define my_invalid 0
102 #endif
103
104 static const char *
e_to_str(int e)105 e_to_str(int e)
106 {
107 if (e == 0)
108 return "NONE";
109
110 #ifdef FE_DIVBYZERO
111 if (e == FE_DIVBYZERO)
112 return "FE_DIVBYZERO";
113 #endif
114 #ifdef FE_OVERFLOW
115 if (e == FE_OVERFLOW)
116 return "FE_OVERFLOW";
117 #endif
118 #ifdef FE_UNDERFLOW
119 if (e == FE_UNDERFLOW)
120 return "FE_UNDERFLOW";
121 #endif
122 #ifdef FE_INEXACT
123 if (e == FE_INEXACT)
124 return "FE_INEXACT";
125 #endif
126 #ifdef FE_INVALID
127 if (e == FE_INVALID)
128 return "FE_INVALID";
129 #endif
130 #if defined(FE_OVERFLOW) && defined(FE_INEXACT)
131 if (e == (FE_OVERFLOW|FE_INEXACT))
132 return "FE_OVERFLOW|FE_INEXACT";
133 #endif
134 #if defined(FE_UNDERFLOW) && defined(FE_INEXACT)
135 if (e == (FE_UNDERFLOW|FE_INEXACT))
136 return "FE_UNDERFLOW|FE_INEXACT";
137 #endif
138 static char buf[50];
139 sprintf(buf, "Invalid 0x%x", e);
140 return buf;
141 }
142
143 #define s(e) #e
144
145 static int
report(char * expr,test_t v,int e,int exception,int oexception)146 report(char *expr, test_t v, int e, int exception, int oexception)
147 {
148 /* powerpc has additional details in the exception flags */
149 e &= (my_inexact | my_divbyzero | my_underflow | my_overflow | my_invalid);
150 printf("%-20.20s: ", expr);
151 printf("%8g ", (double) v);
152 printf("(e expect %s", e_to_str(exception));
153 if (oexception)
154 printf(" or %s", e_to_str(oexception));
155 printf(" got %s\n", e_to_str(e));
156 if (e == (exception) ||
157 (oexception && e == (oexception)))
158 {
159 return 0;
160 }
161 printf("\tgot %s expecting %s", e_to_str(e), e_to_str(exception));
162 if (oexception)
163 printf(" or %s", e_to_str(oexception));
164 printf("\n");
165 return 1;
166 }
167
168 #define TEST_CASE2(expr, exception, oexception) do { \
169 int e; \
170 volatile test_t v; \
171 feclearexcept(FE_ALL_EXCEPT); \
172 v = expr; \
173 e = fetestexcept(FE_ALL_EXCEPT); \
174 result += report(s(expr), v, e, exception, oexception); \
175 } while(0)
176
177 #define TEST_CASE(expr, exception) do { \
178 if ((exception & (my_overflow|my_underflow)) && my_inexact != 0) \
179 TEST_CASE2(expr, exception, exception | my_inexact); \
180 else \
181 TEST_CASE2(expr, exception, 0); \
182 } while(0)
183
main(void)184 int main(void)
185 {
186 int result = 0;
187
188 (void) report;
189 (void) e_to_str;
190 if (math_errhandling & MATH_ERREXCEPT) {
191 #if FE_DIVBYZERO
192 TEST_CASE(one / zero, FE_DIVBYZERO);
193 TEST_CASE(test_log(zero), FE_DIVBYZERO);
194 #endif
195 #if FE_OVERFLOW
196 TEST_CASE(huge * huge, FE_OVERFLOW);
197 TEST_CASE(test_pow(two, huge), FE_OVERFLOW);
198 #endif
199 #if FE_UNDERFLOW
200 TEST_CASE(tiny * tiny, FE_UNDERFLOW);
201 TEST_CASE(test_pow(two, -huge), FE_UNDERFLOW);
202 #endif
203 #if FE_INVALID
204 TEST_CASE(zero * inf, FE_INVALID);
205 TEST_CASE(inf * zero, FE_INVALID);
206 TEST_CASE(inf + -inf, FE_INVALID);
207 TEST_CASE(inf - inf, FE_INVALID);
208 TEST_CASE(zero / zero, FE_INVALID);
209 TEST_CASE(inf / inf, FE_INVALID);
210 TEST_CASE(test_remainder(one, zero), FE_INVALID);
211 TEST_CASE(test_remainder(inf, two), FE_INVALID);
212 TEST_CASE(test_sqrt(-two), FE_INVALID);
213 #endif
214 }
215 return result;
216 }
217