]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // |
2 | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) | |
3 | // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) | |
4 | // | |
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
7 | // | |
8 | // Official repository: https://github.com/boostorg/json | |
9 | // | |
10 | ||
11 | #include <boost/json/stream_parser.hpp> | |
12 | #include <boost/json/parse.hpp> | |
13 | #include <boost/json/serialize.hpp> | |
14 | ||
15 | #include <iostream> | |
16 | #include <random> | |
17 | #include <cinttypes> | |
18 | ||
19 | #include "parse-vectors.hpp" | |
20 | #include "test.hpp" | |
21 | #include "test_suite.hpp" | |
22 | ||
23 | BOOST_JSON_NS_BEGIN | |
24 | ||
25 | template<std::size_t N, class... Args> | |
26 | void | |
27 | sprintf(char (&buf)[N], | |
28 | char const* format, Args&&... args) | |
29 | { | |
30 | #ifdef _MSC_VER | |
31 | sprintf_s(buf, format, | |
32 | std::forward<Args>(args)...); | |
33 | #else | |
34 | std::sprintf(buf, format, | |
35 | std::forward<Args>(args)...); | |
36 | #endif | |
37 | } | |
38 | ||
39 | class double_test | |
40 | { | |
41 | public: | |
42 | struct f_boost | |
43 | { | |
44 | static | |
45 | string_view | |
46 | name() noexcept | |
47 | { | |
48 | return "boost"; | |
49 | } | |
50 | ||
51 | double | |
52 | operator()(string_view s) const | |
53 | { | |
54 | BOOST_TEST_CHECKPOINT(); | |
55 | error_code ec; | |
56 | stream_parser p; | |
57 | p.write(s.data(), s.size(), ec); | |
58 | if(BOOST_TEST(! ec)) | |
59 | p.finish(ec); | |
60 | if(! BOOST_TEST(! ec)) | |
61 | return 0; | |
62 | auto const jv = p.release(); | |
63 | double const d = jv.as_double(); | |
64 | grind_double(s, d); | |
65 | return d; | |
66 | } | |
67 | }; | |
68 | ||
69 | bool | |
70 | within_1ulp(double x, double y) | |
71 | { | |
72 | std::uint64_t bx, by; | |
73 | std::memcpy(&bx, &x, sizeof(x)); | |
74 | std::memcpy(&by, &y, sizeof(y)); | |
75 | ||
76 | auto diff = bx - by; | |
77 | switch (diff) | |
78 | { | |
79 | case 0: | |
80 | case 1: | |
81 | case 0xffffffffffffffff: | |
82 | return true; | |
83 | default: | |
84 | break; | |
85 | } | |
86 | return false; | |
87 | } | |
88 | ||
89 | static | |
90 | value | |
91 | from_string_test( | |
92 | string_view s, | |
93 | storage_ptr sp = {}, | |
94 | const parse_options& po = parse_options()) | |
95 | { | |
96 | stream_parser p(storage_ptr(), po); | |
97 | error_code ec; | |
98 | p.reset(std::move(sp)); | |
99 | p.write(s.data(), s.size(), ec); | |
100 | if(BOOST_TEST(! ec)) | |
101 | p.finish(ec); | |
102 | BOOST_TEST(! ec); | |
103 | return p.release(); | |
104 | } | |
105 | ||
106 | void | |
107 | static | |
108 | check_round_trip(value const& jv1, | |
109 | const parse_options& po = parse_options()) | |
110 | { | |
111 | auto const s2 = | |
112 | //to_string_test(jv1); // use this if serializer is broken | |
113 | serialize(jv1); | |
114 | auto jv2 = | |
115 | from_string_test(s2, {}, po); | |
116 | BOOST_TEST(equal(jv1, jv2)); | |
117 | } | |
118 | ||
119 | template<class F> | |
120 | void | |
121 | static | |
122 | grind_one( | |
123 | string_view s, | |
124 | storage_ptr sp, | |
125 | F const& f, | |
126 | const parse_options& po = parse_options()) | |
127 | { | |
128 | auto const jv = | |
129 | from_string_test(s, sp, po); | |
130 | f(jv, po); | |
131 | } | |
132 | ||
133 | static | |
134 | void | |
135 | grind_one(string_view s) | |
136 | { | |
137 | auto const jv = | |
138 | from_string_test(s); | |
139 | check_round_trip(jv); | |
140 | } | |
141 | ||
142 | template<class F> | |
143 | static | |
144 | void | |
145 | grind(string_view s, F const& f, | |
146 | const parse_options& po = parse_options()) | |
147 | { | |
148 | try | |
149 | { | |
150 | grind_one(s, {}, f, po); | |
151 | ||
152 | fail_loop([&](storage_ptr const& sp) | |
153 | { | |
154 | grind_one(s, sp, f, po); | |
155 | }); | |
156 | ||
157 | if(s.size() > 1) | |
158 | { | |
159 | // Destroy the stream_parser at every | |
160 | // split point to check leaks. | |
161 | for(std::size_t i = 1; | |
162 | i < s.size(); ++i) | |
163 | { | |
164 | fail_resource mr; | |
165 | mr.fail_max = 0; | |
166 | stream_parser p(storage_ptr(), po); | |
167 | error_code ec; | |
168 | p.reset(&mr); | |
169 | p.write(s.data(), i, ec); | |
170 | if(BOOST_TEST(! ec)) | |
171 | p.write( | |
172 | s.data() + i, | |
173 | s.size() - i, ec); | |
174 | if(BOOST_TEST(! ec)) | |
175 | p.finish(ec); | |
176 | if(BOOST_TEST(! ec)) | |
177 | f(p.release(), po); | |
178 | } | |
179 | } | |
180 | } | |
181 | catch(std::exception const&) | |
182 | { | |
183 | BOOST_TEST_FAIL(); | |
184 | } | |
185 | } | |
186 | ||
187 | static | |
188 | void | |
189 | grind(string_view s, | |
190 | const parse_options& po = parse_options()) | |
191 | { | |
192 | grind(s, | |
193 | [](value const& jv, const parse_options& po) | |
194 | { | |
195 | check_round_trip(jv, po); | |
196 | }, po); | |
197 | } | |
198 | ||
199 | static | |
200 | void | |
201 | grind_double(string_view s, double v) | |
202 | { | |
203 | grind(s, | |
204 | [v](value const& jv, const parse_options&) | |
205 | { | |
206 | if(! BOOST_TEST(jv.is_double())) | |
207 | return; | |
208 | BOOST_TEST(jv.get_double() == v); | |
209 | }); | |
210 | } | |
211 | ||
212 | // Verify that f converts to the | |
213 | // same double produced by `strtod`. | |
214 | // Requires `s` is not represented by an integral type. | |
215 | template<class F> | |
216 | void | |
217 | fc(std::string const& s, F const& f) | |
218 | { | |
219 | char* str_end; | |
220 | double const need = | |
221 | std::strtod(s.c_str(), &str_end); | |
222 | // BOOST_TEST(str_end == &s.back() + 1); | |
223 | double const got = f(s); | |
224 | auto same = got == need; | |
225 | auto close = same ? | |
226 | true : within_1ulp(got, need); | |
227 | ||
228 | if( !BOOST_TEST(close) ) | |
229 | { | |
230 | std::cerr << "Failure on '" << s << "': " << got << " != " << need << "\n"; | |
231 | } | |
232 | } | |
233 | ||
234 | void | |
235 | fc(std::string const& s) | |
236 | { | |
237 | fc(s, f_boost{}); | |
238 | fc(s + std::string( 64, ' ' ), f_boost{}); | |
239 | } | |
240 | ||
241 | void | |
242 | testDouble() | |
243 | { | |
244 | grind_double("-1.010", -1.01); | |
245 | grind_double("-0.010", -0.01); | |
246 | grind_double("-0.0", -0.0); | |
247 | grind_double("-0e0", -0.0); | |
248 | grind_double( "18446744073709551616", 1.8446744073709552e+19); | |
249 | grind_double("-18446744073709551616", -1.8446744073709552e+19); | |
250 | grind_double( "18446744073709551616.0", 1.8446744073709552e+19); | |
251 | grind_double( "18446744073709551616.00009", 1.8446744073709552e+19); | |
252 | grind_double( "1844674407370955161600000", 1.8446744073709552e+24); | |
253 | grind_double("-1844674407370955161600000", -1.8446744073709552e+24); | |
254 | grind_double( "1844674407370955161600000.0", 1.8446744073709552e+24); | |
255 | grind_double( "1844674407370955161600000.00009", 1.8446744073709552e+24); | |
256 | grind_double( "19700720435664.186294290058937593e13", 1.9700720435664185e+26); | |
257 | ||
258 | grind_double( "1.0", 1.0); | |
259 | grind_double( "1.1", 1.1); | |
260 | grind_double( "1.11", 1.11); | |
261 | grind_double( "1.11111", 1.11111); | |
262 | grind_double( "11.1111", 11.1111); | |
263 | grind_double( "111.111", 111.111); | |
264 | ||
265 | fc("-0.9999999999999999999999"); | |
266 | fc("-0.9999999999999999"); | |
267 | fc("-0.9007199254740991"); | |
268 | fc("-0.999999999999999"); | |
269 | fc("-0.99999999999999"); | |
270 | fc("-0.9999999999999"); | |
271 | fc("-0.999999999999"); | |
272 | fc("-0.99999999999"); | |
273 | fc("-0.9999999999"); | |
274 | fc("-0.999999999"); | |
275 | fc("-0.99999999"); | |
276 | fc("-0.9999999"); | |
277 | fc("-0.999999"); | |
278 | fc("-0.99999"); | |
279 | fc("-0.9999"); | |
280 | fc("-0.8125"); | |
281 | fc("-0.999"); | |
282 | fc("-0.99"); | |
283 | fc("-1.0"); | |
284 | fc("-0.9"); | |
285 | fc("-0.0"); | |
286 | fc("0.0"); | |
287 | fc("0.9"); | |
288 | fc("0.99"); | |
289 | fc("0.999"); | |
290 | fc("0.8125"); | |
291 | fc("0.9999"); | |
292 | fc("0.99999"); | |
293 | fc("0.999999"); | |
294 | fc("0.9999999"); | |
295 | fc("0.99999999"); | |
296 | fc("0.999999999"); | |
297 | fc("0.9999999999"); | |
298 | fc("0.99999999999"); | |
299 | fc("0.999999999999"); | |
300 | fc("0.9999999999999"); | |
301 | fc("0.99999999999999"); | |
302 | fc("0.999999999999999"); | |
303 | fc("0.9007199254740991"); | |
304 | fc("0.9999999999999999"); | |
305 | fc("0.9999999999999999999999"); | |
306 | fc("0.999999999999999999999999999"); | |
307 | ||
308 | fc("-1e308"); | |
309 | fc("-1e-308"); | |
310 | fc("-9999e300"); | |
311 | fc("-999e100"); | |
312 | fc("-99e10"); | |
313 | fc("-9e1"); | |
314 | fc("9e1"); | |
315 | fc("99e10"); | |
316 | fc("999e100"); | |
317 | fc("9999e300"); | |
318 | fc("999999999999999999.0"); | |
319 | fc("999999999999999999999.0"); | |
320 | fc("999999999999999999999e5"); | |
321 | fc("999999999999999999999.0e5"); | |
322 | ||
323 | fc("0.00000000000000001"); | |
324 | ||
325 | fc("-1e-1"); | |
326 | fc("-1e0"); | |
327 | fc("-1e1"); | |
328 | fc("0e0"); | |
329 | fc("1e0"); | |
330 | fc("1e10"); | |
331 | ||
332 | fc("0." | |
333 | "00000000000000000000000000000000000000000000000000" // 50 zeroes | |
334 | "1e50"); | |
335 | fc("-0." | |
336 | "00000000000000000000000000000000000000000000000000" // 50 zeroes | |
337 | "1e50"); | |
338 | ||
339 | fc("0." | |
340 | "00000000000000000000000000000000000000000000000000" | |
341 | "00000000000000000000000000000000000000000000000000" | |
342 | "00000000000000000000000000000000000000000000000000" | |
343 | "00000000000000000000000000000000000000000000000000" | |
344 | "00000000000000000000000000000000000000000000000000" | |
345 | "00000000000000000000000000000000000000000000000000" | |
346 | "00000000000000000000000000000000000000000000000000" | |
347 | "00000000000000000000000000000000000000000000000000" | |
348 | "00000000000000000000000000000000000000000000000000" | |
349 | "00000000000000000000000000000000000000000000000000" // 500 zeroes | |
350 | "1e600"); | |
351 | fc("-0." | |
352 | "00000000000000000000000000000000000000000000000000" | |
353 | "00000000000000000000000000000000000000000000000000" | |
354 | "00000000000000000000000000000000000000000000000000" | |
355 | "00000000000000000000000000000000000000000000000000" | |
356 | "00000000000000000000000000000000000000000000000000" | |
357 | "00000000000000000000000000000000000000000000000000" | |
358 | "00000000000000000000000000000000000000000000000000" | |
359 | "00000000000000000000000000000000000000000000000000" | |
360 | "00000000000000000000000000000000000000000000000000" | |
361 | "00000000000000000000000000000000000000000000000000" // 500 zeroes | |
362 | "1e600"); | |
363 | ||
364 | fc("0e" | |
365 | "00000000000000000000000000000000000000000000000000" | |
366 | "00000000000000000000000000000000000000000000000000" | |
367 | "00000000000000000000000000000000000000000000000000" | |
368 | "00000000000000000000000000000000000000000000000000" | |
369 | "00000000000000000000000000000000000000000000000000" | |
370 | "00000000000000000000000000000000000000000000000000" | |
371 | "00000000000000000000000000000000000000000000000000" | |
372 | "00000000000000000000000000000000000000000000000000" | |
373 | "00000000000000000000000000000000000000000000000000" | |
374 | "00000000000000000000000000000000000000000000000000" // 500 zeroes | |
375 | ); | |
376 | } | |
377 | ||
378 | void checkAccuracy(const char* nm, int max_ulp) | |
379 | { | |
380 | double x = std::strtod( nm, 0 ); | |
381 | double y = boost::json::parse( nm ).as_double(); | |
382 | std::uint64_t bx, by; | |
383 | std::memcpy( &bx, &x, sizeof(x) ); | |
384 | std::memcpy( &by, &y, sizeof(y) ); | |
385 | std::int64_t diff = bx - by; | |
386 | if (!BOOST_TEST(std::abs( diff ) <= max_ulp)) | |
387 | std::fprintf(stderr, | |
388 | "%s: difference %" PRId64 " ulp\n" | |
389 | " strtod: %.13a %.16g\n" | |
390 | " boost.json: %.13a %.16g\n\n", | |
391 | nm, diff, x, x, y, y ); | |
392 | } | |
393 | ||
394 | void | |
395 | testWithinULP() | |
396 | { | |
397 | std::mt19937_64 rng; | |
398 | ||
399 | checkAccuracy("10199214983525025199.13135016100190689227e-308", 2); | |
400 | ||
401 | for( int i = 0; i < 1000000; ++i ) | |
402 | { | |
403 | unsigned long long x1 = rng(); | |
404 | unsigned long long x2 = rng(); | |
405 | int x3 = std::uniform_int_distribution<>( -308, +308 )( rng ); | |
406 | ||
407 | char buffer[ 128 ]; | |
408 | sprintf( buffer, "%llu.%llue%d", x1, x2, x3 ); | |
409 | ||
410 | checkAccuracy( buffer, 2 ); | |
411 | } | |
412 | ||
413 | for( int i = -326; i <= +309; ++i ) | |
414 | { | |
415 | char buffer[ 128 ]; | |
416 | sprintf( buffer, "1e%d", i ); | |
417 | ||
418 | checkAccuracy( buffer, 1 ); // 1e-307 is 1ulp, rest 0 | |
419 | } | |
420 | }; | |
421 | ||
422 | void | |
423 | run() | |
424 | { | |
425 | testDouble(); | |
426 | testWithinULP(); | |
427 | } | |
428 | }; | |
429 | ||
430 | TEST_SUITE(double_test, "boost.json.double"); | |
431 | ||
432 | BOOST_JSON_NS_END |