1 | /* Test for correct rounding of printf floating-point output. |
2 | Copyright (C) 2012-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <array_length.h> |
20 | #include <fenv.h> |
21 | #include <stdio.h> |
22 | #include <string.h> |
23 | |
24 | struct dec_test { |
25 | double d; |
26 | const char *fmt; |
27 | const char *rd, *rn, *rz, *ru; |
28 | }; |
29 | |
30 | static const struct dec_test dec_tests[] = { |
31 | { 1.5, "%.0f" , "1" , "2" , "1" , "2" }, |
32 | { -1.5, "%.0f" , "-2" , "-2" , "-1" , "-1" }, |
33 | { 2.5, "%.0f" , "2" , "2" , "2" , "3" }, |
34 | { -2.5, "%.0f" , "-3" , "-2" , "-2" , "-2" }, |
35 | { 1.4999, "%.0f" , "1" , "1" , "1" , "2" }, |
36 | { -1.4999, "%.0f" , "-2" , "-1" , "-1" , "-1" }, |
37 | { 1.5001, "%.0f" , "1" , "2" , "1" , "2" }, |
38 | { -1.5001, "%.0f" , "-2" , "-2" , "-1" , "-1" }, |
39 | { 2.4999, "%.0f" , "2" , "2" , "2" , "3" }, |
40 | { -2.4999, "%.0f" , "-3" , "-2" , "-2" , "-2" }, |
41 | { 2.5001, "%.0f" , "2" , "3" , "2" , "3" }, |
42 | { -2.5001, "%.0f" , "-3" , "-3" , "-2" , "-2" }, |
43 | { 1.0 / 3.0, "%f" , "0.333333" , "0.333333" , "0.333333" , "0.333334" }, |
44 | { -1.0 / 3.0, "%f" , "-0.333334" , "-0.333333" , "-0.333333" , "-0.333333" }, |
45 | { 0.2500001, "%.2e" , "2.50e-01" , "2.50e-01" , "2.50e-01" , "2.51e-01" }, |
46 | { -0.2500001, "%.2e" , "-2.51e-01" , "-2.50e-01" , "-2.50e-01" , "-2.50e-01" }, |
47 | { 1000001.0, "%.1e" , "1.0e+06" , "1.0e+06" , "1.0e+06" , "1.1e+06" }, |
48 | { -1000001.0, "%.1e" , "-1.1e+06" , "-1.0e+06" , "-1.0e+06" , "-1.0e+06" }, |
49 | }; |
50 | |
51 | static int |
52 | test_dec_in_one_mode (double d, const char *fmt, const char *expected, |
53 | const char *mode_name) |
54 | { |
55 | char buf[100]; |
56 | int ret = snprintf (s: buf, maxlen: sizeof buf, format: fmt, d); |
57 | if (ret <= 0 || ret >= (int) sizeof buf) |
58 | { |
59 | printf (format: "snprintf for %a returned %d\n" , d, ret); |
60 | return 1; |
61 | } |
62 | if (strcmp (buf, expected) == 0) |
63 | return 0; |
64 | else |
65 | { |
66 | printf (format: "snprintf (\"%s\", %a) returned \"%s\" not \"%s\" (%s)\n" , |
67 | fmt, d, buf, expected, mode_name); |
68 | return 1; |
69 | } |
70 | } |
71 | |
72 | struct hex_test |
73 | { |
74 | double d; |
75 | const char *fmt; |
76 | const char *rd[4], *rn[4], *rz[4], *ru[4]; |
77 | }; |
78 | |
79 | static const struct hex_test hex_tests[] = |
80 | { |
81 | { |
82 | 0x1.fffffp+4, "%.1a" , |
83 | { "0x1.fp+4" , "0x3.fp+3" , "0x7.fp+2" , "0xf.fp+1" }, |
84 | { "0x2.0p+4" , "0x4.0p+3" , "0x8.0p+2" , "0x1.0p+5" }, |
85 | { "0x1.fp+4" , "0x3.fp+3" , "0x7.fp+2" , "0xf.fp+1" }, |
86 | { "0x2.0p+4" , "0x4.0p+3" , "0x8.0p+2" , "0x1.0p+5" } |
87 | }, |
88 | { |
89 | -0x1.fffffp+4, "%.1a" , |
90 | { "-0x2.0p+4" , "-0x4.0p+3" , "-0x8.0p+2" , "-0x1.0p+5" }, |
91 | { "-0x2.0p+4" , "-0x4.0p+3" , "-0x8.0p+2" , "-0x1.0p+5" }, |
92 | { "-0x1.fp+4" , "-0x3.fp+3" , "-0x7.fp+2" , "-0xf.fp+1" }, |
93 | { "-0x1.fp+4" , "-0x3.fp+3" , "-0x7.fp+2" , "-0xf.fp+1" } |
94 | }, |
95 | { |
96 | 0x1.88p+4, "%.1a" , |
97 | { "0x1.8p+4" , "0x3.1p+3" , "0x6.2p+2" , "0xc.4p+1" }, |
98 | { "0x1.8p+4" , "0x3.1p+3" , "0x6.2p+2" , "0xc.4p+1" }, |
99 | { "0x1.8p+4" , "0x3.1p+3" , "0x6.2p+2" , "0xc.4p+1" }, |
100 | { "0x1.9p+4" , "0x3.1p+3" , "0x6.2p+2" , "0xc.4p+1" } |
101 | }, |
102 | { |
103 | -0x1.88p+4, "%.1a" , |
104 | { "-0x1.9p+4" , "-0x3.1p+3" , "-0x6.2p+2" , "-0xc.4p+1" }, |
105 | { "-0x1.8p+4" , "-0x3.1p+3" , "-0x6.2p+2" , "-0xc.4p+1" }, |
106 | { "-0x1.8p+4" , "-0x3.1p+3" , "-0x6.2p+2" , "-0xc.4p+1" }, |
107 | { "-0x1.8p+4" , "-0x3.1p+3" , "-0x6.2p+2" , "-0xc.4p+1" } |
108 | }, |
109 | { |
110 | 0x1.78p+4, "%.1a" , |
111 | { "0x1.7p+4" , "0x2.fp+3" , "0x5.ep+2" , "0xb.cp+1" }, |
112 | { "0x1.8p+4" , "0x2.fp+3" , "0x5.ep+2" , "0xb.cp+1" }, |
113 | { "0x1.7p+4" , "0x2.fp+3" , "0x5.ep+2" , "0xb.cp+1" }, |
114 | { "0x1.8p+4" , "0x2.fp+3" , "0x5.ep+2" , "0xb.cp+1" } |
115 | }, |
116 | { |
117 | -0x1.78p+4, "%.1a" , |
118 | { "-0x1.8p+4" , "-0x2.fp+3" , "-0x5.ep+2" , "-0xb.cp+1" }, |
119 | { "-0x1.8p+4" , "-0x2.fp+3" , "-0x5.ep+2" , "-0xb.cp+1" }, |
120 | { "-0x1.7p+4" , "-0x2.fp+3" , "-0x5.ep+2" , "-0xb.cp+1" }, |
121 | { "-0x1.7p+4" , "-0x2.fp+3" , "-0x5.ep+2" , "-0xb.cp+1" } |
122 | }, |
123 | { |
124 | 64.0 / 3.0, "%.1a" , |
125 | { "0x1.5p+4" , "0x2.ap+3" , "0x5.5p+2" , "0xa.ap+1" }, |
126 | { "0x1.5p+4" , "0x2.bp+3" , "0x5.5p+2" , "0xa.bp+1" }, |
127 | { "0x1.5p+4" , "0x2.ap+3" , "0x5.5p+2" , "0xa.ap+1" }, |
128 | { "0x1.6p+4" , "0x2.bp+3" , "0x5.6p+2" , "0xa.bp+1" } |
129 | }, |
130 | { |
131 | -64.0 / 3.0, "%.1a" , |
132 | { "-0x1.6p+4" , "-0x2.bp+3" , "-0x5.6p+2" , "-0xa.bp+1" }, |
133 | { "-0x1.5p+4" , "-0x2.bp+3" , "-0x5.5p+2" , "-0xa.bp+1" }, |
134 | { "-0x1.5p+4" , "-0x2.ap+3" , "-0x5.5p+2" , "-0xa.ap+1" }, |
135 | { "-0x1.5p+4" , "-0x2.ap+3" , "-0x5.5p+2" , "-0xa.ap+1" } |
136 | }, |
137 | }; |
138 | |
139 | static int |
140 | test_hex_in_one_mode (double d, const char *fmt, const char *const expected[4], |
141 | const char *mode_name) |
142 | { |
143 | char buf[100]; |
144 | int ret = snprintf (s: buf, maxlen: sizeof buf, format: fmt, d); |
145 | if (ret <= 0 || ret >= (int) sizeof buf) |
146 | { |
147 | printf (format: "snprintf for %a returned %d\n" , d, ret); |
148 | return 1; |
149 | } |
150 | if (strcmp (buf, expected[0]) == 0 |
151 | || strcmp (buf, expected[1]) == 0 |
152 | || strcmp (buf, expected[2]) == 0 |
153 | || strcmp (buf, expected[3]) == 0) |
154 | return 0; |
155 | else |
156 | { |
157 | printf (format: "snprintf (\"%s\", %a) returned \"%s\" not " |
158 | "\"%s\" or \"%s\" or \"%s\" or \"%s\" (%s)\n" , |
159 | fmt, d, buf, expected[0], expected[1], expected[2], expected[3], |
160 | mode_name); |
161 | return 1; |
162 | } |
163 | } |
164 | |
165 | static int |
166 | do_test (void) |
167 | { |
168 | int save_round_mode __attribute__ ((unused)) = fegetround (); |
169 | int result = 0; |
170 | |
171 | for (size_t i = 0; i < array_length (dec_tests); i++) |
172 | { |
173 | result |= test_dec_in_one_mode (d: dec_tests[i].d, fmt: dec_tests[i].fmt, |
174 | expected: dec_tests[i].rn, mode_name: "default rounding mode" ); |
175 | #ifdef FE_DOWNWARD |
176 | if (!fesetround (FE_DOWNWARD)) |
177 | { |
178 | result |= test_dec_in_one_mode (d: dec_tests[i].d, fmt: dec_tests[i].fmt, |
179 | expected: dec_tests[i].rd, mode_name: "FE_DOWNWARD" ); |
180 | fesetround (rounding_direction: save_round_mode); |
181 | } |
182 | #endif |
183 | #ifdef FE_TOWARDZERO |
184 | if (!fesetround (FE_TOWARDZERO)) |
185 | { |
186 | result |= test_dec_in_one_mode (d: dec_tests[i].d, fmt: dec_tests[i].fmt, |
187 | expected: dec_tests[i].rz, mode_name: "FE_TOWARDZERO" ); |
188 | fesetround (rounding_direction: save_round_mode); |
189 | } |
190 | #endif |
191 | #ifdef FE_UPWARD |
192 | if (!fesetround (FE_UPWARD)) |
193 | { |
194 | result |= test_dec_in_one_mode (d: dec_tests[i].d, fmt: dec_tests[i].fmt, |
195 | expected: dec_tests[i].ru, mode_name: "FE_UPWARD" ); |
196 | fesetround (rounding_direction: save_round_mode); |
197 | } |
198 | #endif |
199 | } |
200 | |
201 | for (size_t i = 0; i < array_length (hex_tests); i++) |
202 | { |
203 | result |= test_hex_in_one_mode (d: hex_tests[i].d, fmt: hex_tests[i].fmt, |
204 | expected: hex_tests[i].rn, mode_name: "default rounding mode" ); |
205 | #ifdef FE_DOWNWARD |
206 | if (!fesetround (FE_DOWNWARD)) |
207 | { |
208 | result |= test_hex_in_one_mode (d: hex_tests[i].d, fmt: hex_tests[i].fmt, |
209 | expected: hex_tests[i].rd, mode_name: "FE_DOWNWARD" ); |
210 | fesetround (rounding_direction: save_round_mode); |
211 | } |
212 | #endif |
213 | #ifdef FE_TOWARDZERO |
214 | if (!fesetround (FE_TOWARDZERO)) |
215 | { |
216 | result |= test_hex_in_one_mode (d: hex_tests[i].d, fmt: hex_tests[i].fmt, |
217 | expected: hex_tests[i].rz, mode_name: "FE_TOWARDZERO" ); |
218 | fesetround (rounding_direction: save_round_mode); |
219 | } |
220 | #endif |
221 | #ifdef FE_UPWARD |
222 | if (!fesetround (FE_UPWARD)) |
223 | { |
224 | result |= test_hex_in_one_mode (d: hex_tests[i].d, fmt: hex_tests[i].fmt, |
225 | expected: hex_tests[i].ru, mode_name: "FE_UPWARD" ); |
226 | fesetround (rounding_direction: save_round_mode); |
227 | } |
228 | #endif |
229 | } |
230 | |
231 | return result; |
232 | } |
233 | |
234 | #define TEST_FUNCTION do_test () |
235 | #include "../test-skeleton.c" |
236 | |