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