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