1 /* $OpenBSD: test-utils.h,v 1.3 2021/10/22 18:00:23 mbuhl Exp $ */
2 /*-
3 * Copyright (c) 2005-2013 David Schultz <das@FreeBSD.org>
4 * All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/lib/msun/tests/test-utils.h 314650 2017-03-04 10:07:46Z ngie $
28 */
29
30 #ifndef _TEST_UTILS_H_
31 #define _TEST_UTILS_H_
32
33 #include <complex.h>
34 #include <fenv.h>
35 #include <float.h>
36
37 #include "atf-c.h"
38
39 /*
40 * Implementations are permitted to define additional exception flags
41 * not specified in the standard, so it is not necessarily true that
42 * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
43 */
44 #define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
45 FE_OVERFLOW | FE_UNDERFLOW)
46 #define OPT_INVALID (ALL_STD_EXCEPT & ~FE_INVALID)
47 #define OPT_INEXACT (ALL_STD_EXCEPT & ~FE_INEXACT)
48 #define FLT_ULP() ldexpl(1.0, 1 - FLT_MANT_DIG)
49 #define DBL_ULP() ldexpl(1.0, 1 - DBL_MANT_DIG)
50 #define LDBL_ULP() ldexpl(1.0, 1 - LDBL_MANT_DIG)
51
52 /*
53 * Flags that control the behavior of various fpequal* functions.
54 * XXX This is messy due to merging various notions of "close enough"
55 * that are best suited for different functions.
56 *
57 * CS_REAL
58 * CS_IMAG
59 * CS_BOTH
60 * (cfpequal_cs, fpequal_tol, cfpequal_tol) Whether to check the sign of
61 * the real part of the result, the imaginary part, or both.
62 *
63 * FPE_ABS_ZERO
64 * (fpequal_tol, cfpequal_tol) If set, treats the tolerance as an absolute
65 * tolerance when the expected value is 0. This is useful when there is
66 * round-off error in the input, e.g., cos(Pi/2) ~= 0.
67 */
68 #define CS_REAL 0x01
69 #define CS_IMAG 0x02
70 #define CS_BOTH (CS_REAL | CS_IMAG)
71 #define FPE_ABS_ZERO 0x04
72
73 #ifdef DEBUG
74 #define debug(...) printf(__VA_ARGS__)
75 #else
76 #define debug(...) (void)0
77 #endif
78
79 /*
80 * XXX The ancient version of gcc in the base system doesn't support CMPLXL,
81 * but we can fake it most of the time.
82 */
83 #ifndef CMPLXL
84 static inline long double complex
CMPLXL(long double x,long double y)85 CMPLXL(long double x, long double y)
86 {
87 long double complex z;
88
89 __real__ z = x;
90 __imag__ z = y;
91 return (z);
92 }
93 #endif
94
95 /*
96 * The compiler-rt fp128 builtins do not update FP exceptions.
97 * See https://llvm.org/PR34126
98 */
99
100 static int cfpequal(long double complex, long double complex) __used;
101
102 /*
103 * Determine whether x and y are equal, with two special rules:
104 * +0.0 != -0.0
105 * NaN == NaN
106 * If checksign is false, we compare the absolute values instead.
107 */
108 static inline int
fpequal_cs(long double x,long double y,bool checksign)109 fpequal_cs(long double x, long double y, bool checksign)
110 {
111 if (isnan(x) && isnan(y))
112 return (1);
113 if (checksign)
114 return (x == y && !signbit(x) == !signbit(y));
115 else
116 return (fabsl(x) == fabsl(y));
117 }
118
119 static inline int
fpequal_tol(long double x,long double y,long double tol,unsigned int flags)120 fpequal_tol(long double x, long double y, long double tol,
121 unsigned int flags)
122 {
123 fenv_t env;
124 int ret;
125
126 if (isnan(x) && isnan(y))
127 return (1);
128 if (!signbit(x) != !signbit(y) && (flags & CS_BOTH))
129 return (0);
130 if (x == y)
131 return (1);
132 if (tol == 0)
133 return (0);
134
135 /* Hard case: need to check the tolerance. */
136 feholdexcept(&env);
137 /*
138 * For our purposes here, if y=0, we interpret tol as an absolute
139 * tolerance. This is to account for roundoff in the input, e.g.,
140 * cos(Pi/2) ~= 0.
141 */
142 if ((flags & FPE_ABS_ZERO) && y == 0.0)
143 ret = fabsl(x - y) <= fabsl(tol);
144 else
145 ret = fabsl(x - y) <= fabsl(y * tol);
146 fesetenv(&env);
147 return (ret);
148 }
149
150 #define CHECK_FPEQUAL(x, y) CHECK_FPEQUAL_CS(x, y, true)
151
152 #define CHECK_FPEQUAL_CS(x, y, checksign) do { \
153 long double _x = x; \
154 long double _y = y; \
155 ATF_CHECK_MSG(fpequal_cs(_x, _y, checksign), \
156 "%s (%.25Lg) ~= %s (%.25Lg)", #x, _x, #y, _y); \
157 } while (0)
158
159 #define CHECK_FPEQUAL_TOL(x, y, tol, flags) do { \
160 long double _x = x; \
161 long double _y = y; \
162 bool eq = fpequal_tol(_x, _y, tol, flags); \
163 long double _diff = eq ? 0.0L : fabsl(_x - _y); \
164 ATF_CHECK_MSG(eq, "%s (%.25Lg) ~= %s (%.25Lg), diff=%Lg, maxdiff=%Lg,", \
165 #x, _x, #y, _y, _diff, fabsl(_y * tol)); \
166 } while (0)
167
168 static inline int
cfpequal(long double complex d1,long double complex d2)169 cfpequal(long double complex d1, long double complex d2)
170 {
171
172 return (fpequal_cs(creall(d1), creall(d2), true) &&
173 fpequal_cs(cimagl(d1), cimagl(d2), true));
174 }
175
176 #ifdef __OpenBSD__
177 static int
cfpequal_cs(x,y,checksign)178 cfpequal_cs(x, y, checksign)
179 {
180 long double _x = x;
181 long double _y = y;
182 return
183 fpequal_cs(creal(_x), creal(_y), (checksign & CS_REAL) != 0) &&
184 fpequal_cs(cimag(_x), cimag(_y), (checksign & CS_IMAG) != 0);
185 }
186 #endif
187
188 #define CHECK_CFPEQUAL_CS(x, y, checksign) do { \
189 long double _x = x; \
190 long double _y = y; \
191 bool equal_cs = \
192 fpequal_cs(creal(_x), creal(_y), (checksign & CS_REAL) != 0) && \
193 fpequal_cs(cimag(_x), cimag(_y), (checksign & CS_IMAG) != 0); \
194 ATF_CHECK_MSG(equal_cs, "%s (%Lg + %Lg I) ~= %s (%Lg + %Lg I)", \
195 #x, creall(_x), cimagl(_x), #y, creall(_y), cimagl(_y)); \
196 } while (0)
197
198 #define CHECK_CFPEQUAL_TOL(x, y, tol, flags) do { \
199 long double _x = x; \
200 long double _y = y; \
201 bool equal_tol = (fpequal_tol(creal(_x), creal(_y), tol, flags) && \
202 fpequal_tol(cimag(_x), cimag(_y), tol, flags)); \
203 ATF_CHECK_MSG(equal_tol, "%s (%Lg + %Lg I) ~= %s (%Lg + %Lg I)", \
204 #x, creall(_x), cimagl(_x), #y, creall(_y), cimagl(_y)); \
205 } while (0)
206
207 #define CHECK_FP_EXCEPTIONS(excepts, exceptmask) \
208 ATF_CHECK_EQ_MSG((excepts), fetestexcept(exceptmask), \
209 "unexpected exception flags: got %#x not %#x", \
210 fetestexcept(exceptmask), (excepts))
211 #define CHECK_FP_EXCEPTIONS_MSG(excepts, exceptmask, fmt, ...) \
212 ATF_CHECK_EQ_MSG((excepts), fetestexcept(exceptmask), \
213 "unexpected exception flags: got %#x not %#x " fmt, \
214 fetestexcept(exceptmask), (excepts), __VA_ARGS__)
215
216 #endif /* _TEST_UTILS_H_ */
217