DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_real_decimal.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
15
16#include <daw/daw_arith_traits.h>
17#include <daw/daw_bit_cast.h>
18#include <daw/daw_not_null.h>
19
20#include <cstddef>
21#include <cstdint>
22#include <limits>
23#include <type_traits>
24
25namespace daw::json {
26 inline namespace DAW_JSON_VER {
27 namespace json_details {
28 namespace decimal_details {
29 // Simple Decimal Conversion, specialized for the rare case where a
30 // long JSON significand straddles an IEEE-754 rounding boundary.
31 // Binary64 requires at most 767 significant decimal digits to resolve
32 // rounding, so one additional digit is retained.
33 struct decimal_sequence {
34 static constexpr std::size_t max_digits = 768;
35 static constexpr std::int32_t decimal_point_limit = 2047;
36
37 std::uint8_t digits[max_digits]{ };
38 std::size_t digit_count = 0;
39 std::int32_t decimal_point = 0;
40 bool truncated = false;
41
42 constexpr void push_digit( unsigned digit ) {
43 if( digit_count < max_digits ) {
44 digits[digit_count++] = static_cast<std::uint8_t>( digit );
45 } else {
46 truncated |= digit != 0;
47 }
48 }
49
50 constexpr void trim( ) {
51 while( digit_count != 0 and digits[digit_count - 1] == 0 ) {
52 --digit_count;
53 }
54 if( digit_count == 0 ) {
55 decimal_point = 0;
56 }
57 }
58
59 // Compute decimal * 2^shift. shift is at most 60, so each
60 // digit-times-factor operation and its carry fit in uint64_t.
61 constexpr void shift_left( unsigned shift ) {
62 if( digit_count == 0 or shift == 0 ) {
63 return;
64 }
65
66 DAW_CPP23_STATIC_LOCAL constexpr std::size_t max_growth = 19;
67 std::uint8_t result[max_digits + max_growth]{ };
68 auto write = max_digits + max_growth;
69 auto read = digit_count;
70 std::uint64_t carry = 0;
71 auto const factor = std::uint64_t{ 1 } << shift;
72
73 while( read != 0 ) {
74 --read;
75 auto const value =
76 static_cast<std::uint64_t>( digits[read] ) * factor + carry;
77 result[--write] = static_cast<std::uint8_t>( value % 10U );
78 carry = value / 10U;
79 }
80 while( carry != 0 ) {
81 result[--write] = static_cast<std::uint8_t>( carry % 10U );
82 carry /= 10U;
83 }
84
85 auto const result_count = max_digits + max_growth - write;
86 auto const growth = result_count - digit_count;
87 decimal_point += static_cast<std::int32_t>( growth );
88
89 auto const stored_count =
90 result_count < max_digits ? result_count : max_digits;
91 for( std::size_t n = 0; n < stored_count; ++n ) {
92 digits[n] = result[write + n];
93 }
94 for( auto n = stored_count; n < result_count; ++n ) {
95 truncated |= result[write + n] != 0;
96 }
97 digit_count = stored_count;
98 trim( );
99 }
100
101 // Compute decimal * 2^-shift. shift is at most 60.
102 constexpr void shift_right( unsigned shift ) {
103 if( digit_count == 0 or shift == 0 ) {
104 return;
105 }
106
107 std::size_t read = 0;
108 std::size_t write = 0;
109 std::uint64_t value = 0;
110 while( ( value >> shift ) == 0 ) {
111 if( read < digit_count ) {
112 value =
113 value * 10U + static_cast<std::uint64_t>( digits[read++] );
114 } else if( value == 0 ) {
115 return;
116 } else {
117 do {
118 value *= 10U;
119 ++read;
120 } while( ( value >> shift ) == 0 );
121 break;
122 }
123 }
124
125 decimal_point -= static_cast<std::int32_t>( read - 1 );
126 if( decimal_point < -decimal_point_limit ) {
127 digit_count = 0;
128 decimal_point = 0;
129 truncated = false;
130 return;
131 }
132
133 auto const mask = ( std::uint64_t{ 1 } << shift ) - 1U;
134 while( read < digit_count ) {
135 auto const digit = static_cast<std::uint8_t>( value >> shift );
136 value = 10U * ( value & mask ) +
137 static_cast<std::uint64_t>( digits[read++] );
138 digits[write++] = digit;
139 }
140 while( value != 0 ) {
141 auto const digit = static_cast<std::uint8_t>( value >> shift );
142 value = 10U * ( value & mask );
143 if( write < max_digits ) {
144 digits[write++] = digit;
145 } else {
146 truncated |= digit != 0;
147 }
148 }
149 digit_count = write;
150 trim( );
151 }
152
153 [[nodiscard]] constexpr std::uint64_t round( ) const {
154 if( digit_count == 0 or decimal_point < 0 ) {
155 return 0;
156 }
157 if( decimal_point >= 19 ) {
158 return daw::max_value<std::uint64_t>;
159 }
160
161 auto const point = static_cast<std::size_t>( decimal_point );
162 std::uint64_t result = 0;
163 for( std::size_t n = 0; n < point; ++n ) {
164 result *= 10U;
165 if( n < digit_count ) {
166 result += digits[n];
167 }
168 }
169
170 bool round_up = false;
171 if( point < digit_count ) {
172 round_up = digits[point] >= 5;
173 if( digits[point] == 5 and point + 1 == digit_count ) {
174 round_up =
175 truncated or ( point != 0 and ( digits[point - 1] & 1U ) );
176 }
177 }
178 return result + static_cast<std::uint64_t>( round_up );
179 }
180 };
181
182 [[nodiscard]] constexpr std::int64_t
183 saturating_add( std::int64_t lhs, std::int64_t rhs ) {
184 DAW_CPP23_STATIC_LOCAL constexpr auto max_value =
185 daw::max_value<std::int64_t>;
186 DAW_CPP23_STATIC_LOCAL constexpr auto lowest_value =
187 daw::lowest_value<std::int64_t>;
188
189 if( rhs > 0 and lhs > max_value - rhs ) {
190 return max_value;
191 }
192 if( rhs < 0 and lhs < lowest_value - rhs ) {
193 return lowest_value;
194 }
195 return lhs + rhs;
196 }
197
198 [[nodiscard]] constexpr std::int64_t
199 parse_exponent( char const *first, char const *last ) {
200 if( first == nullptr or first == last ) {
201 return 0;
202 }
203
204 bool negative = false;
205 if( *first == '+' or *first == '-' ) {
206 negative = *first == '-';
207 ++first;
208 }
209
210 DAW_CPP23_STATIC_LOCAL constexpr auto positive_limit =
211 static_cast<std::uint64_t>( daw::max_value<std::int64_t> );
212 DAW_CPP23_STATIC_LOCAL constexpr auto negative_limit =
213 positive_limit + 1U;
214 auto const limit = negative ? negative_limit : positive_limit;
215 std::uint64_t result = 0;
216 bool overflow = false;
217 while( first < last ) {
218 auto const digit = parse_digit( *first++ );
219 if( digit >= 10U ) {
220 break;
221 }
222 if( result > ( limit - digit ) / 10U ) {
223 result = limit;
224 overflow = true;
225 } else if( not overflow ) {
226 result = result * 10U + digit;
227 }
228 }
229
230 if( negative ) {
231 if( result == negative_limit ) {
232 return daw::lowest_value<std::int64_t>;
233 }
234 return -static_cast<std::int64_t>( result );
235 }
236 return static_cast<std::int64_t>( result );
237 }
238
239 [[nodiscard]] constexpr decimal_sequence
240 parse_json_decimal( daw::not_null<char const *> first,
241 daw::not_null<char const *> const last ) {
242 decimal_sequence result{ };
243 if( *first == '-' ) {
244 ++first;
245 }
246
247 std::int64_t decimal_point = 0;
248 bool seen_nonzero = false;
249 while( first < last and parse_digit( *first ) < 10U ) {
250 auto const digit = parse_digit( *first++ );
251 if( seen_nonzero or digit != 0 ) {
252 seen_nonzero = true;
253 result.push_digit( digit );
254 ++decimal_point;
255 }
256 }
257
258 if( first < last and *first == '.' ) {
259 ++first;
260 while( first < last and parse_digit( *first ) < 10U ) {
261 auto const digit = parse_digit( *first++ );
262 if( seen_nonzero or digit != 0 ) {
263 seen_nonzero = true;
264 result.push_digit( digit );
265 } else {
266 --decimal_point;
267 }
268 }
269 }
270
271 char const *exponent_first = nullptr;
272 if( first < last and ( *first == 'e' or *first == 'E' ) ) {
273 exponent_first = ++first;
274 }
275 decimal_point = saturating_add(
276 decimal_point, parse_exponent( exponent_first, last.get( ) ) );
277
278 if( not seen_nonzero ) {
279 result.digit_count = 0;
280 result.decimal_point = 0;
281 result.truncated = false;
282 return result;
283 }
284
285 if( decimal_point > decimal_sequence::decimal_point_limit ) {
286 result.decimal_point = decimal_sequence::decimal_point_limit;
287 } else if( decimal_point < -decimal_sequence::decimal_point_limit ) {
288 result.decimal_point = -decimal_sequence::decimal_point_limit;
289 } else {
290 result.decimal_point = static_cast<std::int32_t>( decimal_point );
291 }
292 result.trim( );
293 return result;
294 }
295
296 [[nodiscard]] constexpr unsigned
297 shift_amount( std::int32_t decimal_places ) {
298 constexpr unsigned powers[19] = {
299 0, 3, 6, 9, 13, 16, 19, 23, 26, 29,
300 33, 36, 39, 43, 46, 49, 53, 56, 59,
301 };
302 auto const places = static_cast<std::uint32_t>(
303 decimal_places < 0 ? -decimal_places : decimal_places );
304 return places < 19U ? powers[places] : 60U;
305 }
306
307 template<typename Real>
308 [[nodiscard]] constexpr Real pack_real( bool negative,
309 std::uint64_t mantissa,
310 std::int32_t power2 ) {
311 using format = eisellemire_details::binary_format<Real>;
312 using uint_type = typename format::uint_type;
313 auto const bits = static_cast<uint_type>(
314 mantissa |
315 ( static_cast<std::uint64_t>( power2 ) << format::mantissa_bits ) |
316 ( static_cast<std::uint64_t>( negative ) << format::sign_bit ) );
317 return DAW_BIT_CAST( Real, bits );
318 }
319 } // namespace decimal_details
320
321 template<typename Real>
322 [[nodiscard]] constexpr Real
323 parse_json_real_exact( bool negative, daw::not_null<char const *> first,
324 daw::not_null<char const *> last ) {
325 using format = eisellemire_details::binary_format<Real>;
326 auto decimal = decimal_details::parse_json_decimal( first, last );
327
328 if( decimal.digit_count == 0 ) {
329 return decimal_details::pack_real<Real>( negative, 0, 0 );
330 }
331
332 // Below rounds to zero, above is infinity. e.g. -324/310 for double
333 // and -45/40 for float
334 DAW_CPP23_STATIC_LOCAL constexpr std::int32_t smallest_decimal_point =
335 std::numeric_limits<Real>::min_exponent10 -
336 std::numeric_limits<Real>::digits10 - 2;
337 DAW_CPP23_STATIC_LOCAL constexpr std::int32_t largest_decimal_point =
338 std::numeric_limits<Real>::max_exponent10 + 2;
339 if( decimal.decimal_point < smallest_decimal_point ) {
340 return decimal_details::pack_real<Real>( negative, 0, 0 );
341 }
342 if( decimal.decimal_point >= largest_decimal_point ) {
343 return decimal_details::pack_real<Real>(
344 negative, 0, format::infinite_power );
345 }
346
347 std::int32_t exponent2 = 0;
348 while( decimal.decimal_point > 0 ) {
349 auto const shift =
350 decimal_details::shift_amount( decimal.decimal_point );
351 decimal.shift_right( shift );
352 exponent2 += static_cast<std::int32_t>( shift );
353 }
354 while( decimal.decimal_point <= 0 ) {
355 unsigned shift = 0;
356 if( decimal.decimal_point == 0 ) {
357 if( decimal.digits[0] >= 5 ) {
358 break;
359 }
360 shift = decimal.digits[0] <= 1 ? 2U : 1U;
361 } else {
362 shift = decimal_details::shift_amount( decimal.decimal_point );
363 }
364 decimal.shift_left( shift );
365 exponent2 -= static_cast<std::int32_t>( shift );
366 }
367
368 // decimal is now in [0.5, 1); IEEE formats use [1, 2).
369 --exponent2;
370 DAW_CPP23_STATIC_LOCAL constexpr auto minimum_exponent =
371 1 - format::exponent_bias;
372 while( exponent2 < minimum_exponent ) {
373 auto shift = minimum_exponent - exponent2;
374 if( shift > 60 ) {
375 shift = 60;
376 }
377 decimal.shift_right( static_cast<unsigned>( shift ) );
378 exponent2 += shift;
379 }
380
381 if( exponent2 - minimum_exponent + 1 >= format::infinite_power ) {
382 return decimal_details::pack_real<Real>(
383 negative, 0, format::infinite_power );
384 }
385
386 decimal.shift_left(
387 static_cast<unsigned>( format::mantissa_bits + 1 ) );
388 auto mantissa = decimal.round( );
389 if( mantissa >=
390 ( std::uint64_t{ 1 } << ( format::mantissa_bits + 1 ) ) ) {
391 decimal.shift_right( 1 );
392 ++exponent2;
393 mantissa = decimal.round( );
394 if( exponent2 - minimum_exponent + 1 >= format::infinite_power ) {
395 return decimal_details::pack_real<Real>(
396 negative, 0, format::infinite_power );
397 }
398 }
399
400 auto power2 = exponent2 - minimum_exponent + 1;
401 if( mantissa < ( std::uint64_t{ 1 } << format::mantissa_bits ) ) {
402 --power2;
403 }
404 mantissa &= ( std::uint64_t{ 1 } << format::mantissa_bits ) - 1U;
405 return decimal_details::pack_real<Real>( negative, mantissa, power2 );
406 }
407 } // namespace json_details
408 } // namespace DAW_JSON_VER
409} // namespace daw::json
string_writer grows its string with resize_and_overwrite, leaving the new bytes uninitialized,...
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20