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 #define _GNU_SOURCE
37 #include <fenv.h>
38 #include <math.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42
43 #ifdef __STDC_IEC_559__
44 #define HAVE_HW_DOUBLE
45 #endif
46
47 #ifdef HAVE_HW_DOUBLE
48 typedef double test_t;
49 #define test_sqrt(x) sqrt(x)
50 #define test_pow(x,y) pow(x,y)
51 #define test_remainder(x,y) remainder(x,y)
52 #define test_log(x) log(x)
53 #define tiny_val 1e-300
54 #define huge_val 1e300;
55 #else
56 typedef float test_t;
57 #define test_sqrt(x) sqrtf(x)
58 #define test_pow(x,y) powf(x,y)
59 #define test_remainder(x,y) remainderf(x,y)
60 #define test_log(x) logf(x)
61 #define tiny_val 1e-30f
62 #define huge_val 1e30f
63 #endif
64
65 volatile test_t one = 1.0;
66 volatile test_t zero = 0.0;
67 volatile test_t two = 2.0;
68 volatile test_t huge = huge_val;
69 volatile test_t tiny = tiny_val;
70 volatile test_t inf = INFINITY;
71
72 #define lowbit(x) ((x) & -(x))
73 #define ispoweroftwo(x) (((x) & ((x) - 1)) == 0)
74
75 #ifdef FE_DIVBYZERO
76 #define my_divbyzero FE_DIVBYZERO
77 #else
78 #define my_divbyzero 0
79 #endif
80
81 #ifdef FE_OVERFLOW
82 #define my_overflow FE_OVERFLOW
83 #else
84 #define my_overflow 0
85 #endif
86
87 #ifdef FE_UNDERFLOW
88 #define my_underflow FE_UNDERFLOW
89 #else
90 #define my_underflow 0
91 #endif
92
93 #ifdef FE_INEXACT
94 #define my_inexact FE_INEXACT
95 #else
96 #define my_inexact 0
97 #endif
98
99 #ifdef FE_INVALID
100 #define my_invalid FE_INVALID
101 #else
102 #define my_invalid 0
103 #endif
104
105 static const char *
e_to_str(int e)106 e_to_str(int e)
107 {
108 if (e == 0)
109 return "NONE";
110
111 #ifdef FE_DIVBYZERO
112 if (e == FE_DIVBYZERO)
113 return "FE_DIVBYZERO";
114 #endif
115 #ifdef FE_OVERFLOW
116 if (e == FE_OVERFLOW)
117 return "FE_OVERFLOW";
118 #endif
119 #ifdef FE_UNDERFLOW
120 if (e == FE_UNDERFLOW)
121 return "FE_UNDERFLOW";
122 #endif
123 #ifdef FE_INEXACT
124 if (e == FE_INEXACT)
125 return "FE_INEXACT";
126 #endif
127 #ifdef FE_INVALID
128 if (e == FE_INVALID)
129 return "FE_INVALID";
130 #endif
131 #if defined(FE_OVERFLOW) && defined(FE_INEXACT)
132 if (e == (FE_OVERFLOW|FE_INEXACT))
133 return "FE_OVERFLOW|FE_INEXACT";
134 #endif
135 #if defined(FE_UNDERFLOW) && defined(FE_INEXACT)
136 if (e == (FE_UNDERFLOW|FE_INEXACT))
137 return "FE_UNDERFLOW|FE_INEXACT";
138 #endif
139 static char buf[50];
140 sprintf(buf, "Invalid 0x%x", e);
141 return buf;
142 }
143
144 #define s(e) #e
145
146 static int
report(char * expr,test_t v,int e,int exception,int oexception)147 report(char *expr, test_t v, int e, int exception, int oexception)
148 {
149 /* powerpc has additional details in the exception flags */
150 e &= (my_inexact | my_divbyzero | my_underflow | my_overflow | my_invalid);
151 printf("%-20.20s: ", expr);
152 printf("%8g ", (double) v);
153 printf("(e expect %s", e_to_str(exception));
154 if (oexception)
155 printf(" or %s", e_to_str(oexception));
156 printf(" got %s\n", e_to_str(e));
157 if (e == (exception) ||
158 (oexception && e == (oexception)))
159 {
160 return 0;
161 }
162 printf("\tgot %s expecting %s", e_to_str(e), e_to_str(exception));
163 if (oexception)
164 printf(" or %s", e_to_str(oexception));
165 printf("\n");
166 return 1;
167 }
168
169 #define TEST_CASE2(expr, exception, oexception) do { \
170 int e; \
171 volatile test_t v; \
172 feclearexcept(FE_ALL_EXCEPT); \
173 v = expr; \
174 e = fetestexcept(FE_ALL_EXCEPT); \
175 result += report(s(expr), v, e, exception, oexception); \
176 } while(0)
177
178 #define TEST_CASE(expr, exception) do { \
179 if ((exception & (my_overflow|my_underflow)) && my_inexact != 0) \
180 TEST_CASE2(expr, exception, exception | my_inexact); \
181 else \
182 TEST_CASE2(expr, exception, 0); \
183 } while(0)
184
185 static const struct {
186 const char *name;
187 int value;
188 } excepts[] = {
189 { .name = "None", .value = 0 },
190 #if FE_DIVBYZERO
191 { .name = "Divide by zero", .value = FE_DIVBYZERO },
192 #endif
193 #if FE_OVERFLOW
194 { .name = "Overflow", .value = FE_OVERFLOW },
195 #endif
196 #if FE_UNDERFLOW
197 { .name = "Underflow", .value = FE_UNDERFLOW },
198 #endif
199 #if FE_INVALID
200 { .name = "Invalid", .value = FE_INVALID },
201 #endif
202 };
203
204 #define NUM_EXCEPTS (sizeof(excepts)/sizeof(excepts[0]))
205
main(void)206 int main(void)
207 {
208 int result = 0;
209 int ret;
210 unsigned i;
211
212 (void) report;
213 (void) e_to_str;
214 if (math_errhandling & MATH_ERREXCEPT) {
215 #if FE_DIVBYZERO
216 TEST_CASE(one / zero, FE_DIVBYZERO);
217 TEST_CASE(test_log(zero), FE_DIVBYZERO);
218 #endif
219 #if FE_OVERFLOW
220 TEST_CASE(huge * huge, FE_OVERFLOW);
221 TEST_CASE(test_pow(two, huge), FE_OVERFLOW);
222 #endif
223 #if FE_UNDERFLOW
224 TEST_CASE(tiny * tiny, FE_UNDERFLOW);
225 TEST_CASE(test_pow(two, -huge), FE_UNDERFLOW);
226 #endif
227 #if FE_INVALID
228 TEST_CASE(zero * inf, FE_INVALID);
229 TEST_CASE(inf * zero, FE_INVALID);
230 TEST_CASE(inf + -inf, FE_INVALID);
231 TEST_CASE(inf - inf, FE_INVALID);
232 TEST_CASE(zero / zero, FE_INVALID);
233 TEST_CASE(inf / inf, FE_INVALID);
234 TEST_CASE(test_remainder(one, zero), FE_INVALID);
235 TEST_CASE(test_remainder(inf, two), FE_INVALID);
236 TEST_CASE(test_sqrt(-two), FE_INVALID);
237 #endif
238 }
239
240 for (i = 0; i < NUM_EXCEPTS; i++) {
241 ret = feenableexcept(excepts[i].value);
242 if (ret == 0) {
243 ret = fedisableexcept(excepts[i].value);
244 if (ret != excepts[i].value) {
245 printf("enable %s worked, disabled returned %d\n", excepts[i].name, ret);
246 result = 1;
247 }
248 } else {
249 if (excepts[i].value == 0) {
250 printf("enable %s returned %d", excepts[i].name, ret);
251 result = 1;
252 }
253 }
254 }
255 return result;
256 }
257