]>
Commit | Line | Data |
---|---|---|
46692946 PM |
1 | /* Test for correct rounding of results of strtod and related |
2 | functions. | |
bfff8b1b | 3 | Copyright (C) 2012-2017 Free Software Foundation, Inc. |
46692946 PM |
4 | This file is part of the GNU C Library. |
5 | ||
6 | The GNU C Library is free software; you can redistribute it and/or | |
7 | modify it under the terms of the GNU Lesser General Public | |
8 | License as published by the Free Software Foundation; either | |
9 | version 2.1 of the License, or (at your option) any later version. | |
10 | ||
11 | The GNU C Library is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public | |
17 | License along with the GNU C Library; if not, see | |
18 | <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | /* Defining _LIBC_TEST ensures long double math functions are | |
21 | declared in the headers. */ | |
22 | #define _LIBC_TEST 1 | |
23 | #include <fenv.h> | |
24 | #include <float.h> | |
25 | #include <math.h> | |
26 | #include <stdbool.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <math-tests.h> | |
31 | ||
32 | #include "tst-strtod.h" | |
33 | ||
34 | /* Non-standard macros expected to be externally defined: | |
35 | ||
36 | L_(str): Pastes the appropriate modifier to a string literal str. | |
37 | ||
38 | FNPFX: Expands to the correct prefix for the strtod equivalent | |
39 | of type CHAR. (e.g str or wcs). | |
40 | ||
41 | CHAR: Expands to the string type being tested (e.g wchar_t or char). | |
42 | ||
43 | STRM: Expands to a string literal suitable for printing CHAR* via | |
44 | printf (e.g "%s" or "%ls"). */ | |
45 | ||
46 | #define _CONCAT(a, b) a ## b | |
47 | #define CONCAT(a, b) _CONCAT (a, b) | |
48 | ||
49 | #define STRTO(x) CONCAT (CONCAT (FNPFX, to), x) | |
50 | ||
51 | #if LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 | |
52 | /* This is a stupid hack for IBM long double. This test ignores | |
53 | inexact values for long double due to the limitations of the | |
54 | format. This ensures rounding tests are ignored. */ | |
55 | # undef ROUNDING_TESTS_long_double | |
56 | # define ROUNDING_TESTS_long_double(x) 0 | |
57 | #endif | |
58 | ||
59 | /* Generator to create an FTYPE member variabled named FSUF | |
60 | used to populate struct member variables. */ | |
bf5eea32 | 61 | #define FTYPE_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
46692946 PM |
62 | FTYPE FSUF; |
63 | ||
64 | /* Likewise, but each member is of type bool. */ | |
bf5eea32 | 65 | #define BOOL_MEMBER(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
46692946 PM |
66 | bool FSUF; |
67 | ||
68 | #define STRUCT_FOREACH_FLOAT_FTYPE GEN_TEST_STRTOD_FOREACH (FTYPE_MEMBER) | |
69 | #define STRUCT_FOREACH_FLOAT_BOOL GEN_TEST_STRTOD_FOREACH (BOOL_MEMBER) | |
70 | ||
71 | /* Define the long double choose (CHOOSE_ld) macro | |
72 | to select the appropriate generated long double | |
73 | value from the generated test data. */ | |
74 | #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024 | |
75 | /* This is for the long double == double format. */ | |
76 | # define CHOOSE_ld(f,d,...) d | |
77 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16381 | |
78 | /* This is for the Intel extended float format. */ | |
79 | # define CHOOSE_ld(f,d,ld64i,...) ld64i | |
80 | #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && LDBL_MIN_EXP == -16382 | |
81 | /* This is for the Motorola extended float format. */ | |
82 | # define CHOOSE_ld(f,d,ld64i,ld64m,...) ld64m | |
83 | #elif LDBL_MANT_DIG == 106 && LDBL_MAX_EXP == 1024 | |
84 | /* This is for the IBM extended double format. */ | |
85 | # define CHOOSE_ld(f,d,ld64i,ld64m,ld106,...) ld106 | |
86 | #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 | |
87 | /* This is for the IEEE binary128 format. */ | |
88 | # define CHOOSE_ld(f,d,ld64i,ld64m,ld106,ld113,...) ld113 | |
89 | #else | |
90 | # error "unknown long double format" | |
91 | #endif | |
92 | ||
93 | /* Add type specific choosing macros below. */ | |
94 | #define CHOOSE_f(f,...) f | |
95 | #define CHOOSE_d(f,d,...) d | |
45f39d45 | 96 | #define CHOOSE_f128(f,d,ld64i,ld64m,ld106,ld113,...) ld113 |
46692946 PM |
97 | /* long double is special, and handled above. */ |
98 | ||
99 | /* Selector for expected result field of a given type. */ | |
bf5eea32 | 100 | #define _ENTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \ |
46692946 PM |
101 | CONCAT (CHOOSE_ ## FSUF (__VA_ARGS__), LSUF), |
102 | #define ENTRY(...) \ | |
103 | GEN_TEST_STRTOD_FOREACH (_ENTRY, __VA_ARGS__) | |
104 | ||
105 | /* Selector for boolean exact tag of expected results. */ | |
bf5eea32 | 106 | #define _XNTRY(FSUF, FTYPE, FTOSTR, LSUF, CSUF, ...) \ |
46692946 PM |
107 | CHOOSE_ ## FSUF (__VA_ARGS__), |
108 | #define XNTRY(...) \ | |
109 | GEN_TEST_STRTOD_FOREACH (_XNTRY, __VA_ARGS__) | |
110 | ||
111 | /* This is hacky way around the seemingly unavoidable macro | |
112 | expansion of the INFINITY or HUGE_VAL like macros in the | |
113 | above. It is assumed the compiler will implicitly convert | |
114 | the infinity correctly. */ | |
115 | #define INF INFINITY + 0.0 | |
116 | ||
117 | /* This macro is used in conjunction with the output from the | |
118 | gen-tst-strtod-round utility to select the appropriately | |
119 | rounded long double value for a given format. */ | |
120 | #define TEST(s, \ | |
121 | fx, fd, fn, fz, fu, \ | |
122 | dx, dd, dn, dz, du, \ | |
123 | ld64ix, ld64id, ld64in, ld64iz, ld64iu, \ | |
124 | ld64mx, ld64md, ld64mn, ld64mz, ld64mu, \ | |
125 | ld106x, ld106d, ld106n, ld106z, ld106u, \ | |
126 | ld113x, ld113d, ld113n, ld113z, ld113u) \ | |
127 | { \ | |
128 | L_ (s), \ | |
129 | { XNTRY (fx, dx, ld64ix, ld64mx, ld106x, ld113x) }, \ | |
130 | { \ | |
131 | { ENTRY (fn, dn, ld64in, ld64mn, ld106n, ld113n) }, \ | |
132 | { ENTRY (fd, dd, ld64id, ld64md, ld106d, ld113d) }, \ | |
133 | { ENTRY (fz, dz, ld64iz, ld64mz, ld106z, ld113z) }, \ | |
134 | { ENTRY (fu, du, ld64iu, ld64mu, ld106u, ld113u) } \ | |
135 | } \ | |
136 | } | |
137 | ||
138 | struct test_exactness | |
139 | { | |
140 | STRUCT_FOREACH_FLOAT_BOOL | |
141 | }; | |
142 | ||
143 | struct test_results | |
144 | { | |
145 | STRUCT_FOREACH_FLOAT_FTYPE | |
146 | }; | |
147 | ||
148 | struct test { | |
149 | const CHAR *s; | |
150 | struct test_exactness exact; | |
151 | struct test_results r[4]; | |
152 | }; | |
153 | ||
154 | /* Include the generated test data. */ | |
155 | #include "tst-strtod-round-data.h" | |
156 | ||
157 | #define STRX(x) #x | |
158 | #define STR(x) STRX (x) | |
159 | #define FNPFXS STR (FNPFX) | |
160 | ||
4725d33e JM |
161 | #ifndef FE_INEXACT |
162 | # define FE_INEXACT 0 | |
163 | #endif | |
164 | ||
bf5eea32 | 165 | #define GEN_ONE_TEST(FSUF, FTYPE, FTOSTR, LSUF, CSUF) \ |
46692946 | 166 | { \ |
4725d33e | 167 | feclearexcept (FE_INEXACT); \ |
46692946 PM |
168 | FTYPE f = STRTO (FSUF) (s, NULL); \ |
169 | if (f != expected->FSUF \ | |
170 | || (copysign ## CSUF) (1.0 ## LSUF, f) \ | |
171 | != (copysign ## CSUF) (1.0 ## LSUF, expected->FSUF)) \ | |
172 | { \ | |
bf5eea32 RS |
173 | char efstr[FSTRLENMAX]; \ |
174 | char fstr[FSTRLENMAX]; \ | |
175 | FTOSTR (efstr, FSTRLENMAX, "%a", expected->FSUF); \ | |
176 | FTOSTR (fstr, FSTRLENMAX, "%a", f); \ | |
177 | printf (FNPFXS "to" #FSUF " (" STRM ") returned %s not " \ | |
178 | "%s (%s)\n", s, fstr, efstr, mode_name); \ | |
46692946 PM |
179 | if (ROUNDING_TESTS (FTYPE, rnd_mode) || exact->FSUF) \ |
180 | result = 1; \ | |
181 | else \ | |
182 | printf ("ignoring this inexact result\n"); \ | |
183 | } \ | |
4725d33e JM |
184 | else if (FE_INEXACT != 0) \ |
185 | { \ | |
186 | bool inexact_raised = fetestexcept (FE_INEXACT) != 0; \ | |
187 | if (inexact_raised != !exact->FSUF) \ | |
188 | { \ | |
189 | printf (FNPFXS "to" #FSUF " (" STRM ") inexact %d " \ | |
190 | "not %d\n", s, inexact_raised, !exact->FSUF); \ | |
191 | if (EXCEPTION_TESTS (FTYPE)) \ | |
192 | result = 1; \ | |
193 | else \ | |
194 | printf ("ignoring this exception error\n"); \ | |
195 | } \ | |
196 | } \ | |
46692946 PM |
197 | } |
198 | ||
199 | static int | |
200 | test_in_one_mode (const CHAR *s, const struct test_results *expected, | |
201 | const struct test_exactness *exact, const char *mode_name, | |
202 | int rnd_mode) | |
203 | { | |
204 | int result = 0; | |
205 | GEN_TEST_STRTOD_FOREACH (GEN_ONE_TEST) | |
206 | return result; | |
207 | } | |
208 | ||
209 | static const struct fetestmodes | |
210 | { | |
211 | const char *mode_name; | |
212 | int rnd_mode; | |
213 | int rnd_i; /* Corresponding index into r array of struct test. */ | |
214 | } modes[] = { | |
215 | { "default rounding mode", FE_TONEAREST, 0 }, | |
216 | #ifdef FE_DOWNWARD | |
217 | { "FE_DOWNWARD", FE_DOWNWARD, 1 }, | |
218 | #endif | |
219 | #ifdef FE_TOWARDZERO | |
220 | { "FE_TOWARDZERO", FE_TOWARDZERO, 2 }, | |
221 | #endif | |
222 | #ifdef FE_UPWARD | |
223 | { "FE_UPWARD", FE_UPWARD, 3 }, | |
224 | #endif | |
225 | {} | |
226 | }; | |
227 | ||
228 | static int | |
229 | do_test (void) | |
230 | { | |
231 | int save_round_mode __attribute__ ((unused)) = fegetround (); | |
232 | int result = 0; | |
233 | for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); i++) | |
234 | { | |
235 | result |= test_in_one_mode (tests[i].s, &tests[i].r[modes[0].rnd_i], | |
236 | &tests[i].exact, modes[0].mode_name, | |
237 | modes[0].rnd_mode); | |
238 | for (const struct fetestmodes *m = &modes[1]; m->mode_name != NULL; m++) | |
239 | { | |
240 | if (!fesetround (m->rnd_mode)) | |
241 | { | |
242 | result |= test_in_one_mode (tests[i].s, &tests[i].r[m->rnd_i], | |
243 | &tests[i].exact, m->mode_name, | |
244 | m->rnd_mode); | |
245 | fesetround (save_round_mode); | |
246 | } | |
247 | } | |
248 | } | |
249 | return result; | |
250 | } | |
251 | ||
252 | #define TEST_FUNCTION do_test () | |
253 | #include "../test-skeleton.c" |