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