]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/json/test/double.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / json / test / double.cpp
CommitLineData
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
23BOOST_JSON_NS_BEGIN
24
25template<std::size_t N, class... Args>
26void
27sprintf(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
39class double_test
40{
41public:
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
430TEST_SUITE(double_test, "boost.json.double");
431
432BOOST_JSON_NS_END